diff options
author | dnovillo <dnovillo@138bc75d-0d04-0410-961f-82ee72b054a4> | 2006-01-18 19:21:25 +0000 |
---|---|---|
committer | dnovillo <dnovillo@138bc75d-0d04-0410-961f-82ee72b054a4> | 2006-01-18 19:21:25 +0000 |
commit | 1e8e992020adfba209ef30b3c369e2ca6282d837 (patch) | |
tree | b7647b4100a528e9911385ca6d0d57a23899ae2c /gcc | |
parent | 14efb9b7b52e816aa93ea4a451b5c10eb7634a8b (diff) | |
download | gcc-1e8e992020adfba209ef30b3c369e2ca6282d837.tar.gz |
2006-01-18 Richard Henderson <rth@redhat.com>
Jakub Jelinek <jakub@redhat.com>
Diego Novillo <dnovillo@redhat.com>
* libgomp: New directory.
* Makefile.def: Add target_module libgomp.
* Makefile.in: Regenerate.
* configure.in (target_libraries): Add target-libgomp.
* configure: Regenerate.
contrib/
2006-01-18 Richard Henderson <rth@redhat.com>
Diego Novillo <dnovillo@redhat.com>
* gcc_update (files_and_dependencies): Add libgomp files.
gcc/
2006-01-18 Richard Henderson <rth@redhat.com>
Aldy Hernandez <aldyh@redhat.com>
Jakub Jelinek <jakub@redhat.com>
Diego Novillo <dnovillo@redhat.com>
* omp-low.c: New file.
* c-omp.c: New file.
2006-01-18 Richard Henderson <rth@redhat.com>
Jakub Jelinek <jakub@redhat.com>
Diego Novillo <dnovillo@redhat.com>
* doc/invoke.texi: Document -fopenmp.
* tree-dump.h (debug_function): Declare.
* hooks.c (hook_bool_tree_bool_false): New function.
(hook_tree_tree_null): Remove.
(hook_tree_tree_tree_null): New.
* hooks.h: Update to match.
* tree-pretty-print.c (debug_tree_chain): New.
(print_generic_expr): Handle TDF_CHAIN.
(dump_generic_node): Handle BLOCK.
Do not abort with incomplete SWITCH_EXPRs.
Do not dump body of an OpenMP directive if TDF_SLIM is given.
<case OMP_PARALLEL, OMP_FOR, OMP_SECTIONS>: Don't
print space after directive name.
<OMP_FOR>: Handle printing OMP_FOR_PRE_BODY.
Handle OMP_MASTER and OMP_ORDERED.
Handle printing of OMP_BODY just in one place, goto
dump_omp_body in the rest of OMP_* nodes that have
OMP_BODY.
Don't handle clause nodes here. Update omp statements to
use dump_omp_clauses.
Handle OMP_SINGLE, OMP_SECTIONS, OMP_SECTION,
OMP_CLAUSE_ORDERED, OMP_CLAUSE_SCHEDULE, OMP_ATOMIC,
OMP_CRITICAL, OMP_CLAUSE_NOWAIT, GOMP_CLAUSE_IF,
GOMP_CLAUSE_NUM_THREADS, GOMP_FOR, GOMP_CLAUSE_SHARED,
GOMP_CLAUSE_FIRSTPRIVATE, GOMP_CLAUSE_LASTPRIVATE,
GOMP_CLAUSE_COPYIN and GOMP_CLAUSE_COPYPRIVATE.
Adjust output for GOMP_PARALLEL.
(dump_omp_clauses): New.
(print_declaration): Dump DECL_VALUE_EXPR.
(op_symbol_1): Split out of op_symbol.
(dumping_stmts): Remove. Update all users.
* cgraph.c (cgraph_analyze_queue): New.
(cgraph_add_new_function): New.
* cgraph.h (cgraph_analyze_queue): Declare.
(cgraph_add_new_function): Declare.
(cgraph_lower_function): Remove.
* tree.c (walk_tree): Walk OMP_CLAUSE_CHAIN of OMP_CLAUSE_*
nodes. Use switch for all nodes, handle most of IS_EXPR_CODE_CLASS
and TYPE_P nodes in its default clause.
(empty_body_p): New.
(tree_range_check_failed): New.
(build5_stat): New.
* tree.h (OMP_CLAUSE_REDUCTION_INIT,
OMP_CLAUSE_REDUCTION_MERGE,
OMP_CLAUSE_REDUCTION_PLACEHOLDER,
OMP_CLAUSE_PRIVATE_DEBUG,
OMP_CLAUSE_LASTPRIVATE_FIRSTPRIVATE, OMP_FOR_PRE_BODY,
OMP_MASTER_BODY, OMP_ORDERED_BODY OMP_BODY,
OMP_CLAUSES, OMP_CLAUSE_DECL, OMP_CLAUSE_DEFAULT_KIND,
OMP_CLAUSE_CHAIN, OMP_CLAUSE_OUTER_DECL,
OMP_CLAUSE_INNER_DECL, OMP_CLAUSE_NUM_THREADS_EXPR,
OMP_CLAUSE_IF_EXPR, OMP_CLAUSE_SCHEDULE_CHUNK_EXPR,
OMP_CLAUSE_SCHEDULE_CHUNK_SIZE. OMP_PARALLEL_VAR_INIT,
OMP_PARALLEL_VAR_REDUC, OMP_FOR_VAR_INIT,
OMP_FOR_VAR_LAST, OMP_FOR_VAR_REDUC,
OMP_SECTIONS_VAR_INIT, OMP_SECTIONS_VAR_LAST,
OMP_SECTIONS_VAR_REDUC, OMP_CLAUSE_REDUCTION_CODE
OMP_SINGLE_CLAUSES, OMP_SINGLE_BODY,
OMP_CLAUSE_SCHEDULE_CHUNK_SIZE, OMP_SECTION_BODY,
OMP_CRITICAL_NAME, OMP_CRITICAL_BODY): New.
(TREE_RANGE_CHECK): New.
(empty_body_p): Declare.
(enum omp_clause_default_kind): New.
(build_string_literal): Declare.
(enum omp_clause_schedule_kind, OMP_CLAUSE_SCHEDULE_KIND): New.
(build5_stat, build5): Declare.
* tree-pass.h (TDF_CHAIN): Define.
* tree-pass.h (PROP_gimple_lomp): Define.
(pass_lower_omp): Declare.
* diagnostic.h (debug_tree_chain): Declare.
* builtins.c (get_builtin_sync_mode): Use 0 as last argument to
mode_for_size.
(expand_builtin): Handle sync BUILT_IN_*_16 builtins.
* builtins.c (build_string_literal): Make extern.
* gcc.c (include_spec_function): New.
(static_spec_functions): Add it.
(main): Move load of libgomp.spec ...
(LINK_COMMAND_SPEC): ... here.
(link_gomp_spec): New.
(static_specs): Include it.
(LINK_COMMAND_SPEC): Add link_gomp.
(GOMP_SELF_SPECS): New.
(driver_self_specs): Include it.
(switch_matches): Don't mark inline.
(main): Load libgomp.spec.
* tree-gimple.c (is_gimple_stmt): True for OMP_MASTER,
OMP_ORDERED, OMP_CRITICAL, OMP_SECTIONS, OMP_SECTION,
and OMP_SINGLE, OMP_FOR and OMP_PARALLEL.
* tree-gimple.h (enum omp_parallel): Declare.
(determine_parallel_type): Declare.
(omp_firstprivatize_variable): Declare.
(omp_reduction_init): Declare.
(diagnose_omp_structured_block_errors): Declare.
(struct walk_stmt_info): Add want_return_expr.
(struct walk_stmt_info): Add want_bind_expr, want_locations.
(find_omp_clause): Declare.
(insert_field_into_struct): Declare.
(struct walk_stmt_info): Move from tree-nested.c
(walk_stmts): Declare.
* c-cppbuiltin.c (c_cpp_builtins): If -fopenmp, #define _OPENMP
to 200505.
* cgraphunit.c (cgraph_lower_function): Make static.
(cgraph_finalize_pending_functions): New.
(cgraph_finalize_function): Call it.
(cgraph_finalize_compilation_unit): Likewise.
* builtin-types.def (BT_I16, BT_FN_I16_VPTR_I16,
BT_FN_BOOL_VPTR_I16_I16, BT_FN_I16_VPTR_I16_I16): Add.
(BT_FN_UINT_UINT): New.
(DEF_FUNCTION_TYPE_6, DEF_FUNCTION_TYPE_7,
DEF_FUNCTION_TYPE_VAR_4): Document.
(BT_PTR_LONG, BT_PTR_PTR, BT_FN_BOOL, BT_FN_INT,
BT_FN_VOID_PTRPTR, BT_PTR_FN_VOID_PTR,
BT_FN_BOOL_LONGPTR_LONGPTR, BT_FN_VOID_OMPFN_PTR_UINT,
BT_FN_VOID_OMPFN_PTR_UINT_UINT,
BT_FN_BOOL_LONG_LONG_LONG_LONGPTR_LONGPTR,
BT_FN_BOOL_LONG_LONG_LONG_LONG_LONGPTR_LONGPTR,
BT_FN_VOID_OMPFN_PTR_UINT_LONG_LONG_LONG,
BT_FN_VOID_OMPFN_PTR_UINT_LONG_LONG_LONG_LONG): New.
* builtins.def: Update DEF_BUILTIN comment to include COND argument.
Move all DEF_SYNC_BUILTIN () and DEF_GOMP_BUILTIN () builtins
into separate files.
(DEF_GOMP_BUILTIN): New.
(BUILT_IN_OMP_GET_THREAD_NUM, BUILT_IN_GOMP_BARRIER,
BUILT_IN_GOMP_CRITICAL_START, BUILT_IN_GOMP_CRITICAL_END,
BUILT_IN_GOMP_CRITICAL_NAME_START, BUILT_IN_GOMP_CRITICAL_NAME_END,
BUILT_IN_GOMP_LOOP_STATIC_START, BUILT_IN_GOMP_LOOP_DYNAMIC_START,
BUILT_IN_GOMP_LOOP_GUIDED_START, BUILT_IN_GOMP_LOOP_RUNTIME_START,
BUILT_IN_GOMP_LOOP_ORDERED_STATIC_START,
BUILT_IN_GOMP_LOOP_ORDERED_DYNAMIC_START,
BUILT_IN_GOMP_LOOP_ORDERED_GUIDED_START,
BUILT_IN_GOMP_LOOP_ORDERED_RUNTIME_START,
BUILT_IN_GOMP_LOOP_STATIC_NEXT, BUILT_IN_GOMP_LOOP_DYNAMIC_NEXT,
BUILT_IN_GOMP_LOOP_GUIDED_NEXT, BUILT_IN_GOMP_LOOP_RUNTIME_NEXT,
BUILT_IN_GOMP_LOOP_ORDERED_STATIC_NEXT,
BUILT_IN_GOMP_LOOP_ORDERED_DYNAMIC_NEXT,
BUILT_IN_GOMP_LOOP_ORDERED_GUIDED_NEXT,
BUILT_IN_GOMP_LOOP_ORDERED_RUNTIME_NEXT,
BUILT_IN_GOMP_PARALLEL_LOOP_STATIC_START,
BUILT_IN_GOMP_PARALLEL_LOOP_DYNAMIC_START,
BUILT_IN_GOMP_PARALLEL_LOOP_GUIDED_START,
BUILT_IN_GOMP_PARALLEL_LOOP_RUNTIME_START,
BUILT_IN_GOMP_LOOP_END, BUILT_IN_GOMP_LOOP_END_NOWAIT,
BUILT_IN_GOMP_ORDERED_START, BUILT_IN_GOMP_ORDERED_END,
BUILT_IN_GOMP_PARALLEL_START, BUILT_IN_GOMP_PARALLEL_END,
BUILT_IN_GOMP_SECTIONS_START, BUILT_IN_GOMP_SECTIONS_NEXT,
BUILT_IN_GOMP_PARALLEL_SECTIONS_START, BUILT_IN_GOMP_SECTIONS_END,
BUILT_IN_GOMP_SECTIONS_END_NOWAIT, BUILT_IN_GOMP_SINGLE_START,
BUILT_IN_GOMP_SINGLE_COPY_START, BUILT_IN_GOMP_SINGLE_COPY_END): New.
* sync-builtins.def: New file, moved from builtins.def.
* omp-builtins.def: New file, moved from builtins.def.
* c-objc-common.h (LANG_HOOKS_OMP_PREDETERMINED_SHARING): Redefine.
* gimple-low.c (lower_function_body): Clear data.
(lower_stmt): Do not handle COMPOUND_EXPR.
Remove call to print_node_brief.
* c-tree.h (c_finish_omp_clauses): New prototype.
(C_DECL_THREADPRIVATE_P): Define.
(lookup_name_no_remap, c_omp_remap_private): Remove
(c_begin_omp_parallel, c_finish_omp_parallel): Update.
(check_for_loop_decls): Update decl.
(lookup_name_no_remap, c_omp_remap_private): Declare.
(build_indirect_ref, build_modify_expr, pushdecl,
pushdecl_top_level): Move to c-common.h.
* dwarf2out.c (loc_descriptor_from_tree_1): Don't set unsignedp
before the switch, but just in the 2 places that need it.
* c-decl.c (diagnose_mismatched_decls): Do not check for
mismatched thread-local attributes when OLDDECL is marked
threadprivate and NEWDECL has no thread-local attributes.
(merge_decls): Merge C_DECL_THREADPRIVATE_P.
(c_gimple_diagnostics_recursively): Rename from
c_warn_unused_result_recursively. Invoke
diagnose_omp_structured_block_errors.
(check_for_loop_decls): Return a singular decl found.
* langhooks.c (lhd_omp_predetermined_sharing): Return
OMP_CLAUSE_DEFAULT_SHARED for DECL_ARTIFICIAL decls.
(lhd_omp_firstprivatize_type_sizes): New.
(lhd_omp_assignment): New.
(lhd_omp_predetermined_sharing): New.
* langhooks.h (struct gimplify_omp_ctx): Forward declare.
(struct lang_hooks_for_types): Add
omp_firstprivatize_type_sizes, omp_privatize_by_reference,
omp_predetermined_sharing, omp_disregard_value_expr,
omp_private_debug_clause, omp_clause_default_ctor,
omp_clause_copy_ctor, omp_clause_assign_op, omp_clause_dtor.
(c_finish_omp_clauses): New.
(c_finish_bc_stmt): Diagnose break within omp for.
(c_begin_omp_parallel, c_finish_omp_parallel): New.
(build_unary_op): Return error_mark after reporting
a readonly_error.
(build_modify_expr): Likewise.
* gimplify.c: Include optabs.h and pointer-set.h.
(enum gimplify_omp_var_data): Declare.
(struct gimplify_omp_ctx): Declare.
(struct gimplify_ctx): Add fields prev_context, combined_pre_p
and combined_ctxp.
(gimplify_ctxp, gimplify_omp_ctxp): New local variables.
(push_gimplify_context, pop_gimplify_context): Allow nesting.
(splay_tree_compare_decl_uid): New.
(new_omp_context): New.
(delete_omp_context): New.
(gimple_add_tmp_var): Call omp_add_variable.
(gimplify_bind_expr): Likewise.
(gimplify_var_or_parm_decl): If omp_notice_variable returned
true, disregard DECL_VALUE_EXPR on the decl if any.
(gimplify_expr_in_ctx): New.
(omp_firstprivatize_variable, omp_firstprivatize_type_sizes
omp_add_variable, omp_notice_variable, omp_is_private
gimplify_scan_omp_clauses, gimplify_adjust_omp_clauses_1
gimplify_adjust_omp_clauses, gimplify_omp_parallel
gimplify_omp_for, gimplify_omp_workshare, goa_lhs_expr_p
gimplify_omp_atomic_fetch_op, goa_stabilize_expr
gimplify_omp_atomic_pipeline, gimplify_omp_atomic_mutex
gimplify_omp_atomic): New.
(gimplify_expr): Handle OMP_PARALLEL, OMP_FOR, OMP_SECTIONS,
OMP_SINGLE, OMP_SECTION, OMP_MASTER, OMP_ORDERED,
OMP_CRITICAL and OMP_ATOMIC.
(gimplify_body): Verify gimplify_ctxp is empty after gimplification.
* c-pragma.h (enum pragma_kind): Add
PRAGMA_OMP_ATOMIC, PRAGMA_OMP_BARRIER,
PRAGMA_OMP_CRITICAL, PRAGMA_OMP_FLUSH, PRAGMA_OMP_FOR,
PRAGMA_OMP_MASTER, PRAGMA_OMP_ORDERED,
PRAGMA_OMP_PARALLEL, PRAGMA_OMP_PARALLEL_FOR,
PRAGMA_OMP_PARALLEL_SECTIONS, PRAGMA_OMP_SECTION,
PRAGMA_OMP_SECTIONS, PRAGMA_OMP_SINGLE,
PRAGMA_OMP_THREADPRIVATE.
* tree.def (OMP_PARALLEL, OMP_FOR, OMP_SECTIONS,
OMP_SINGLE, OMP_SECTION, OMP_MASTER, OMP_ORDERED,
OMP_CRITICAL, OMP_ATOMIC, OMP_CLAUSE_PRIVATE,
OMP_CLAUSE_SHARED, OMP_CLAUSE_FIRSTPRIVATE,
OMP_CLAUSE_LASTPRIVATE, OMP_CLAUSE_REDUCTION,
OMP_CLAUSE_COPYIN, OMP_CLAUSE_COPYPRIVATE,
OMP_CLAUSE_IF, OMP_CLAUSE_NUM_THREADS,
OMP_CLAUSE_SCHEDULE, OMP_CLAUSE_NOWAIT,
OMP_CLAUSE_ORDERED, OMP_CLAUSE_DEFAULT): Define.
* print-tree.c (print_node): Dump DECL_VALUE_EXPR.
* tree-ssa-dce.c (find_control_dependence): Do not assume that
ENTRY_BLOCK_PTR->next_bb == single_succ (ENTRY_BLOCK_PTR).
* tree-nested.c (convert_call_expr): Call walk_body on OMP_BODY for
OpenMP directives.
(struct nesting_info): Add field_map,
suppress_expansion, debug_var_chain.
(create_nesting_tree): Initialize them.
(lookup_field_for_decl): Use field_map.
(get_nonlocal_debug_decl, get_local_debug_decl): New.
(convert_local_omp_clauses): New.
(finalize_nesting_tree_1): Add debug_var_chain to toplevel block.
(walk_body): Split out of walk_function.
(convert_nonlocal_omp_clauses, convert_local_omp_clauses): New.
(convert_nonlocal_reference): Handle omp statements.
(convert_local_reference): Likewise.
(unnest_nesting_tree_1): Split out of finalize_nesting_tree_1.
(unnest_nesting_tree): New.
(lower_nested_functions): Call it.
(insert_field_into_struct): Make extern.
(struct walk_stmt_info): Move to tree-gimple.h.
(walk_stmts): Make extern.
* omp-builtins.def: New file.
* tree-iterator.c (expr_only): Clarify comment.
* c-common.h (pushdecl_top_level, pushdecl,
build_modify_expr, build_indirect_ref,
c_finish_omp_master, c_finish_omp_critical,
c_finish_omp_ordered, c_finish_omp_barrier,
c_finish_omp_atomic, c_finish_omp_flush,
c_finish_omp_for, c_split_parallel_clauses,
omp_clause_default_kind, c_omp_sharing_predetermined,
c_omp_remap_decl): Declare.
* Makefile.in (BUILTINS_DEF): Add omp-builtins.def.
(OBJS-common): Add omp-low.o.
(c-omp.o, omp-low.o): Add.
(gimplify.o): Add dependency on $(OPTABS_H).
(GTFILES): Add omp-low.c.
(gt-stringpool.h): Add.
* tree-cfg.c (set_bb_for_stmt): Do not update the
block-to-labels map if we are currently expanding to RTL.
(tree_node_can_be_shared): Remove unnecessary CONSTANT_CLASS_P
checks.
Handle IDENTIFIER_NODE.
(tree_verify_flow_info): Do not ICE when emitting error
messages about invalid labels.
(dump_function_to_file): Reset CFUN before emitting the body
of the function.
(debug_function): New.
* passes.c (init_optimization_passes): Schedule
pass_lower_omp.
* langhooks-def.h (lhd_omp_predetermined_sharing,
lhd_omp_assignment, lhd_omp_firstprivatize_type_sizes):
Declare.
(LANG_HOOKS_OMP_FIRSTPRIVATIZE_TYPE_SIZES): Define.
(LANG_HOOKS_FOR_TYPES_INITIALIZER): Use it.
(LANG_HOOKS_OMP_PRIVATIZE_BY_REFERENCE,
LANG_HOOKS_OMP_PREDETERMINED_SHARING,
LANG_HOOKS_OMP_DISREGARD_VALUE_EXPR,
LANG_HOOKS_OMP_PRIVATE_DEBUG_CLAUSE,
LANG_HOOKS_OMP_CLAUSE_DEFAULT_CTOR,
LANG_HOOKS_OMP_CLAUSE_COPY_CTOR,
LANG_HOOKS_OMP_CLAUSE_ASSIGN_OP,
LANG_HOOKS_OMP_CLAUSE_DTOR): Define.
(LANG_HOOK_DECLS): Use them.
2006-01-18 Dmitry Kurochkin <dmitry.kurochkin@gmail.com>
Richard Henderson <rth@redhat.com>
Jakub Jelinek <jakub@redhat.com>
Diego Novillo <dnovillo@redhat.com>
* c-parser.c (pragma_omp_clause): Define.
(c_parser_declaration_or_fndef): Document OpenMP syntax.
(c_parser_compound_statement): Likewise.
(c_parser_statement): Likewise.
(c_parser_pragma): Handle omp pragmas.
(OMP_FOR_CLAUSE_MASK, OMP_SECTIONS_CLAUSE_MASK,
OMP_PARALLEL_CLAUSE_MASK, OMP_SINGLE_CLAUSE_MASK): Define.
(c_parser_omp_clause_name, check_no_duplicate_clause,
c_parser_omp_variable_list,
c_parser_omp_var_list_parens, c_parser_omp_clause_copyin,
c_parser_omp_clause_copyprivate,
c_parser_omp_clause_default,
c_parser_omp_clause_firstprivate, c_parser_omp_clause_if,
c_parser_omp_clause_lastprivate,
c_parser_omp_clause_nowait,
c_parser_omp_clause_num_threads,
c_parser_omp_clause_ordered, c_parser_omp_clause_private,
c_parser_omp_clause_reduction,
c_parser_omp_clause_schedule, c_parser_omp_clause_shared,
c_parser_omp_all_clauses, c_parser_omp_structured_block,
c_parser_omp_atomic, c_parser_omp_barrier,
c_parser_omp_critical, c_parser_omp_flush,
c_parser_omp_for_loop, c_parser_omp_for,
c_parser_omp_master, c_parser_omp_ordered,
c_parser_omp_sections_scope, c_parser_omp_sections,
c_parser_omp_parallel, c_parser_omp_single,
c_parser_omp_construct, c_parser_omp_threadprivate): New.
* c-pragma.c (init_pragma): Do omp pragma registration here.
* c.opt (fopenmp): New flag.
2006-01-18 Eric Christopher <echristo@apple.com>
* gcc.c (GOMP_SELF_SPECS): Bracket in #ifndef/#endif.
* config/darwin.h (GOMP_SELF_SPECS): Define.
testsuite/
2006-01-18 Richard Henderson <rth@redhat.com>
Aldy Hernandez <aldyh@redhat.com>
Jakub Jelinek <jakub@redhat.com>
Diego Novillo <dnovillo@redhat.com>
Uros Bizjak <uros@kss-loka.si>
* testsuite/gcc.dg/gomp: New directory.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@109902 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc')
161 files changed, 11397 insertions, 376 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 1f6041cc420..b57d517e789 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,353 @@ +2006-01-18 Richard Henderson <rth@redhat.com> + Aldy Hernandez <aldyh@redhat.com> + Jakub Jelinek <jakub@redhat.com> + Diego Novillo <dnovillo@redhat.com> + + * omp-low.c: New file. + * c-omp.c: New file. + +2006-01-18 Richard Henderson <rth@redhat.com> + Jakub Jelinek <jakub@redhat.com> + Diego Novillo <dnovillo@redhat.com> + + * doc/invoke.texi: Document -fopenmp. + * tree-dump.h (debug_function): Declare. + * hooks.c (hook_bool_tree_bool_false): New function. + (hook_tree_tree_null): Remove. + (hook_tree_tree_tree_null): New. + * hooks.h: Update to match. + * tree-pretty-print.c (debug_tree_chain): New. + (print_generic_expr): Handle TDF_CHAIN. + (dump_generic_node): Handle BLOCK. + Do not abort with incomplete SWITCH_EXPRs. + Do not dump body of an OpenMP directive if TDF_SLIM is given. + <case OMP_PARALLEL, OMP_FOR, OMP_SECTIONS>: Don't + print space after directive name. + <OMP_FOR>: Handle printing OMP_FOR_PRE_BODY. + Handle OMP_MASTER and OMP_ORDERED. + Handle printing of OMP_BODY just in one place, goto + dump_omp_body in the rest of OMP_* nodes that have + OMP_BODY. + Don't handle clause nodes here. Update omp statements to + use dump_omp_clauses. + Handle OMP_SINGLE, OMP_SECTIONS, OMP_SECTION, + OMP_CLAUSE_ORDERED, OMP_CLAUSE_SCHEDULE, OMP_ATOMIC, + OMP_CRITICAL, OMP_CLAUSE_NOWAIT, GOMP_CLAUSE_IF, + GOMP_CLAUSE_NUM_THREADS, GOMP_FOR, GOMP_CLAUSE_SHARED, + GOMP_CLAUSE_FIRSTPRIVATE, GOMP_CLAUSE_LASTPRIVATE, + GOMP_CLAUSE_COPYIN and GOMP_CLAUSE_COPYPRIVATE. + Adjust output for GOMP_PARALLEL. + (dump_omp_clauses): New. + (print_declaration): Dump DECL_VALUE_EXPR. + (op_symbol_1): Split out of op_symbol. + (dumping_stmts): Remove. Update all users. + * cgraph.c (cgraph_analyze_queue): New. + (cgraph_add_new_function): New. + * cgraph.h (cgraph_analyze_queue): Declare. + (cgraph_add_new_function): Declare. + (cgraph_lower_function): Remove. + * tree.c (walk_tree): Walk OMP_CLAUSE_CHAIN of OMP_CLAUSE_* + nodes. Use switch for all nodes, handle most of IS_EXPR_CODE_CLASS + and TYPE_P nodes in its default clause. + (empty_body_p): New. + (tree_range_check_failed): New. + (build5_stat): New. + * tree.h (OMP_CLAUSE_REDUCTION_INIT, + OMP_CLAUSE_REDUCTION_MERGE, + OMP_CLAUSE_REDUCTION_PLACEHOLDER, + OMP_CLAUSE_PRIVATE_DEBUG, + OMP_CLAUSE_LASTPRIVATE_FIRSTPRIVATE, OMP_FOR_PRE_BODY, + OMP_MASTER_BODY, OMP_ORDERED_BODY OMP_BODY, + OMP_CLAUSES, OMP_CLAUSE_DECL, OMP_CLAUSE_DEFAULT_KIND, + OMP_CLAUSE_CHAIN, OMP_CLAUSE_OUTER_DECL, + OMP_CLAUSE_INNER_DECL, OMP_CLAUSE_NUM_THREADS_EXPR, + OMP_CLAUSE_IF_EXPR, OMP_CLAUSE_SCHEDULE_CHUNK_EXPR, + OMP_CLAUSE_SCHEDULE_CHUNK_SIZE. OMP_PARALLEL_VAR_INIT, + OMP_PARALLEL_VAR_REDUC, OMP_FOR_VAR_INIT, + OMP_FOR_VAR_LAST, OMP_FOR_VAR_REDUC, + OMP_SECTIONS_VAR_INIT, OMP_SECTIONS_VAR_LAST, + OMP_SECTIONS_VAR_REDUC, OMP_CLAUSE_REDUCTION_CODE + OMP_SINGLE_CLAUSES, OMP_SINGLE_BODY, + OMP_CLAUSE_SCHEDULE_CHUNK_SIZE, OMP_SECTION_BODY, + OMP_CRITICAL_NAME, OMP_CRITICAL_BODY): New. + (TREE_RANGE_CHECK): New. + (empty_body_p): Declare. + (enum omp_clause_default_kind): New. + (build_string_literal): Declare. + (enum omp_clause_schedule_kind, OMP_CLAUSE_SCHEDULE_KIND): New. + (build5_stat, build5): Declare. + * tree-pass.h (TDF_CHAIN): Define. + * tree-pass.h (PROP_gimple_lomp): Define. + (pass_lower_omp): Declare. + * diagnostic.h (debug_tree_chain): Declare. + * builtins.c (get_builtin_sync_mode): Use 0 as last argument to + mode_for_size. + (expand_builtin): Handle sync BUILT_IN_*_16 builtins. + * builtins.c (build_string_literal): Make extern. + * gcc.c (include_spec_function): New. + (static_spec_functions): Add it. + (main): Move load of libgomp.spec ... + (LINK_COMMAND_SPEC): ... here. + (link_gomp_spec): New. + (static_specs): Include it. + (LINK_COMMAND_SPEC): Add link_gomp. + (GOMP_SELF_SPECS): New. + (driver_self_specs): Include it. + (switch_matches): Don't mark inline. + (main): Load libgomp.spec. + * tree-gimple.c (is_gimple_stmt): True for OMP_MASTER, + OMP_ORDERED, OMP_CRITICAL, OMP_SECTIONS, OMP_SECTION, + and OMP_SINGLE, OMP_FOR and OMP_PARALLEL. + * tree-gimple.h (enum omp_parallel): Declare. + (determine_parallel_type): Declare. + (omp_firstprivatize_variable): Declare. + (omp_reduction_init): Declare. + (diagnose_omp_structured_block_errors): Declare. + (struct walk_stmt_info): Add want_return_expr. + (struct walk_stmt_info): Add want_bind_expr, want_locations. + (find_omp_clause): Declare. + (insert_field_into_struct): Declare. + (struct walk_stmt_info): Move from tree-nested.c + (walk_stmts): Declare. + * c-cppbuiltin.c (c_cpp_builtins): If -fopenmp, #define _OPENMP + to 200505. + * cgraphunit.c (cgraph_lower_function): Make static. + (cgraph_finalize_pending_functions): New. + (cgraph_finalize_function): Call it. + (cgraph_finalize_compilation_unit): Likewise. + * builtin-types.def (BT_I16, BT_FN_I16_VPTR_I16, + BT_FN_BOOL_VPTR_I16_I16, BT_FN_I16_VPTR_I16_I16): Add. + (BT_FN_UINT_UINT): New. + (DEF_FUNCTION_TYPE_6, DEF_FUNCTION_TYPE_7, + DEF_FUNCTION_TYPE_VAR_4): Document. + (BT_PTR_LONG, BT_PTR_PTR, BT_FN_BOOL, BT_FN_INT, + BT_FN_VOID_PTRPTR, BT_PTR_FN_VOID_PTR, + BT_FN_BOOL_LONGPTR_LONGPTR, BT_FN_VOID_OMPFN_PTR_UINT, + BT_FN_VOID_OMPFN_PTR_UINT_UINT, + BT_FN_BOOL_LONG_LONG_LONG_LONGPTR_LONGPTR, + BT_FN_BOOL_LONG_LONG_LONG_LONG_LONGPTR_LONGPTR, + BT_FN_VOID_OMPFN_PTR_UINT_LONG_LONG_LONG, + BT_FN_VOID_OMPFN_PTR_UINT_LONG_LONG_LONG_LONG): New. + * builtins.def: Update DEF_BUILTIN comment to include COND argument. + Move all DEF_SYNC_BUILTIN () and DEF_GOMP_BUILTIN () builtins + into separate files. + (DEF_GOMP_BUILTIN): New. + (BUILT_IN_OMP_GET_THREAD_NUM, BUILT_IN_GOMP_BARRIER, + BUILT_IN_GOMP_CRITICAL_START, BUILT_IN_GOMP_CRITICAL_END, + BUILT_IN_GOMP_CRITICAL_NAME_START, BUILT_IN_GOMP_CRITICAL_NAME_END, + BUILT_IN_GOMP_LOOP_STATIC_START, BUILT_IN_GOMP_LOOP_DYNAMIC_START, + BUILT_IN_GOMP_LOOP_GUIDED_START, BUILT_IN_GOMP_LOOP_RUNTIME_START, + BUILT_IN_GOMP_LOOP_ORDERED_STATIC_START, + BUILT_IN_GOMP_LOOP_ORDERED_DYNAMIC_START, + BUILT_IN_GOMP_LOOP_ORDERED_GUIDED_START, + BUILT_IN_GOMP_LOOP_ORDERED_RUNTIME_START, + BUILT_IN_GOMP_LOOP_STATIC_NEXT, BUILT_IN_GOMP_LOOP_DYNAMIC_NEXT, + BUILT_IN_GOMP_LOOP_GUIDED_NEXT, BUILT_IN_GOMP_LOOP_RUNTIME_NEXT, + BUILT_IN_GOMP_LOOP_ORDERED_STATIC_NEXT, + BUILT_IN_GOMP_LOOP_ORDERED_DYNAMIC_NEXT, + BUILT_IN_GOMP_LOOP_ORDERED_GUIDED_NEXT, + BUILT_IN_GOMP_LOOP_ORDERED_RUNTIME_NEXT, + BUILT_IN_GOMP_PARALLEL_LOOP_STATIC_START, + BUILT_IN_GOMP_PARALLEL_LOOP_DYNAMIC_START, + BUILT_IN_GOMP_PARALLEL_LOOP_GUIDED_START, + BUILT_IN_GOMP_PARALLEL_LOOP_RUNTIME_START, + BUILT_IN_GOMP_LOOP_END, BUILT_IN_GOMP_LOOP_END_NOWAIT, + BUILT_IN_GOMP_ORDERED_START, BUILT_IN_GOMP_ORDERED_END, + BUILT_IN_GOMP_PARALLEL_START, BUILT_IN_GOMP_PARALLEL_END, + BUILT_IN_GOMP_SECTIONS_START, BUILT_IN_GOMP_SECTIONS_NEXT, + BUILT_IN_GOMP_PARALLEL_SECTIONS_START, BUILT_IN_GOMP_SECTIONS_END, + BUILT_IN_GOMP_SECTIONS_END_NOWAIT, BUILT_IN_GOMP_SINGLE_START, + BUILT_IN_GOMP_SINGLE_COPY_START, BUILT_IN_GOMP_SINGLE_COPY_END): New. + * sync-builtins.def: New file, moved from builtins.def. + * omp-builtins.def: New file, moved from builtins.def. + * c-objc-common.h (LANG_HOOKS_OMP_PREDETERMINED_SHARING): Redefine. + * gimple-low.c (lower_function_body): Clear data. + (lower_stmt): Do not handle COMPOUND_EXPR. + Remove call to print_node_brief. + * c-tree.h (c_finish_omp_clauses): New prototype. + (C_DECL_THREADPRIVATE_P): Define. + (lookup_name_no_remap, c_omp_remap_private): Remove + (c_begin_omp_parallel, c_finish_omp_parallel): Update. + (check_for_loop_decls): Update decl. + (lookup_name_no_remap, c_omp_remap_private): Declare. + (build_indirect_ref, build_modify_expr, pushdecl, + pushdecl_top_level): Move to c-common.h. + * dwarf2out.c (loc_descriptor_from_tree_1): Don't set unsignedp + before the switch, but just in the 2 places that need it. + * c-decl.c (diagnose_mismatched_decls): Do not check for + mismatched thread-local attributes when OLDDECL is marked + threadprivate and NEWDECL has no thread-local attributes. + (merge_decls): Merge C_DECL_THREADPRIVATE_P. + (c_gimple_diagnostics_recursively): Rename from + c_warn_unused_result_recursively. Invoke + diagnose_omp_structured_block_errors. + (check_for_loop_decls): Return a singular decl found. + * langhooks.c (lhd_omp_predetermined_sharing): Return + OMP_CLAUSE_DEFAULT_SHARED for DECL_ARTIFICIAL decls. + (lhd_omp_firstprivatize_type_sizes): New. + (lhd_omp_assignment): New. + (lhd_omp_predetermined_sharing): New. + * langhooks.h (struct gimplify_omp_ctx): Forward declare. + (struct lang_hooks_for_types): Add + omp_firstprivatize_type_sizes, omp_privatize_by_reference, + omp_predetermined_sharing, omp_disregard_value_expr, + omp_private_debug_clause, omp_clause_default_ctor, + omp_clause_copy_ctor, omp_clause_assign_op, omp_clause_dtor. + (c_finish_omp_clauses): New. + (c_finish_bc_stmt): Diagnose break within omp for. + (c_begin_omp_parallel, c_finish_omp_parallel): New. + (build_unary_op): Return error_mark after reporting + a readonly_error. + (build_modify_expr): Likewise. + * gimplify.c: Include optabs.h and pointer-set.h. + (enum gimplify_omp_var_data): Declare. + (struct gimplify_omp_ctx): Declare. + (struct gimplify_ctx): Add fields prev_context, combined_pre_p + and combined_ctxp. + (gimplify_ctxp, gimplify_omp_ctxp): New local variables. + (push_gimplify_context, pop_gimplify_context): Allow nesting. + (splay_tree_compare_decl_uid): New. + (new_omp_context): New. + (delete_omp_context): New. + (gimple_add_tmp_var): Call omp_add_variable. + (gimplify_bind_expr): Likewise. + (gimplify_var_or_parm_decl): If omp_notice_variable returned + true, disregard DECL_VALUE_EXPR on the decl if any. + (gimplify_expr_in_ctx): New. + (omp_firstprivatize_variable, omp_firstprivatize_type_sizes + omp_add_variable, omp_notice_variable, omp_is_private + gimplify_scan_omp_clauses, gimplify_adjust_omp_clauses_1 + gimplify_adjust_omp_clauses, gimplify_omp_parallel + gimplify_omp_for, gimplify_omp_workshare, goa_lhs_expr_p + gimplify_omp_atomic_fetch_op, goa_stabilize_expr + gimplify_omp_atomic_pipeline, gimplify_omp_atomic_mutex + gimplify_omp_atomic): New. + (gimplify_expr): Handle OMP_PARALLEL, OMP_FOR, OMP_SECTIONS, + OMP_SINGLE, OMP_SECTION, OMP_MASTER, OMP_ORDERED, + OMP_CRITICAL and OMP_ATOMIC. + (gimplify_body): Verify gimplify_ctxp is empty after gimplification. + * c-pragma.h (enum pragma_kind): Add + PRAGMA_OMP_ATOMIC, PRAGMA_OMP_BARRIER, + PRAGMA_OMP_CRITICAL, PRAGMA_OMP_FLUSH, PRAGMA_OMP_FOR, + PRAGMA_OMP_MASTER, PRAGMA_OMP_ORDERED, + PRAGMA_OMP_PARALLEL, PRAGMA_OMP_PARALLEL_FOR, + PRAGMA_OMP_PARALLEL_SECTIONS, PRAGMA_OMP_SECTION, + PRAGMA_OMP_SECTIONS, PRAGMA_OMP_SINGLE, + PRAGMA_OMP_THREADPRIVATE. + * tree.def (OMP_PARALLEL, OMP_FOR, OMP_SECTIONS, + OMP_SINGLE, OMP_SECTION, OMP_MASTER, OMP_ORDERED, + OMP_CRITICAL, OMP_ATOMIC, OMP_CLAUSE_PRIVATE, + OMP_CLAUSE_SHARED, OMP_CLAUSE_FIRSTPRIVATE, + OMP_CLAUSE_LASTPRIVATE, OMP_CLAUSE_REDUCTION, + OMP_CLAUSE_COPYIN, OMP_CLAUSE_COPYPRIVATE, + OMP_CLAUSE_IF, OMP_CLAUSE_NUM_THREADS, + OMP_CLAUSE_SCHEDULE, OMP_CLAUSE_NOWAIT, + OMP_CLAUSE_ORDERED, OMP_CLAUSE_DEFAULT): Define. + * print-tree.c (print_node): Dump DECL_VALUE_EXPR. + * tree-ssa-dce.c (find_control_dependence): Do not assume that + ENTRY_BLOCK_PTR->next_bb == single_succ (ENTRY_BLOCK_PTR). + * tree-nested.c (convert_call_expr): Call walk_body on OMP_BODY for + OpenMP directives. + (struct nesting_info): Add field_map, + suppress_expansion, debug_var_chain. + (create_nesting_tree): Initialize them. + (lookup_field_for_decl): Use field_map. + (get_nonlocal_debug_decl, get_local_debug_decl): New. + (convert_local_omp_clauses): New. + (finalize_nesting_tree_1): Add debug_var_chain to toplevel block. + (walk_body): Split out of walk_function. + (convert_nonlocal_omp_clauses, convert_local_omp_clauses): New. + (convert_nonlocal_reference): Handle omp statements. + (convert_local_reference): Likewise. + (unnest_nesting_tree_1): Split out of finalize_nesting_tree_1. + (unnest_nesting_tree): New. + (lower_nested_functions): Call it. + (insert_field_into_struct): Make extern. + (struct walk_stmt_info): Move to tree-gimple.h. + (walk_stmts): Make extern. + * omp-builtins.def: New file. + * tree-iterator.c (expr_only): Clarify comment. + * c-common.h (pushdecl_top_level, pushdecl, + build_modify_expr, build_indirect_ref, + c_finish_omp_master, c_finish_omp_critical, + c_finish_omp_ordered, c_finish_omp_barrier, + c_finish_omp_atomic, c_finish_omp_flush, + c_finish_omp_for, c_split_parallel_clauses, + omp_clause_default_kind, c_omp_sharing_predetermined, + c_omp_remap_decl): Declare. + * Makefile.in (BUILTINS_DEF): Add omp-builtins.def. + (OBJS-common): Add omp-low.o. + (c-omp.o, omp-low.o): Add. + (gimplify.o): Add dependency on $(OPTABS_H). + (GTFILES): Add omp-low.c. + (gt-stringpool.h): Add. + * tree-cfg.c (set_bb_for_stmt): Do not update the + block-to-labels map if we are currently expanding to RTL. + (tree_node_can_be_shared): Remove unnecessary CONSTANT_CLASS_P + checks. + Handle IDENTIFIER_NODE. + (tree_verify_flow_info): Do not ICE when emitting error + messages about invalid labels. + (dump_function_to_file): Reset CFUN before emitting the body + of the function. + (debug_function): New. + * passes.c (init_optimization_passes): Schedule + pass_lower_omp. + * langhooks-def.h (lhd_omp_predetermined_sharing, + lhd_omp_assignment, lhd_omp_firstprivatize_type_sizes): + Declare. + (LANG_HOOKS_OMP_FIRSTPRIVATIZE_TYPE_SIZES): Define. + (LANG_HOOKS_FOR_TYPES_INITIALIZER): Use it. + (LANG_HOOKS_OMP_PRIVATIZE_BY_REFERENCE, + LANG_HOOKS_OMP_PREDETERMINED_SHARING, + LANG_HOOKS_OMP_DISREGARD_VALUE_EXPR, + LANG_HOOKS_OMP_PRIVATE_DEBUG_CLAUSE, + LANG_HOOKS_OMP_CLAUSE_DEFAULT_CTOR, + LANG_HOOKS_OMP_CLAUSE_COPY_CTOR, + LANG_HOOKS_OMP_CLAUSE_ASSIGN_OP, + LANG_HOOKS_OMP_CLAUSE_DTOR): Define. + (LANG_HOOK_DECLS): Use them. + +2006-01-18 Dmitry Kurochkin <dmitry.kurochkin@gmail.com> + Richard Henderson <rth@redhat.com> + Jakub Jelinek <jakub@redhat.com> + Diego Novillo <dnovillo@redhat.com> + + * c-parser.c (pragma_omp_clause): Define. + (c_parser_declaration_or_fndef): Document OpenMP syntax. + (c_parser_compound_statement): Likewise. + (c_parser_statement): Likewise. + (c_parser_pragma): Handle omp pragmas. + (OMP_FOR_CLAUSE_MASK, OMP_SECTIONS_CLAUSE_MASK, + OMP_PARALLEL_CLAUSE_MASK, OMP_SINGLE_CLAUSE_MASK): Define. + (c_parser_omp_clause_name, check_no_duplicate_clause, + c_parser_omp_variable_list, + c_parser_omp_var_list_parens, c_parser_omp_clause_copyin, + c_parser_omp_clause_copyprivate, + c_parser_omp_clause_default, + c_parser_omp_clause_firstprivate, c_parser_omp_clause_if, + c_parser_omp_clause_lastprivate, + c_parser_omp_clause_nowait, + c_parser_omp_clause_num_threads, + c_parser_omp_clause_ordered, c_parser_omp_clause_private, + c_parser_omp_clause_reduction, + c_parser_omp_clause_schedule, c_parser_omp_clause_shared, + c_parser_omp_all_clauses, c_parser_omp_structured_block, + c_parser_omp_atomic, c_parser_omp_barrier, + c_parser_omp_critical, c_parser_omp_flush, + c_parser_omp_for_loop, c_parser_omp_for, + c_parser_omp_master, c_parser_omp_ordered, + c_parser_omp_sections_scope, c_parser_omp_sections, + c_parser_omp_parallel, c_parser_omp_single, + c_parser_omp_construct, c_parser_omp_threadprivate): New. + * c-pragma.c (init_pragma): Do omp pragma registration here. + * c.opt (fopenmp): New flag. + +2006-01-18 Eric Christopher <echristo@apple.com> + + * gcc.c (GOMP_SELF_SPECS): Bracket in #ifndef/#endif. + * config/darwin.h (GOMP_SELF_SPECS): Define. + 2006-01-18 Kazu Hirata <kazu@codesourcery.com> * rtl.h: Remove the prototype for reg_alloc. diff --git a/gcc/Makefile.in b/gcc/Makefile.in index a993f971eeb..dd2c8452926 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -737,7 +737,7 @@ RTL_BASE_H = rtl.h rtl.def $(MACHMODE_H) reg-notes.def insn-notes.def \ input.h real.h statistics.h RTL_H = $(RTL_BASE_H) genrtl.h PARAMS_H = params.h params.def -BUILTINS_DEF = builtins.def sync-builtins.def +BUILTINS_DEF = builtins.def sync-builtins.def omp-builtins.def TREE_H = tree.h tree.def $(MACHMODE_H) tree-check.h $(BUILTINS_DEF) \ input.h statistics.h vec.h treestruct.def BASIC_BLOCK_H = basic-block.h bitmap.h sbitmap.h varray.h $(PARTITION_H) \ @@ -948,7 +948,7 @@ C_AND_OBJC_OBJS = attribs.o c-errors.o c-lex.o c-pragma.o c-decl.o c-typeck.o \ c-convert.o c-aux-info.o c-common.o c-opts.o c-format.o c-semantics.o \ c-incpath.o cppdefault.o c-ppoutput.o c-cppbuiltin.o prefix.o \ c-objc-common.o c-dump.o c-pch.o c-parser.o $(C_TARGET_OBJS) \ - c-gimplify.o tree-mudflap.o c-pretty-print.o + c-gimplify.o tree-mudflap.o c-pretty-print.o c-omp.o # Language-specific object files for C. C_OBJS = c-lang.o stub-objc.o $(C_AND_OBJC_OBJS) @@ -963,7 +963,7 @@ OBJS-common = \ tree-ssa-pre.o tree-ssa-live.o tree-ssa-operands.o tree-ssa-alias.o \ tree-ssa-phiopt.o tree-ssa-forwprop.o tree-nested.o tree-ssa-dse.o \ tree-ssa-dom.o domwalk.o tree-tailcall.o gimple-low.o tree-iterator.o \ - tree-phinodes.o tree-ssanames.o tree-sra.o tree-complex.o \ + omp-low.o tree-phinodes.o tree-ssanames.o tree-sra.o tree-complex.o \ tree-vect-generic.o tree-ssa-loop.o tree-ssa-loop-niter.o \ tree-ssa-loop-manip.o tree-ssa-threadupdate.o \ tree-vectorizer.o tree-vect-analyze.o tree-vect-transform.o \ @@ -1671,6 +1671,9 @@ c-pch.o : c-pch.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(CPPLIB_H) $(TREE_H) \ -DHOST_MACHINE=\"$(host)\" -DTARGET_MACHINE=\"$(target)\" \ $< $(OUTPUT_OPTION) +c-omp.o : c-omp.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) \ + $(FUNCTION_H) $(C_COMMON_H) toplev.h $(TREE_GIMPLE_H) + # Language-independent files. DRIVER_DEFINES = \ @@ -2034,12 +2037,16 @@ gimplify.o : gimplify.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) \ $(DIAGNOSTIC_H) $(TREE_GIMPLE_H) $(TREE_INLINE_H) $(VARRAY_H) langhooks.h \ $(LANGHOOKS_DEF_H) $(TREE_FLOW_H) $(CGRAPH_H) $(TIMEVAR_H) $(TM_H) \ coretypes.h except.h $(FLAGS_H) $(RTL_H) $(FUNCTION_H) $(EXPR_H) output.h \ - $(GGC_H) gt-gimplify.h $(HASHTAB_H) real.h $(TARGET_H) toplev.h + $(GGC_H) gt-gimplify.h $(HASHTAB_H) real.h $(TARGET_H) toplev.h $(OPTABS_H) gimple-low.o : gimple-low.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) \ $(DIAGNOSTIC_H) $(TREE_GIMPLE_H) $(TREE_INLINE_H) $(VARRAY_H) langhooks.h \ $(LANGHOOKS_DEF_H) $(TREE_FLOW_H) $(TIMEVAR_H) $(TM_H) coretypes.h \ except.h $(FLAGS_H) $(RTL_H) $(FUNCTION_H) $(EXPR_H) tree-pass.h \ $(HASHTAB_H) toplev.h +omp-low.o : omp-low.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) \ + $(RTL_H) $(TREE_GIMPLE_H) $(TREE_INLINE_H) langhooks.h $(DIAGNOSTIC_H) \ + $(TREE_FLOW_H) $(TIMEVAR_H) $(FLAGS_H) $(EXPR_H) toplev.h tree-pass.h \ + $(GGC_H) tree-browser.o : tree-browser.c tree-browser.def $(CONFIG_H) $(SYSTEM_H) \ $(TREE_H) $(TREE_INLINE_H) $(DIAGNOSTIC_H) $(HASHTAB_H) \ $(TM_H) coretypes.h @@ -2859,7 +2866,7 @@ GTFILES = $(srcdir)/input.h $(srcdir)/coretypes.h \ $(srcdir)/tree-profile.c $(srcdir)/tree-nested.c \ $(srcdir)/ipa-reference.c $(srcdir)/tree-ssa-structalias.h \ $(srcdir)/tree-ssa-structalias.c \ - $(srcdir)/c-pragma.h \ + $(srcdir)/c-pragma.h $(srcdir)/omp-low.c \ $(srcdir)/targhooks.c $(out_file) \ @all_gtfiles@ @@ -2893,7 +2900,7 @@ gt-tree-ssanames.h gt-tree-iterator.h gt-gimplify.h \ gt-tree-phinodes.h gt-tree-nested.h \ gt-tree-ssa-operands.h gt-tree-ssa-propagate.h \ gt-tree-ssa-structalias.h \ -gt-stringpool.h gt-targhooks.h : s-gtype ; @true +gt-stringpool.h gt-targhooks.h gt-omp-low.h : s-gtype ; @true define echo_quoted_to_gtyp echo "\"$(gtyp)\", " >> tmp-gtyp.h diff --git a/gcc/builtin-types.def b/gcc/builtin-types.def index 9540f3a719f..1e97d6e68be 100644 --- a/gcc/builtin-types.def +++ b/gcc/builtin-types.def @@ -33,6 +33,8 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA DEF_FUNCTION_TYPE_3 (ENUM, RETURN, ARG1, ARG2, ARG3) DEF_FUNCTION_TYPE_4 (ENUM, RETURN, ARG1, ARG2, ARG3, ARG4) DEF_FUNCTION_TYPE_5 (ENUM, RETURN, ARG1, ARG2, ARG3, ARG4, ARG5) + DEF_FUNCTION_TYPE_6 (ENUM, RETURN, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6) + DEF_FUNCTION_TYPE_7 (ENUM, RETURN, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7) These macros describe function types. ENUM is as above. The RETURN type is one of the enumerals already defined. ARG1, ARG2, @@ -43,6 +45,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA DEF_FUNCTION_TYPE_VAR_2 (ENUM, RETURN, ARG1, ARG2) DEF_FUNCTION_TYPE_VAR_3 (ENUM, RETURN, ARG1, ARG2, ARG3) DEF_FUNCTION_TYPE_VAR_4 (ENUM, RETURN, ARG1, ARG2, ARG3, ARG4) + DEF_FUNCTION_TYPE_VAR_4 (ENUM, RETURN, ARG1, ARG2, ARG3, ARG4, ARG5) Similar, but for function types that take variable arguments. For example: @@ -115,10 +118,14 @@ DEF_PRIMITIVE_TYPE (BT_I8, builtin_type_for_size (BITS_PER_UNIT*8, 1)) DEF_PRIMITIVE_TYPE (BT_I16, builtin_type_for_size (BITS_PER_UNIT*16, 1)) DEF_POINTER_TYPE (BT_PTR_CONST_STRING, BT_CONST_STRING) +DEF_POINTER_TYPE (BT_PTR_LONG, BT_LONG) +DEF_POINTER_TYPE (BT_PTR_PTR, BT_PTR) DEF_FUNCTION_TYPE_0 (BT_FN_VOID, BT_VOID) +DEF_FUNCTION_TYPE_0 (BT_FN_BOOL, BT_BOOL) DEF_FUNCTION_TYPE_0 (BT_FN_PTR, BT_PTR) DEF_FUNCTION_TYPE_0 (BT_FN_PID, BT_PID) +DEF_FUNCTION_TYPE_0 (BT_FN_INT, BT_INT) DEF_FUNCTION_TYPE_0 (BT_FN_UINT, BT_UINT) DEF_FUNCTION_TYPE_0 (BT_FN_FLOAT, BT_FLOAT) DEF_FUNCTION_TYPE_0 (BT_FN_DOUBLE, BT_DOUBLE) @@ -194,6 +201,10 @@ DEF_FUNCTION_TYPE_1 (BT_FN_DFLOAT32_DFLOAT32, BT_DFLOAT32, BT_DFLOAT32) DEF_FUNCTION_TYPE_1 (BT_FN_DFLOAT64_DFLOAT64, BT_DFLOAT64, BT_DFLOAT64) DEF_FUNCTION_TYPE_1 (BT_FN_DFLOAT128_DFLOAT128, BT_DFLOAT128, BT_DFLOAT128) DEF_FUNCTION_TYPE_1 (BT_FN_VOID_VPTR, BT_VOID, BT_VOLATILE_PTR) +DEF_FUNCTION_TYPE_1 (BT_FN_VOID_PTRPTR, BT_VOID, BT_PTR_PTR) +DEF_FUNCTION_TYPE_1 (BT_FN_UINT_UINT, BT_UINT, BT_UINT) + +DEF_POINTER_TYPE (BT_PTR_FN_VOID_PTR, BT_FN_VOID_PTR) DEF_FUNCTION_TYPE_2 (BT_FN_VOID_PTR_INT, BT_VOID, BT_PTR, BT_INT) DEF_FUNCTION_TYPE_2 (BT_FN_STRING_STRING_CONST_STRING, @@ -281,6 +292,8 @@ DEF_FUNCTION_TYPE_2 (BT_FN_I2_VPTR_I2, BT_I2, BT_VOLATILE_PTR, BT_I2) DEF_FUNCTION_TYPE_2 (BT_FN_I4_VPTR_I4, BT_I4, BT_VOLATILE_PTR, BT_I4) DEF_FUNCTION_TYPE_2 (BT_FN_I8_VPTR_I8, BT_I8, BT_VOLATILE_PTR, BT_I8) DEF_FUNCTION_TYPE_2 (BT_FN_I16_VPTR_I16, BT_I16, BT_VOLATILE_PTR, BT_I16) +DEF_FUNCTION_TYPE_2 (BT_FN_BOOL_LONGPTR_LONGPTR, + BT_BOOL, BT_PTR_LONG, BT_PTR_LONG) DEF_FUNCTION_TYPE_3 (BT_FN_STRING_STRING_CONST_STRING_SIZE, BT_STRING, BT_STRING, BT_CONST_STRING, BT_SIZE) @@ -343,6 +356,8 @@ DEF_FUNCTION_TYPE_3 (BT_FN_I4_VPTR_I4_I4, BT_I4, BT_VOLATILE_PTR, BT_I4, BT_I4) DEF_FUNCTION_TYPE_3 (BT_FN_I8_VPTR_I8_I8, BT_I8, BT_VOLATILE_PTR, BT_I8, BT_I8) DEF_FUNCTION_TYPE_3 (BT_FN_I16_VPTR_I16_I16, BT_I16, BT_VOLATILE_PTR, BT_I16, BT_I16) +DEF_FUNCTION_TYPE_3 (BT_FN_VOID_OMPFN_PTR_UINT, BT_VOID, BT_PTR_FN_VOID_PTR, + BT_PTR, BT_UINT) DEF_FUNCTION_TYPE_4 (BT_FN_SIZE_CONST_PTR_SIZE_SIZE_FILEPTR, BT_SIZE, BT_CONST_PTR, BT_SIZE, BT_SIZE, BT_FILEPTR) @@ -358,14 +373,29 @@ DEF_FUNCTION_TYPE_4 (BT_FN_STRING_STRING_CONST_STRING_SIZE_SIZE, BT_STRING, BT_STRING, BT_CONST_STRING, BT_SIZE, BT_SIZE) DEF_FUNCTION_TYPE_4 (BT_FN_INT_FILEPTR_INT_CONST_STRING_VALIST_ARG, BT_INT, BT_FILEPTR, BT_INT, BT_CONST_STRING, BT_VALIST_ARG) +DEF_FUNCTION_TYPE_4 (BT_FN_VOID_OMPFN_PTR_UINT_UINT, + BT_VOID, BT_PTR_FN_VOID_PTR, BT_PTR, BT_UINT, BT_UINT) DEF_FUNCTION_TYPE_5 (BT_FN_INT_STRING_INT_SIZE_CONST_STRING_VALIST_ARG, BT_INT, BT_STRING, BT_INT, BT_SIZE, BT_CONST_STRING, BT_VALIST_ARG) +DEF_FUNCTION_TYPE_5 (BT_FN_BOOL_LONG_LONG_LONG_LONGPTR_LONGPTR, + BT_BOOL, BT_LONG, BT_LONG, BT_LONG, + BT_PTR_LONG, BT_PTR_LONG) DEF_FUNCTION_TYPE_6 (BT_FN_INT_STRING_SIZE_INT_SIZE_CONST_STRING_VALIST_ARG, BT_INT, BT_STRING, BT_SIZE, BT_INT, BT_SIZE, BT_CONST_STRING, BT_VALIST_ARG) +DEF_FUNCTION_TYPE_6 (BT_FN_BOOL_LONG_LONG_LONG_LONG_LONGPTR_LONGPTR, + BT_BOOL, BT_LONG, BT_LONG, BT_LONG, BT_LONG, + BT_PTR_LONG, BT_PTR_LONG) +DEF_FUNCTION_TYPE_6 (BT_FN_VOID_OMPFN_PTR_UINT_LONG_LONG_LONG, + BT_VOID, BT_PTR_FN_VOID_PTR, BT_PTR, BT_UINT, + BT_LONG, BT_LONG, BT_LONG) + +DEF_FUNCTION_TYPE_7 (BT_FN_VOID_OMPFN_PTR_UINT_LONG_LONG_LONG_LONG, + BT_VOID, BT_PTR_FN_VOID_PTR, BT_PTR, BT_UINT, + BT_LONG, BT_LONG, BT_LONG, BT_LONG) DEF_FUNCTION_TYPE_VAR_0 (BT_FN_VOID_VAR, BT_VOID) DEF_FUNCTION_TYPE_VAR_0 (BT_FN_INT_VAR, BT_INT) @@ -404,3 +434,4 @@ DEF_FUNCTION_TYPE_VAR_5 (BT_FN_INT_STRING_SIZE_INT_SIZE_CONST_STRING_VAR, DEF_POINTER_TYPE (BT_PTR_FN_VOID_VAR, BT_FN_VOID_VAR) DEF_FUNCTION_TYPE_3 (BT_FN_PTR_PTR_FN_VOID_VAR_PTR_SIZE, BT_PTR, BT_PTR_FN_VOID_VAR, BT_PTR, BT_SIZE) + diff --git a/gcc/builtins.c b/gcc/builtins.c index 657f38b7e22..2d4322f9700 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -76,7 +76,6 @@ static const char *c_getstr (tree); static rtx c_readstr (const char *, enum machine_mode); static int target_char_cast (tree, char *); static rtx get_memory_rtx (tree, tree); -static tree build_string_literal (int, const char *); static int apply_args_size (void); static int apply_result_size (void); #if defined (HAVE_untyped_call) || defined (HAVE_untyped_return) @@ -4785,7 +4784,7 @@ expand_builtin_copysign (tree arglist, rtx target, rtx subtarget) /* Create a new constant string literal and return a char* pointer to it. The STRING_CST value is the LEN characters at STR. */ -static tree +tree build_string_literal (int len, const char *str) { tree t, elem, index, type; diff --git a/gcc/builtins.def b/gcc/builtins.def index d3563612237..b0d54a085d0 100644 --- a/gcc/builtins.def +++ b/gcc/builtins.def @@ -23,7 +23,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA /* Before including this file, you should define a macro: DEF_BUILTIN (ENUM, NAME, CLASS, TYPE, LIBTYPE, BOTH_P, - FALLBACK_P, NONANSI_P, ATTRS, IMPLICIT) + FALLBACK_P, NONANSI_P, ATTRS, IMPLICIT, COND) This macro will be called once for each builtin function. The ENUM will be of type `enum built_in_function', and will indicate @@ -61,8 +61,10 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA define it's meaning. When user uses floorf we may assume that the floorf has the meaning we expect, but we can't produce floorf by simplifying floor((double)float) since the runtime need not implement - it. */ - + it. + + The builtins is registered only if COND is true. */ + /* A GCC builtin (like __builtin_saveregs) is provided by the compiler, but does not correspond to a function in the standard library. */ @@ -133,6 +135,13 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA DEF_BUILTIN (ENUM, NAME, BUILT_IN_NORMAL, 0, 0, false, false, \ false, 0, false, false) +/* Builtin used by the implementation of GNU OpenMP. None of these are + actually implemented in the compiler; they're all in libgomp. */ +#undef DEF_GOMP_BUILTIN +#define DEF_GOMP_BUILTIN(ENUM, NAME, TYPE, ATTRS) \ + DEF_BUILTIN (ENUM, "__builtin_" NAME, BUILT_IN_NORMAL, TYPE, TYPE, \ + false, true, true, ATTRS, false, flag_openmp) + /* Define an attribute list for math functions that are normally "impure" because some of them may write into global memory for `errno'. If !flag_errno_math they are instead "const". */ @@ -711,3 +720,6 @@ DEF_BUILTIN_STUB (BUILT_IN_PROFILE_FUNC_EXIT, "profile_func_exit") /* Synchronization Primitives. */ #include "sync-builtins.def" + +/* OpenMP builtins. */ +#include "omp-builtins.def" diff --git a/gcc/c-common.h b/gcc/c-common.h index 735250f8011..6da433c6524 100644 --- a/gcc/c-common.h +++ b/gcc/c-common.h @@ -296,6 +296,10 @@ extern tree push_stmt_list (void); extern tree pop_stmt_list (tree); extern tree add_stmt (tree); extern void push_cleanup (tree, tree, bool); +extern tree pushdecl_top_level (tree); +extern tree pushdecl (tree); +extern tree build_modify_expr (tree, enum tree_code, tree); +extern tree build_indirect_ref (tree, const char *); extern int c_expand_decl (tree); @@ -934,6 +938,21 @@ extern void pp_file_change (const struct line_map *); extern void pp_dir_change (cpp_reader *, const char *); extern bool check_missing_format_attribute (tree, tree); +/* In c-omp.c */ +extern tree c_finish_omp_master (tree); +extern tree c_finish_omp_critical (tree, tree); +extern tree c_finish_omp_ordered (tree); +extern void c_finish_omp_barrier (void); +extern void c_finish_omp_atomic (enum tree_code, tree, tree); +extern void c_finish_omp_flush (void); +extern tree c_finish_omp_for (location_t, tree, tree, tree, tree, tree, tree); +extern void c_split_parallel_clauses (tree, tree *, tree *); +extern enum omp_clause_default_kind c_omp_predetermined_sharing (tree); + +/* Not in c-omp.c; provided by the front end. */ +extern bool c_omp_sharing_predetermined (tree); +extern tree c_omp_remap_decl (tree, bool); + /* In order for the format checking to accept the C frontend diagnostic framework extensions, you must include this file before toplev.h, not after. The C front end formats are a subset of those diff --git a/gcc/c-cppbuiltin.c b/gcc/c-cppbuiltin.c index e9f60f63bf3..f54d17e86e6 100644 --- a/gcc/c-cppbuiltin.c +++ b/gcc/c-cppbuiltin.c @@ -544,6 +544,9 @@ c_cpp_builtins (cpp_reader *pfile) else if (flag_stack_protect == 1) cpp_define (pfile, "__SSP__=1"); + if (flag_openmp) + cpp_define (pfile, "_OPENMP=200505"); + /* A straightforward target hook doesn't work, because of problems linking that hook's body when part of non-C front ends. */ # define preprocessing_asm_p() (cpp_get_options (pfile)->lang == CLK_ASM) diff --git a/gcc/c-decl.c b/gcc/c-decl.c index 4c786b6cdb1..a3b56c0b634 100644 --- a/gcc/c-decl.c +++ b/gcc/c-decl.c @@ -1385,7 +1385,14 @@ diagnose_mismatched_decls (tree newdecl, tree olddecl, { /* Only variables can be thread-local, and all declarations must agree on this property. */ - if (DECL_THREAD_LOCAL_P (newdecl) != DECL_THREAD_LOCAL_P (olddecl)) + if (C_DECL_THREADPRIVATE_P (olddecl) && !DECL_THREAD_LOCAL_P (newdecl)) + { + /* Nothing to check. Since OLDDECL is marked threadprivate + and NEWDECL does not have a thread-local attribute, we + will merge the threadprivate attribute into NEWDECL. */ + ; + } + else if (DECL_THREAD_LOCAL_P (newdecl) != DECL_THREAD_LOCAL_P (olddecl)) { if (DECL_THREAD_LOCAL_P (newdecl)) error ("thread-local declaration of %q+D follows " @@ -1672,6 +1679,13 @@ merge_decls (tree newdecl, tree olddecl, tree newtype, tree oldtype) if (DECL_INITIAL (newdecl) == 0) DECL_INITIAL (newdecl) = DECL_INITIAL (olddecl); + /* Merge the threadprivate attribute. */ + if (TREE_CODE (olddecl) == VAR_DECL && C_DECL_THREADPRIVATE_P (olddecl)) + { + DECL_TLS_MODEL (newdecl) = DECL_TLS_MODEL (olddecl); + C_DECL_THREADPRIVATE_P (newdecl) = 1; + } + if (CODE_CONTAINS_STRUCT (TREE_CODE (olddecl), TS_DECL_WITH_VIS)) { /* Merge the unused-warning information. */ @@ -6454,21 +6468,25 @@ store_parm_decls (void) cfun->x_dont_save_pending_sizes_p = 1; } -/* Handle attribute((warn_unused_result)) on FNDECL and all its nested - functions. */ +/* Emit diagnostics that require gimple input for detection. Operate on + FNDECL and all its nested functions. */ static void -c_warn_unused_result_recursively (tree fndecl) +c_gimple_diagnostics_recursively (tree fndecl) { struct cgraph_node *cgn; /* Handle attribute((warn_unused_result)). Relies on gimple input. */ c_warn_unused_result (&DECL_SAVED_TREE (fndecl)); + /* Notice when OpenMP structured block constraints are violated. */ + if (flag_openmp) + diagnose_omp_structured_block_errors (fndecl); + /* Finalize all nested functions now. */ cgn = cgraph_node (fndecl); for (cgn = cgn->nested; cgn ; cgn = cgn->next_nested) - c_warn_unused_result_recursively (cgn->decl); + c_gimple_diagnostics_recursively (cgn->decl); } /* Finish up a function declaration and compile that function @@ -6596,7 +6614,7 @@ finish_function (void) if (!decl_function_context (fndecl)) { c_genericize (fndecl); - c_warn_unused_result_recursively (fndecl); + c_gimple_diagnostics_recursively (fndecl); /* ??? Objc emits functions after finalizing the compilation unit. This should be cleaned up later and this conditional removed. */ @@ -6650,11 +6668,15 @@ c_expand_body (tree fndecl) } /* Check the declarations given in a for-loop for satisfying the C99 - constraints. */ -void + constraints. If exactly one such decl is found, return it. */ + +tree check_for_loop_decls (void) { struct c_binding *b; + tree one_decl = NULL_TREE; + int n_decls = 0; + if (!flag_isoc99) { @@ -6662,7 +6684,7 @@ check_for_loop_decls (void) the C99 for loop scope. This doesn't make much sense, so don't allow it. */ error ("%<for%> loop initial declaration used outside C99 mode"); - return; + return NULL_TREE; } /* C99 subclause 6.8.5 paragraph 3: @@ -6713,7 +6735,12 @@ check_for_loop_decls (void) error ("declaration of non-variable %q+D in %<for%> loop " "initial declaration", decl); } + + n_decls++; + one_decl = decl; } + + return n_decls == 1 ? one_decl : NULL_TREE; } /* Save and reinitialize the variables diff --git a/gcc/c-objc-common.h b/gcc/c-objc-common.h index 69212599612..136445ca68e 100644 --- a/gcc/c-objc-common.h +++ b/gcc/c-objc-common.h @@ -134,4 +134,7 @@ extern void c_initialize_diagnostics (diagnostic_context *); #undef LANG_HOOKS_GIMPLIFY_EXPR #define LANG_HOOKS_GIMPLIFY_EXPR c_gimplify_expr +#undef LANG_HOOKS_OMP_PREDETERMINED_SHARING +#define LANG_HOOKS_OMP_PREDETERMINED_SHARING c_omp_predetermined_sharing + #endif /* GCC_C_OBJC_COMMON */ diff --git a/gcc/c-omp.c b/gcc/c-omp.c new file mode 100644 index 00000000000..30e7e64b47b --- /dev/null +++ b/gcc/c-omp.c @@ -0,0 +1,429 @@ +/* This file contains routines to construct GNU OpenMP constructs, + called from parsing in the C and C++ front ends. + + Copyright (C) 2005 Free Software Foundation, Inc. + Contributed by Richard Henderson <rth@redhat.com>, + Diego Novillo <dnovillo@redhat.com>. + +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 2, 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 COPYING. If not, write to the Free +Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301, USA. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "tree.h" +#include "function.h" +#include "c-common.h" +#include "toplev.h" +#include "tree-gimple.h" +#include "bitmap.h" +#include "langhooks.h" + + +/* Complete a #pragma omp master construct. STMT is the structured-block + that follows the pragma. */ + +tree +c_finish_omp_master (tree stmt) +{ + return add_stmt (build1 (OMP_MASTER, void_type_node, stmt)); +} + +/* Complete a #pragma omp critical construct. STMT is the structured-block + that follows the pragma, NAME is the identifier in the pragma, or null + if it was omitted. */ + +tree +c_finish_omp_critical (tree body, tree name) +{ + tree stmt = make_node (OMP_CRITICAL); + TREE_TYPE (stmt) = void_type_node; + OMP_CRITICAL_BODY (stmt) = body; + OMP_CRITICAL_NAME (stmt) = name; + return add_stmt (stmt); +} + +/* Complete a #pragma omp ordered construct. STMT is the structured-block + that follows the pragma. */ + +tree +c_finish_omp_ordered (tree stmt) +{ + return add_stmt (build1 (OMP_ORDERED, void_type_node, stmt)); +} + + +/* Complete a #pragma omp barrier construct. */ + +void +c_finish_omp_barrier (void) +{ + tree x; + + x = built_in_decls[BUILT_IN_GOMP_BARRIER]; + x = build_function_call_expr (x, NULL); + add_stmt (x); +} + + +/* Complete a #pragma omp atomic construct. The expression to be + implemented atomically is LHS code= RHS. */ + +void +c_finish_omp_atomic (enum tree_code code, tree lhs, tree rhs) +{ + tree x, type, addr; + + if (lhs == error_mark_node || rhs == error_mark_node) + return; + + /* ??? According to one reading of the OpenMP spec, complex type are + supported, but there are no atomic stores for any architecture. + But at least icc 9.0 doesn't support complex types here either. + And lets not even talk about vector types... */ + type = TREE_TYPE (lhs); + if (!INTEGRAL_TYPE_P (type) + && !POINTER_TYPE_P (type) + && !SCALAR_FLOAT_TYPE_P (type)) + { + error ("invalid expression type for %<#pragma omp atomic%>"); + return; + } + + /* ??? Validate that rhs does not overlap lhs. */ + + /* Take and save the address of the lhs. From then on we'll reference it + via indirection. */ + addr = build_unary_op (ADDR_EXPR, lhs, 0); + if (addr == error_mark_node) + return; + addr = save_expr (addr); + lhs = build_indirect_ref (addr, NULL); + + /* There are lots of warnings, errors, and conversions that need to happen + in the course of interpreting a statement. Use the normal mechanisms + to do this, and then take it apart again. */ + x = build_modify_expr (lhs, code, rhs); + if (x == error_mark_node) + return; + gcc_assert (TREE_CODE (x) == MODIFY_EXPR); + rhs = TREE_OPERAND (x, 1); + + /* Punt the actual generation of atomic operations to common code. */ + add_stmt (build2 (OMP_ATOMIC, void_type_node, addr, rhs)); +} + + +/* Complete a #pragma omp flush construct. We don't do anything with the + variable list that the syntax allows. */ + +void +c_finish_omp_flush (void) +{ + tree x; + + x = built_in_decls[BUILT_IN_SYNCHRONIZE]; + x = build_function_call_expr (x, NULL); + add_stmt (x); +} + + +/* Check and canonicalize #pragma omp for increment expression. + Helper function for c_finish_omp_for. */ + +static tree +check_omp_for_incr_expr (tree exp, tree decl) +{ + tree t; + + if (!INTEGRAL_TYPE_P (TREE_TYPE (exp)) + || TYPE_PRECISION (TREE_TYPE (exp)) < TYPE_PRECISION (TREE_TYPE (decl))) + return error_mark_node; + + if (exp == decl) + return build_int_cst (TREE_TYPE (exp), 0); + + switch (TREE_CODE (exp)) + { + case NOP_EXPR: + t = check_omp_for_incr_expr (TREE_OPERAND (exp, 0), decl); + if (t != error_mark_node) + return fold_convert (TREE_TYPE (exp), t); + break; + case MINUS_EXPR: + t = check_omp_for_incr_expr (TREE_OPERAND (exp, 0), decl); + if (t != error_mark_node) + return fold_build2 (MINUS_EXPR, TREE_TYPE (exp), t, TREE_OPERAND (exp, 1)); + break; + case PLUS_EXPR: + t = check_omp_for_incr_expr (TREE_OPERAND (exp, 0), decl); + if (t != error_mark_node) + return fold_build2 (PLUS_EXPR, TREE_TYPE (exp), t, TREE_OPERAND (exp, 1)); + t = check_omp_for_incr_expr (TREE_OPERAND (exp, 1), decl); + if (t != error_mark_node) + return fold_build2 (PLUS_EXPR, TREE_TYPE (exp), TREE_OPERAND (exp, 0), t); + break; + default: + break; + } + + return error_mark_node; +} + +/* Validate and emit code for the OpenMP directive #pragma omp for. + INIT, COND, INCR, BODY and PRE_BODY are the five basic elements + of the loop (initialization expression, controlling predicate, increment + expression, body of the loop and statements to go before the loop). + DECL is the iteration variable. */ + +tree +c_finish_omp_for (location_t locus, tree decl, tree init, tree cond, + tree incr, tree body, tree pre_body) +{ + location_t elocus = locus; + bool fail = false; + + if (EXPR_HAS_LOCATION (init)) + elocus = EXPR_LOCATION (init); + + /* Validate the iteration variable. */ + if (!INTEGRAL_TYPE_P (TREE_TYPE (decl))) + { + error ("%Hinvalid type for iteration variable %qE", &elocus, decl); + fail = true; + } + if (TYPE_UNSIGNED (TREE_TYPE (decl))) + warning (0, "%Hiteration variable %qE is unsigned", &elocus, decl); + + /* In the case of "for (int i = 0...)", init will be a decl. It should + have a DECL_INITIAL that we can turn into an assignment. */ + if (init == decl) + { + elocus = DECL_SOURCE_LOCATION (decl); + + init = DECL_INITIAL (decl); + if (init == NULL) + { + error ("%H%qE is not initialized", &elocus, decl); + init = integer_zero_node; + fail = true; + } + + init = build_modify_expr (decl, NOP_EXPR, init); + SET_EXPR_LOCATION (init, elocus); + } + gcc_assert (TREE_CODE (init) == MODIFY_EXPR); + gcc_assert (TREE_OPERAND (init, 0) == decl); + + if (cond == NULL_TREE) + { + error ("%Hmissing controlling predicate", &elocus); + fail = true; + } + else + { + bool cond_ok = false; + + if (EXPR_HAS_LOCATION (cond)) + elocus = EXPR_LOCATION (cond); + + if (TREE_CODE (cond) == LT_EXPR + || TREE_CODE (cond) == LE_EXPR + || TREE_CODE (cond) == GT_EXPR + || TREE_CODE (cond) == GE_EXPR) + { + tree op0 = TREE_OPERAND (cond, 0); + tree op1 = TREE_OPERAND (cond, 1); + + /* 2.5.1. The comparison in the condition is computed in the type + of DECL, otherwise the behavior is undefined. + + For example: + long n; int i; + i < n; + + according to ISO will be evaluated as: + (long)i < n; + + We want to force: + i < (int)n; */ + if (TREE_CODE (op0) == NOP_EXPR + && decl == TREE_OPERAND (op0, 0)) + { + TREE_OPERAND (cond, 0) = TREE_OPERAND (op0, 0); + TREE_OPERAND (cond, 1) = fold_build1 (NOP_EXPR, TREE_TYPE (decl), + TREE_OPERAND (cond, 1)); + } + else if (TREE_CODE (op1) == NOP_EXPR + && decl == TREE_OPERAND (op1, 0)) + { + TREE_OPERAND (cond, 1) = TREE_OPERAND (op1, 0); + TREE_OPERAND (cond, 0) = fold_build1 (NOP_EXPR, TREE_TYPE (decl), + TREE_OPERAND (cond, 0)); + } + + if (decl == TREE_OPERAND (cond, 0)) + cond_ok = true; + else if (decl == TREE_OPERAND (cond, 1)) + { + TREE_SET_CODE (cond, swap_tree_comparison (TREE_CODE (cond))); + TREE_OPERAND (cond, 1) = TREE_OPERAND (cond, 0); + TREE_OPERAND (cond, 0) = decl; + cond_ok = true; + } + } + + if (!cond_ok) + { + error ("%Hinvalid controlling predicate", &elocus); + fail = true; + } + } + + if (incr == NULL_TREE) + { + error ("%Hmissing increment expression", &elocus); + fail = true; + } + else + { + bool incr_ok = false; + + if (EXPR_HAS_LOCATION (incr)) + elocus = EXPR_LOCATION (incr); + + /* Check all the valid increment expressions: v++, v--, ++v, --v, + v = v + incr, v = incr + v and v = v - incr. */ + switch (TREE_CODE (incr)) + { + case POSTINCREMENT_EXPR: + case PREINCREMENT_EXPR: + case POSTDECREMENT_EXPR: + case PREDECREMENT_EXPR: + incr_ok = (TREE_OPERAND (incr, 0) == decl); + break; + + case MODIFY_EXPR: + if (TREE_OPERAND (incr, 0) != decl) + break; + if (TREE_OPERAND (incr, 1) == decl) + break; + if (TREE_CODE (TREE_OPERAND (incr, 1)) == PLUS_EXPR + && (TREE_OPERAND (TREE_OPERAND (incr, 1), 0) == decl + || TREE_OPERAND (TREE_OPERAND (incr, 1), 1) == decl)) + incr_ok = true; + else if (TREE_CODE (TREE_OPERAND (incr, 1)) == MINUS_EXPR + && TREE_OPERAND (TREE_OPERAND (incr, 1), 0) == decl) + incr_ok = true; + else + { + tree t = check_omp_for_incr_expr (TREE_OPERAND (incr, 1), decl); + if (t != error_mark_node) + { + incr_ok = true; + t = build2 (PLUS_EXPR, TREE_TYPE (decl), decl, t); + incr = build2 (MODIFY_EXPR, void_type_node, decl, t); + } + } + break; + + default: + break; + } + if (!incr_ok) + { + error ("%Hinvalid increment expression", &elocus); + fail = true; + } + } + + if (fail) + return NULL; + else + { + tree t = make_node (OMP_FOR); + + TREE_TYPE (t) = void_type_node; + OMP_FOR_INIT (t) = init; + OMP_FOR_COND (t) = cond; + OMP_FOR_INCR (t) = incr; + OMP_FOR_BODY (t) = body; + OMP_FOR_PRE_BODY (t) = pre_body; + + SET_EXPR_LOCATION (t, locus); + return add_stmt (t); + } +} + + +/* Divide CLAUSES into two lists: those that apply to a parallel construct, + and those that apply to a work-sharing construct. Place the results in + *PAR_CLAUSES and *WS_CLAUSES respectively. In addition, add a nowait + clause to the work-sharing list. */ + +void +c_split_parallel_clauses (tree clauses, tree *par_clauses, tree *ws_clauses) +{ + tree next; + + *par_clauses = NULL; + *ws_clauses = make_node (OMP_CLAUSE_NOWAIT); + + for (; clauses ; clauses = next) + { + next = OMP_CLAUSE_CHAIN (clauses); + + switch (TREE_CODE (clauses)) + { + case OMP_CLAUSE_PRIVATE: + case OMP_CLAUSE_SHARED: + case OMP_CLAUSE_FIRSTPRIVATE: + case OMP_CLAUSE_LASTPRIVATE: + case OMP_CLAUSE_REDUCTION: + case OMP_CLAUSE_COPYIN: + case OMP_CLAUSE_IF: + case OMP_CLAUSE_NUM_THREADS: + case OMP_CLAUSE_DEFAULT: + OMP_CLAUSE_CHAIN (clauses) = *par_clauses; + *par_clauses = clauses; + break; + + case OMP_CLAUSE_SCHEDULE: + case OMP_CLAUSE_ORDERED: + OMP_CLAUSE_CHAIN (clauses) = *ws_clauses; + *ws_clauses = clauses; + break; + + default: + gcc_unreachable (); + } + } +} + +/* True if OpenMP sharing attribute of DECL is predetermined. */ + +enum omp_clause_default_kind +c_omp_predetermined_sharing (tree decl) +{ + /* Variables with const-qualified type having no mutable member + are predetermined shared. */ + if (TREE_READONLY (decl)) + return OMP_CLAUSE_DEFAULT_SHARED; + + return OMP_CLAUSE_DEFAULT_UNSPECIFIED; +} diff --git a/gcc/c-parser.c b/gcc/c-parser.c index 522f2d2e4a5..e594d1fbd34 100644 --- a/gcc/c-parser.c +++ b/gcc/c-parser.c @@ -200,6 +200,26 @@ static const struct resword reswords[] = }; #define N_reswords (sizeof reswords / sizeof (struct resword)) +/* All OpenMP clauses. OpenMP 2.5. */ +typedef enum pragma_omp_clause { + PRAGMA_OMP_CLAUSE_NONE = 0, + + PRAGMA_OMP_CLAUSE_COPYIN, + PRAGMA_OMP_CLAUSE_COPYPRIVATE, + PRAGMA_OMP_CLAUSE_DEFAULT, + PRAGMA_OMP_CLAUSE_FIRSTPRIVATE, + PRAGMA_OMP_CLAUSE_IF, + PRAGMA_OMP_CLAUSE_LASTPRIVATE, + PRAGMA_OMP_CLAUSE_NOWAIT, + PRAGMA_OMP_CLAUSE_NUM_THREADS, + PRAGMA_OMP_CLAUSE_ORDERED, + PRAGMA_OMP_CLAUSE_PRIVATE, + PRAGMA_OMP_CLAUSE_REDUCTION, + PRAGMA_OMP_CLAUSE_SCHEDULE, + PRAGMA_OMP_CLAUSE_SHARED +} pragma_omp_clause; + + /* Initialization routine for this file. */ void @@ -981,6 +1001,10 @@ static struct c_expr c_parser_postfix_expression_after_primary (c_parser *, static struct c_expr c_parser_expression (c_parser *); static struct c_expr c_parser_expression_conv (c_parser *); static tree c_parser_expr_list (c_parser *, bool); +static void c_parser_omp_construct (c_parser *); +static void c_parser_omp_threadprivate (c_parser *); +static void c_parser_omp_barrier (c_parser *); +static void c_parser_omp_flush (c_parser *); enum pragma_context { pragma_external, pragma_stmt, pragma_compound }; static bool c_parser_pragma (c_parser *, enum pragma_context); @@ -1189,7 +1213,12 @@ c_parser_external_declaration (c_parser *parser) absence is diagnosed through the diagnosis of implicit int. In GNU C we also allow but diagnose declarations without declaration specifiers, but only at top level (elsewhere they conflict with - other syntax). */ + other syntax). + + OpenMP: + + declaration: + threadprivate-directive */ static void c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, bool empty_ok, @@ -3256,7 +3285,16 @@ c_parser_initval (c_parser *parser, struct c_expr *after) prefix attributes on the declaration. ??? The syntax follows the old parser in requiring something after label declarations. Although they are erroneous if the labels declared aren't defined, - is it useful for the syntax to be this way? */ + is it useful for the syntax to be this way? + + OpenMP: + + block-item: + openmp-directive + + openmp-directive: + barrier-directive + flush-directive */ static tree c_parser_compound_statement (c_parser *parser) @@ -3527,7 +3565,53 @@ c_parser_label (c_parser *parser) objc-throw-statement: @throw expression ; @throw ; -*/ + + OpenMP: + + statement: + openmp-construct + + openmp-construct: + parallel-construct + for-construct + sections-construct + single-construct + parallel-for-construct + parallel-sections-construct + master-construct + critical-construct + atomic-construct + ordered-construct + + parallel-construct: + parallel-directive structured-block + + for-construct: + for-directive iteration-statement + + sections-construct: + sections-directive section-scope + + single-construct: + single-directive structured-block + + parallel-for-construct: + parallel-for-directive iteration-statement + + parallel-sections-construct: + parallel-sections-directive section-scope + + master-construct: + master-directive structured-block + + critical-construct: + critical-directive structured-block + + atomic-construct: + atomic-directive expression-statement + + ordered-construct: + ordered-directive structured-block */ static void c_parser_statement (c_parser *parser) @@ -6344,12 +6428,13 @@ c_parser_objc_keywordexpr (c_parser *parser) } -/* Handle pragmas. ALLOW_STMT is true if we're within the context of - a function and such pragmas are to be allowed. Returns true if we - actually parsed such a pragma. */ +/* Handle pragmas. Some OpenMP pragmas are associated with, and therefore + should be considered, statements. ALLOW_STMT is true if we're within + the context of a function and such pragmas are to be allowed. Returns + true if we actually parsed such a pragma. */ static bool -c_parser_pragma (c_parser *parser, enum pragma_context context ATTRIBUTE_UNUSED) +c_parser_pragma (c_parser *parser, enum pragma_context context) { unsigned int id; @@ -6358,13 +6443,56 @@ c_parser_pragma (c_parser *parser, enum pragma_context context ATTRIBUTE_UNUSED) switch (id) { + case PRAGMA_OMP_BARRIER: + if (context != pragma_compound) + { + if (context == pragma_stmt) + c_parser_error (parser, "%<#pragma omp barrier%> may only be " + "used in compound statements"); + goto bad_stmt; + } + c_parser_omp_barrier (parser); + return false; + + case PRAGMA_OMP_FLUSH: + if (context != pragma_compound) + { + if (context == pragma_stmt) + c_parser_error (parser, "%<#pragma omp flush%> may only be " + "used in compound statements"); + goto bad_stmt; + } + c_parser_omp_flush (parser); + return false; + + case PRAGMA_OMP_THREADPRIVATE: + c_parser_omp_threadprivate (parser); + return false; + + case PRAGMA_OMP_SECTION: + error ("%<#pragma omp section%> may only be used in " + "%<#pragma omp sections%> construct"); + c_parser_skip_until_found (parser, CPP_PRAGMA_EOL, NULL); + return false; + case PRAGMA_GCC_PCH_PREPROCESS: c_parser_error (parser, "%<#pragma GCC pch_preprocess%> must be first"); c_parser_skip_until_found (parser, CPP_PRAGMA_EOL, NULL); return false; default: - gcc_assert (id >= PRAGMA_FIRST_EXTERNAL); + if (id < PRAGMA_FIRST_EXTERNAL) + { + if (context == pragma_external) + { + bad_stmt: + c_parser_error (parser, "expected declaration specifiers"); + c_parser_skip_until_found (parser, CPP_PRAGMA_EOL, NULL); + return false; + } + c_parser_omp_construct (parser); + return true; + } break; } @@ -6420,6 +6548,1213 @@ c_parser_pragma_pch_preprocess (c_parser *parser) c_common_pch_pragma (parse_in, TREE_STRING_POINTER (name)); } +/* OpenMP 2.5 parsing routines. */ + +/* Returns name of the next clause. + If the clause is not recognized PRAGMA_OMP_CLAUSE_NONE is returned and + the token is not consumed. Otherwise appropriate pragma_omp_clause is + returned and the token is consumed. */ + +static pragma_omp_clause +c_parser_omp_clause_name (c_parser *parser) +{ + pragma_omp_clause result = PRAGMA_OMP_CLAUSE_NONE; + + if (c_parser_next_token_is_keyword (parser, RID_IF)) + result = PRAGMA_OMP_CLAUSE_IF; + else if (c_parser_next_token_is_keyword (parser, RID_DEFAULT)) + result = PRAGMA_OMP_CLAUSE_DEFAULT; + else if (c_parser_next_token_is (parser, CPP_NAME)) + { + const char *p = IDENTIFIER_POINTER (c_parser_peek_token (parser)->value); + + switch (p[0]) + { + case 'c': + if (!strcmp ("copyin", p)) + result = PRAGMA_OMP_CLAUSE_COPYIN; + else if (!strcmp ("copyprivate", p)) + result = PRAGMA_OMP_CLAUSE_COPYPRIVATE; + break; + case 'f': + if (!strcmp ("firstprivate", p)) + result = PRAGMA_OMP_CLAUSE_FIRSTPRIVATE; + break; + case 'l': + if (!strcmp ("lastprivate", p)) + result = PRAGMA_OMP_CLAUSE_LASTPRIVATE; + break; + case 'n': + if (!strcmp ("nowait", p)) + result = PRAGMA_OMP_CLAUSE_NOWAIT; + else if (!strcmp ("num_threads", p)) + result = PRAGMA_OMP_CLAUSE_NUM_THREADS; + break; + case 'o': + if (!strcmp ("ordered", p)) + result = PRAGMA_OMP_CLAUSE_ORDERED; + break; + case 'p': + if (!strcmp ("private", p)) + result = PRAGMA_OMP_CLAUSE_PRIVATE; + break; + case 'r': + if (!strcmp ("reduction", p)) + result = PRAGMA_OMP_CLAUSE_REDUCTION; + break; + case 's': + if (!strcmp ("schedule", p)) + result = PRAGMA_OMP_CLAUSE_SCHEDULE; + else if (!strcmp ("shared", p)) + result = PRAGMA_OMP_CLAUSE_SHARED; + break; + } + } + + if (result != PRAGMA_OMP_CLAUSE_NONE) + c_parser_consume_token (parser); + + return result; +} + +/* Validate that a clause of the given type does not already exist. */ + +static void +check_no_duplicate_clause (tree clauses, enum tree_code code, const char *name) +{ + tree c; + + for (c = clauses; c ; c = OMP_CLAUSE_CHAIN (c)) + if (TREE_CODE (c) == code) + { + error ("too many %qs clauses", name); + break; + } +} + +/* OpenMP 2.5: + variable-list: + identifier + variable-list , identifier + + If KIND is nonzero, create the appropriate node and install the decl + in OMP_CLAUSE_DECL and add the node to the head of the list. + + If KIND is zero, create a TREE_LIST with the decl in TREE_PURPOSE; + return the list created. */ + +static tree +c_parser_omp_variable_list (c_parser *parser, enum tree_code kind, tree list) +{ + if (c_parser_next_token_is_not (parser, CPP_NAME) + || c_parser_peek_token (parser)->id_kind != C_ID_ID) + c_parser_error (parser, "expected identifier"); + + while (c_parser_next_token_is (parser, CPP_NAME) + && c_parser_peek_token (parser)->id_kind == C_ID_ID) + { + tree t = lookup_name (c_parser_peek_token (parser)->value); + + if (t == NULL_TREE) + undeclared_variable (c_parser_peek_token (parser)->value, + c_parser_peek_token (parser)->location); + else if (t == error_mark_node) + ; + else if (kind != 0) + { + tree u = make_node (kind); + OMP_CLAUSE_DECL (u) = t; + OMP_CLAUSE_CHAIN (u) = list; + list = u; + } + else + list = tree_cons (t, NULL_TREE, list); + + c_parser_consume_token (parser); + + if (c_parser_next_token_is_not (parser, CPP_COMMA)) + break; + + c_parser_consume_token (parser); + } + + return list; +} + +/* Similarly, but expect leading and trailing parenthesis. This is a very + common case for omp clauses. */ + +static tree +c_parser_omp_var_list_parens (c_parser *parser, enum tree_code kind, tree list) +{ + if (c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) + { + list = c_parser_omp_variable_list (parser, kind, list); + c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>"); + } + return list; +} + +/* OpenMP 2.5: + copyin ( variable-list ) */ + +static tree +c_parser_omp_clause_copyin (c_parser *parser, tree list) +{ + return c_parser_omp_var_list_parens (parser, OMP_CLAUSE_COPYIN, list); +} + +/* OpenMP 2.5: + copyprivate ( variable-list ) */ + +static tree +c_parser_omp_clause_copyprivate (c_parser *parser, tree list) +{ + return c_parser_omp_var_list_parens (parser, OMP_CLAUSE_COPYPRIVATE, list); +} + +/* OpenMP 2.5: + default ( shared | none ) */ + +static tree +c_parser_omp_clause_default (c_parser *parser, tree list) +{ + enum omp_clause_default_kind kind = OMP_CLAUSE_DEFAULT_UNSPECIFIED; + tree c; + + if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) + return list; + if (c_parser_next_token_is (parser, CPP_NAME)) + { + const char *p = IDENTIFIER_POINTER (c_parser_peek_token (parser)->value); + + switch (p[0]) + { + case 'n': + if (strcmp ("none", p) != 0) + goto invalid_kind; + kind = OMP_CLAUSE_DEFAULT_NONE; + break; + + case 's': + if (strcmp ("shared", p) != 0) + goto invalid_kind; + kind = OMP_CLAUSE_DEFAULT_SHARED; + break; + + default: + goto invalid_kind; + } + + c_parser_consume_token (parser); + } + else + { + invalid_kind: + c_parser_error (parser, "expected %<none%> or %<shared%>"); + } + c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>"); + + if (kind == OMP_CLAUSE_DEFAULT_UNSPECIFIED) + return list; + + check_no_duplicate_clause (list, OMP_CLAUSE_DEFAULT, "default"); + c = make_node (OMP_CLAUSE_DEFAULT); + OMP_CLAUSE_CHAIN (c) = list; + OMP_CLAUSE_DEFAULT_KIND (c) = kind; + + return c; +} + +/* OpenMP 2.5: + firstprivate ( variable-list ) */ + +static tree +c_parser_omp_clause_firstprivate (c_parser *parser, tree list) +{ + return c_parser_omp_var_list_parens (parser, OMP_CLAUSE_FIRSTPRIVATE, list); +} + +/* OpenMP 2.5: + if ( expression ) */ + +static tree +c_parser_omp_clause_if (c_parser *parser, tree list) +{ + if (c_parser_next_token_is (parser, CPP_OPEN_PAREN)) + { + tree t = c_parser_paren_condition (parser); + tree c; + + check_no_duplicate_clause (list, OMP_CLAUSE_IF, "if"); + + c = make_node (OMP_CLAUSE_IF); + OMP_CLAUSE_IF_EXPR (c) = t; + OMP_CLAUSE_CHAIN (c) = list; + list = c; + } + else + c_parser_error (parser, "expected %<(%>"); + + return list; +} + +/* OpenMP 2.5: + lastprivate ( variable-list ) */ + +static tree +c_parser_omp_clause_lastprivate (c_parser *parser, tree list) +{ + return c_parser_omp_var_list_parens (parser, OMP_CLAUSE_LASTPRIVATE, list); +} + +/* OpenMP 2.5: + nowait */ + +static tree +c_parser_omp_clause_nowait (c_parser *parser ATTRIBUTE_UNUSED, tree list) +{ + tree c; + + check_no_duplicate_clause (list, OMP_CLAUSE_NOWAIT, "nowait"); + + c = make_node (OMP_CLAUSE_NOWAIT); + OMP_CLAUSE_CHAIN (c) = list; + return c; +} + +/* OpenMP 2.5: + num_threads ( expression ) */ + +static tree +c_parser_omp_clause_num_threads (c_parser *parser, tree list) +{ + if (c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) + { + tree c, t = c_parser_expression (parser).value; + + c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>"); + + if (!INTEGRAL_TYPE_P (TREE_TYPE (t))) + { + c_parser_error (parser, "expected integer expression"); + return list; + } + + /* Attempt to statically determine when the number isn't positive. */ + c = fold_build2 (LE_EXPR, boolean_type_node, t, + build_int_cst (TREE_TYPE (t), 0)); + if (c == boolean_true_node) + { + warning (0, "%<num_threads%> value must be positive"); + t = integer_one_node; + } + + check_no_duplicate_clause (list, OMP_CLAUSE_NUM_THREADS, "num_threads"); + + c = make_node (OMP_CLAUSE_NUM_THREADS); + OMP_CLAUSE_NUM_THREADS_EXPR (c) = t; + OMP_CLAUSE_CHAIN (c) = list; + list = c; + } + + return list; +} + +/* OpenMP 2.5: + ordered */ + +static tree +c_parser_omp_clause_ordered (c_parser *parser ATTRIBUTE_UNUSED, tree list) +{ + tree c; + + check_no_duplicate_clause (list, OMP_CLAUSE_ORDERED, "ordered"); + + c = make_node (OMP_CLAUSE_ORDERED); + OMP_CLAUSE_CHAIN (c) = list; + return c; +} + +/* OpenMP 2.5: + private ( variable-list ) */ + +static tree +c_parser_omp_clause_private (c_parser *parser, tree list) +{ + return c_parser_omp_var_list_parens (parser, OMP_CLAUSE_PRIVATE, list); +} + +/* OpenMP 2.5: + reduction ( reduction-operator : variable-list ) + + reduction-operator: + One of: + * - & ^ | && || */ + +static tree +c_parser_omp_clause_reduction (c_parser *parser, tree list) +{ + if (c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) + { + enum tree_code code; + + switch (c_parser_peek_token (parser)->type) + { + case CPP_PLUS: + code = PLUS_EXPR; + break; + case CPP_MULT: + code = MULT_EXPR; + break; + case CPP_MINUS: + code = MINUS_EXPR; + break; + case CPP_AND: + code = BIT_AND_EXPR; + break; + case CPP_XOR: + code = BIT_XOR_EXPR; + break; + case CPP_OR: + code = BIT_IOR_EXPR; + break; + case CPP_AND_AND: + code = TRUTH_ANDIF_EXPR; + break; + case CPP_OR_OR: + code = TRUTH_ORIF_EXPR; + break; + default: + c_parser_error (parser, + "expected %<+%>, %<*%>, %<-%>, %<&%>, " + "%<^%>, %<|%>, %<&&%>, or %<||%>"); + c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, 0); + return list; + } + c_parser_consume_token (parser); + if (c_parser_require (parser, CPP_COLON, "expected %<:%>")) + { + tree nl, c; + + nl = c_parser_omp_variable_list (parser, OMP_CLAUSE_REDUCTION, list); + for (c = nl; c != list; c = OMP_CLAUSE_CHAIN (c)) + OMP_CLAUSE_REDUCTION_CODE (c) = code; + + list = nl; + } + c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>"); + } + return list; +} + +/* OpenMP 2.5: + schedule ( schedule-kind ) + schedule ( schedule-kind , expression ) + + schedule-kind: + static | dynamic | guided | runtime +*/ + +static tree +c_parser_omp_clause_schedule (c_parser *parser, tree list) +{ + tree c, t; + + if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) + return list; + + c = make_node (OMP_CLAUSE_SCHEDULE); + + if (c_parser_next_token_is (parser, CPP_NAME)) + { + tree kind = c_parser_peek_token (parser)->value; + const char *p = IDENTIFIER_POINTER (kind); + + switch (p[0]) + { + case 'd': + if (strcmp ("dynamic", p) != 0) + goto invalid_kind; + OMP_CLAUSE_SCHEDULE_KIND (c) = OMP_CLAUSE_SCHEDULE_DYNAMIC; + break; + + case 'g': + if (strcmp ("guided", p) != 0) + goto invalid_kind; + OMP_CLAUSE_SCHEDULE_KIND (c) = OMP_CLAUSE_SCHEDULE_GUIDED; + break; + + case 'r': + if (strcmp ("runtime", p) != 0) + goto invalid_kind; + OMP_CLAUSE_SCHEDULE_KIND (c) = OMP_CLAUSE_SCHEDULE_RUNTIME; + break; + + default: + goto invalid_kind; + } + } + else if (c_parser_next_token_is_keyword (parser, RID_STATIC)) + OMP_CLAUSE_SCHEDULE_KIND (c) = OMP_CLAUSE_SCHEDULE_STATIC; + else + goto invalid_kind; + + c_parser_consume_token (parser); + if (c_parser_next_token_is (parser, CPP_COMMA)) + { + c_parser_consume_token (parser); + + t = c_parser_expr_no_commas (parser, NULL).value; + + if (OMP_CLAUSE_SCHEDULE_KIND (c) == OMP_CLAUSE_SCHEDULE_RUNTIME) + error ("schedule %<runtime%> does not take " + "a %<chunk_size%> parameter"); + else if (TREE_CODE (TREE_TYPE (t)) == INTEGER_TYPE) + OMP_CLAUSE_SCHEDULE_CHUNK_EXPR (c) = t; + else + c_parser_error (parser, "expected integer expression"); + + c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>"); + } + else + c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, + "expected %<,%> or %<)%>"); + + check_no_duplicate_clause (list, OMP_CLAUSE_SCHEDULE, "schedule"); + OMP_CLAUSE_CHAIN (c) = list; + return c; + + invalid_kind: + c_parser_error (parser, "invalid schedule kind"); + c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, 0); + return list; +} + +/* OpenMP 2.5: + shared ( variable-list ) */ + +static tree +c_parser_omp_clause_shared (c_parser *parser, tree list) +{ + return c_parser_omp_var_list_parens (parser, OMP_CLAUSE_SHARED, list); +} + +/* Parse all OpenMP clauses. The set clauses allowed by the directive + is a bitmask in MASK. Return the list of clauses found; the result + of clause default goes in *pdefault. */ + +static tree +c_parser_omp_all_clauses (c_parser *parser, unsigned int mask, + const char *where) +{ + tree clauses = NULL; + + while (c_parser_next_token_is_not (parser, CPP_PRAGMA_EOL)) + { + const pragma_omp_clause c_kind = c_parser_omp_clause_name (parser); + const char *c_name; + tree prev = clauses; + + switch (c_kind) + { + case PRAGMA_OMP_CLAUSE_COPYIN: + clauses = c_parser_omp_clause_copyin (parser, clauses); + c_name = "copyin"; + break; + case PRAGMA_OMP_CLAUSE_COPYPRIVATE: + clauses = c_parser_omp_clause_copyprivate (parser, clauses); + c_name = "copyprivate"; + break; + case PRAGMA_OMP_CLAUSE_DEFAULT: + clauses = c_parser_omp_clause_default (parser, clauses); + c_name = "default"; + break; + case PRAGMA_OMP_CLAUSE_FIRSTPRIVATE: + clauses = c_parser_omp_clause_firstprivate (parser, clauses); + c_name = "firstprivate"; + break; + case PRAGMA_OMP_CLAUSE_IF: + clauses = c_parser_omp_clause_if (parser, clauses); + c_name = "if"; + break; + case PRAGMA_OMP_CLAUSE_LASTPRIVATE: + clauses = c_parser_omp_clause_lastprivate (parser, clauses); + c_name = "lastprivate"; + break; + case PRAGMA_OMP_CLAUSE_NOWAIT: + clauses = c_parser_omp_clause_nowait (parser, clauses); + c_name = "nowait"; + break; + case PRAGMA_OMP_CLAUSE_NUM_THREADS: + clauses = c_parser_omp_clause_num_threads (parser, clauses); + c_name = "num_threads"; + break; + case PRAGMA_OMP_CLAUSE_ORDERED: + clauses = c_parser_omp_clause_ordered (parser, clauses); + c_name = "ordered"; + break; + case PRAGMA_OMP_CLAUSE_PRIVATE: + clauses = c_parser_omp_clause_private (parser, clauses); + c_name = "private"; + break; + case PRAGMA_OMP_CLAUSE_REDUCTION: + clauses = c_parser_omp_clause_reduction (parser, clauses); + c_name = "reduction"; + break; + case PRAGMA_OMP_CLAUSE_SCHEDULE: + clauses = c_parser_omp_clause_schedule (parser, clauses); + c_name = "schedule"; + break; + case PRAGMA_OMP_CLAUSE_SHARED: + clauses = c_parser_omp_clause_shared (parser, clauses); + c_name = "shared"; + break; + default: + c_parser_error (parser, "expected %<#pragma omp%> clause"); + goto saw_error; + } + + if (((mask >> c_kind) & 1) == 0 && !parser->error) + { + /* Remove the invalid clause(s) from the list to avoid + confusing the rest of the compiler. */ + clauses = prev; + error ("%qs is not valid for %qs", c_name, where); + } + } + + saw_error: + c_parser_skip_to_pragma_eol (parser); + + return c_finish_omp_clauses (clauses); +} + +/* OpenMP 2.5: + structured-block: + statement + + In practice, we're also interested in adding the statement to an + outer node. So it is convenient if we work around the fact that + c_parser_statement calls add_stmt. */ + +static tree +c_parser_omp_structured_block (c_parser *parser) +{ + tree stmt = push_stmt_list (); + c_parser_statement (parser); + return pop_stmt_list (stmt); +} + +/* OpenMP 2.5: + # pragma omp atomic new-line + expression-stmt + + expression-stmt: + x binop= expr | x++ | ++x | x-- | --x + binop: + +, *, -, /, &, ^, |, <<, >> + + where x is an lvalue expression with scalar type. */ + +static void +c_parser_omp_atomic (c_parser *parser) +{ + tree lhs, rhs; + enum tree_code code; + + c_parser_skip_to_pragma_eol (parser); + + lhs = c_parser_unary_expression (parser).value; + switch (TREE_CODE (lhs)) + { + case ERROR_MARK: + saw_error: + c_parser_skip_to_end_of_block_or_statement (parser); + return; + + case PREINCREMENT_EXPR: + case POSTINCREMENT_EXPR: + lhs = TREE_OPERAND (lhs, 0); + code = PLUS_EXPR; + rhs = integer_one_node; + break; + + case PREDECREMENT_EXPR: + case POSTDECREMENT_EXPR: + lhs = TREE_OPERAND (lhs, 0); + code = MINUS_EXPR; + rhs = integer_one_node; + break; + + default: + switch (c_parser_peek_token (parser)->type) + { + case CPP_MULT_EQ: + code = MULT_EXPR; + break; + case CPP_DIV_EQ: + code = TRUNC_DIV_EXPR; + break; + case CPP_PLUS_EQ: + code = PLUS_EXPR; + break; + case CPP_MINUS_EQ: + code = MINUS_EXPR; + break; + case CPP_LSHIFT_EQ: + code = LSHIFT_EXPR; + break; + case CPP_RSHIFT_EQ: + code = RSHIFT_EXPR; + break; + case CPP_AND_EQ: + code = BIT_AND_EXPR; + break; + case CPP_OR_EQ: + code = BIT_IOR_EXPR; + break; + case CPP_XOR_EQ: + code = BIT_XOR_EXPR; + break; + default: + c_parser_error (parser, + "invalid operator for %<#pragma omp atomic%>"); + goto saw_error; + } + + c_parser_consume_token (parser); + rhs = c_parser_expression (parser).value; + break; + } + c_finish_omp_atomic (code, lhs, rhs); + c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>"); +} + + +/* OpenMP 2.5: + # pragma omp barrier new-line +*/ + +static void +c_parser_omp_barrier (c_parser *parser) +{ + c_parser_consume_pragma (parser); + c_parser_skip_to_pragma_eol (parser); + + c_finish_omp_barrier (); +} + +/* OpenMP 2.5: + # pragma omp critical [(name)] new-line + structured-block +*/ + +static tree +c_parser_omp_critical (c_parser *parser) +{ + tree stmt, name = NULL; + + if (c_parser_next_token_is (parser, CPP_OPEN_PAREN)) + { + c_parser_consume_token (parser); + if (c_parser_next_token_is (parser, CPP_NAME)) + { + name = c_parser_peek_token (parser)->value; + c_parser_consume_token (parser); + c_parser_require (parser, CPP_CLOSE_PAREN, "expected %<)%>"); + } + else + c_parser_error (parser, "expected identifier"); + } + else if (c_parser_next_token_is_not (parser, CPP_PRAGMA_EOL)) + c_parser_error (parser, "expected %<(%> or end of line"); + c_parser_skip_to_pragma_eol (parser); + + stmt = c_parser_omp_structured_block (parser); + return c_finish_omp_critical (stmt, name); +} + +/* OpenMP 2.5: + # pragma omp flush flush-vars[opt] new-line + + flush-vars: + ( variable-list ) */ + +static void +c_parser_omp_flush (c_parser *parser) +{ + c_parser_consume_pragma (parser); + if (c_parser_next_token_is (parser, CPP_OPEN_PAREN)) + c_parser_omp_var_list_parens (parser, 0, NULL); + else if (c_parser_next_token_is_not (parser, CPP_PRAGMA_EOL)) + c_parser_error (parser, "expected %<(%> or end of line"); + c_parser_skip_to_pragma_eol (parser); + + c_finish_omp_flush (); +} + +/* Parse the restricted form of the for statment allowed by OpenMP. + The real trick here is to determine the loop control variable early + so that we can push a new decl if necessary to make it private. */ + +static tree +c_parser_omp_for_loop (c_parser *parser) +{ + tree decl, cond, incr, save_break, save_cont, body, init; + location_t loc; + + if (!c_parser_next_token_is_keyword (parser, RID_FOR)) + { + c_parser_error (parser, "for statement expected"); + return NULL; + } + loc = c_parser_peek_token (parser)->location; + c_parser_consume_token (parser); + + if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) + return NULL; + + /* Parse the initialization declaration or expression. */ + if (c_parser_next_token_starts_declspecs (parser)) + { + c_parser_declaration_or_fndef (parser, true, true, true, true); + decl = check_for_loop_decls (); + if (decl == NULL) + goto error_init; + init = decl; + } + else if (c_parser_next_token_is (parser, CPP_NAME) + && c_parser_peek_2nd_token (parser)->type == CPP_EQ) + { + decl = c_parser_postfix_expression (parser).value; + + c_parser_require (parser, CPP_EQ, "expected %<=%>"); + + init = c_parser_expr_no_commas (parser, NULL).value; + init = build_modify_expr (decl, NOP_EXPR, init); + init = c_process_expr_stmt (init); + + c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>"); + } + else + goto error_init; + + /* Parse the loop condition. */ + cond = NULL_TREE; + if (c_parser_next_token_is_not (parser, CPP_SEMICOLON)) + { + cond = c_parser_expression_conv (parser).value; + cond = c_objc_common_truthvalue_conversion (cond); + if (EXPR_P (cond)) + SET_EXPR_LOCATION (cond, input_location); + } + c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>"); + + /* Parse the increment expression. */ + incr = NULL_TREE; + if (c_parser_next_token_is_not (parser, CPP_CLOSE_PAREN)) + incr = c_process_expr_stmt (c_parser_expression (parser).value); + c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>"); + + parse_body: + save_break = c_break_label; + c_break_label = size_one_node; + save_cont = c_cont_label; + c_cont_label = NULL_TREE; + body = push_stmt_list (); + + add_stmt (c_parser_c99_block_statement (parser)); + if (c_cont_label) + add_stmt (build1 (LABEL_EXPR, void_type_node, c_cont_label)); + + body = pop_stmt_list (body); + c_break_label = save_break; + c_cont_label = save_cont; + + /* Only bother calling c_finish_omp_for if we havn't already generated + an error from the initialization parsing. */ + if (decl != NULL) + return c_finish_omp_for (loc, decl, init, cond, incr, body, NULL); + return NULL; + + error_init: + c_parser_error (parser, "expected iteration declaration or initialization"); + c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>"); + decl = init = cond = incr = NULL_TREE; + goto parse_body; +} + +/* OpenMP 2.5: + #pragma omp for for-clause[optseq] new-line + for-loop +*/ + +#define OMP_FOR_CLAUSE_MASK \ + ( (1u << PRAGMA_OMP_CLAUSE_PRIVATE) \ + | (1u << PRAGMA_OMP_CLAUSE_FIRSTPRIVATE) \ + | (1u << PRAGMA_OMP_CLAUSE_LASTPRIVATE) \ + | (1u << PRAGMA_OMP_CLAUSE_REDUCTION) \ + | (1u << PRAGMA_OMP_CLAUSE_ORDERED) \ + | (1u << PRAGMA_OMP_CLAUSE_SCHEDULE) \ + | (1u << PRAGMA_OMP_CLAUSE_NOWAIT)) + +static tree +c_parser_omp_for (c_parser *parser) +{ + tree block, clauses, ret; + + clauses = c_parser_omp_all_clauses (parser, OMP_FOR_CLAUSE_MASK, + "#pragma omp for"); + + block = c_begin_compound_stmt (true); + ret = c_parser_omp_for_loop (parser); + if (ret) + OMP_FOR_CLAUSES (ret) = clauses; + block = c_end_compound_stmt (block, true); + add_stmt (block); + + return ret; +} + +/* OpenMP 2.5: + # pragma omp master new-line + structured-block +*/ + +static tree +c_parser_omp_master (c_parser *parser) +{ + c_parser_skip_to_pragma_eol (parser); + return c_finish_omp_master (c_parser_omp_structured_block (parser)); +} + +/* OpenMP 2.5: + # pragma omp ordered new-line + structured-block +*/ + +static tree +c_parser_omp_ordered (c_parser *parser) +{ + c_parser_skip_to_pragma_eol (parser); + return c_finish_omp_ordered (c_parser_omp_structured_block (parser)); +} + +/* OpenMP 2.5: + + section-scope: + { section-sequence } + + section-sequence: + section-directive[opt] structured-block + section-sequence section-directive structured-block */ + +static tree +c_parser_omp_sections_scope (c_parser *parser) +{ + tree stmt, substmt; + bool error_suppress = false; + location_t loc; + + if (!c_parser_require (parser, CPP_OPEN_BRACE, "expected %<{%>")) + { + /* Avoid skipping until the end of the block. */ + parser->error = false; + return NULL_TREE; + } + + stmt = push_stmt_list (); + + loc = c_parser_peek_token (parser)->location; + if (c_parser_peek_token (parser)->pragma_kind != PRAGMA_OMP_SECTION) + { + substmt = push_stmt_list (); + + while (1) + { + c_parser_statement (parser); + + if (c_parser_peek_token (parser)->pragma_kind == PRAGMA_OMP_SECTION) + break; + if (c_parser_next_token_is (parser, CPP_CLOSE_BRACE)) + break; + if (c_parser_next_token_is (parser, CPP_EOF)) + break; + } + + substmt = pop_stmt_list (substmt); + substmt = build1 (OMP_SECTION, void_type_node, substmt); + SET_EXPR_LOCATION (substmt, loc); + add_stmt (substmt); + } + + while (1) + { + if (c_parser_next_token_is (parser, CPP_CLOSE_BRACE)) + break; + if (c_parser_next_token_is (parser, CPP_EOF)) + break; + + loc = c_parser_peek_token (parser)->location; + if (c_parser_peek_token (parser)->pragma_kind == PRAGMA_OMP_SECTION) + { + c_parser_consume_pragma (parser); + c_parser_skip_to_pragma_eol (parser); + error_suppress = false; + } + else if (!error_suppress) + { + error ("expected %<#pragma omp section%> or %<}%>"); + error_suppress = true; + } + + substmt = c_parser_omp_structured_block (parser); + substmt = build1 (OMP_SECTION, void_type_node, substmt); + SET_EXPR_LOCATION (substmt, loc); + add_stmt (substmt); + } + c_parser_skip_until_found (parser, CPP_CLOSE_BRACE, + "expected %<#pragma omp section%> or %<}%>"); + + substmt = pop_stmt_list (stmt); + + stmt = make_node (OMP_SECTIONS); + TREE_TYPE (stmt) = void_type_node; + OMP_SECTIONS_BODY (stmt) = substmt; + + return add_stmt (stmt); +} + +/* OpenMP 2.5: + # pragma omp sections sections-clause[optseq] newline + sections-scope +*/ + +#define OMP_SECTIONS_CLAUSE_MASK \ + ( (1u << PRAGMA_OMP_CLAUSE_PRIVATE) \ + | (1u << PRAGMA_OMP_CLAUSE_FIRSTPRIVATE) \ + | (1u << PRAGMA_OMP_CLAUSE_LASTPRIVATE) \ + | (1u << PRAGMA_OMP_CLAUSE_REDUCTION) \ + | (1u << PRAGMA_OMP_CLAUSE_NOWAIT)) + +static tree +c_parser_omp_sections (c_parser *parser) +{ + tree block, clauses, ret; + + clauses = c_parser_omp_all_clauses (parser, OMP_SECTIONS_CLAUSE_MASK, + "#pragma omp sections"); + + block = c_begin_compound_stmt (true); + ret = c_parser_omp_sections_scope (parser); + if (ret) + OMP_SECTIONS_CLAUSES (ret) = clauses; + block = c_end_compound_stmt (block, true); + add_stmt (block); + + return ret; +} + +/* OpenMP 2.5: + # pragma parallel parallel-clause new-line + # pragma parallel for parallel-for-clause new-line + # pragma parallel sections parallel-sections-clause new-line +*/ + +#define OMP_PARALLEL_CLAUSE_MASK \ + ( (1u << PRAGMA_OMP_CLAUSE_IF) \ + | (1u << PRAGMA_OMP_CLAUSE_PRIVATE) \ + | (1u << PRAGMA_OMP_CLAUSE_FIRSTPRIVATE) \ + | (1u << PRAGMA_OMP_CLAUSE_DEFAULT) \ + | (1u << PRAGMA_OMP_CLAUSE_SHARED) \ + | (1u << PRAGMA_OMP_CLAUSE_COPYIN) \ + | (1u << PRAGMA_OMP_CLAUSE_REDUCTION) \ + | (1u << PRAGMA_OMP_CLAUSE_NUM_THREADS)) + +static tree +c_parser_omp_parallel (c_parser *parser) +{ + enum pragma_kind p_kind = PRAGMA_OMP_PARALLEL; + const char *p_name = "#pragma omp parallel"; + tree stmt, clauses, par_clause, ws_clause, block; + unsigned int mask = OMP_PARALLEL_CLAUSE_MASK; + + if (c_parser_next_token_is_keyword (parser, RID_FOR)) + { + c_parser_consume_token (parser); + p_kind = PRAGMA_OMP_PARALLEL_FOR; + p_name = "#pragma omp parallel for"; + mask |= OMP_FOR_CLAUSE_MASK; + mask &= ~(1u << PRAGMA_OMP_CLAUSE_NOWAIT); + } + else if (c_parser_next_token_is (parser, CPP_NAME)) + { + const char *p = IDENTIFIER_POINTER (c_parser_peek_token (parser)->value); + if (strcmp (p, "sections") == 0) + { + c_parser_consume_token (parser); + p_kind = PRAGMA_OMP_PARALLEL_SECTIONS; + p_name = "#pragma omp parallel sections"; + mask |= OMP_SECTIONS_CLAUSE_MASK; + mask &= ~(1u << PRAGMA_OMP_CLAUSE_NOWAIT); + } + } + + clauses = c_parser_omp_all_clauses (parser, mask, p_name); + + switch (p_kind) + { + case PRAGMA_OMP_PARALLEL: + block = c_begin_omp_parallel (); + c_parser_statement (parser); + stmt = c_finish_omp_parallel (clauses, block); + break; + + case PRAGMA_OMP_PARALLEL_FOR: + block = c_begin_omp_parallel (); + c_split_parallel_clauses (clauses, &par_clause, &ws_clause); + stmt = c_parser_omp_for_loop (parser); + if (stmt) + OMP_FOR_CLAUSES (stmt) = ws_clause; + stmt = c_finish_omp_parallel (par_clause, block); + break; + + case PRAGMA_OMP_PARALLEL_SECTIONS: + block = c_begin_omp_parallel (); + c_split_parallel_clauses (clauses, &par_clause, &ws_clause); + stmt = c_parser_omp_sections_scope (parser); + if (stmt) + OMP_SECTIONS_CLAUSES (stmt) = ws_clause; + stmt = c_finish_omp_parallel (par_clause, block); + break; + + default: + gcc_unreachable (); + } + + return stmt; +} + +/* OpenMP 2.5: + # pragma omp single single-clause[optseq] new-line + structured-block +*/ + +#define OMP_SINGLE_CLAUSE_MASK \ + ( (1u << PRAGMA_OMP_CLAUSE_PRIVATE) \ + | (1u << PRAGMA_OMP_CLAUSE_FIRSTPRIVATE) \ + | (1u << PRAGMA_OMP_CLAUSE_COPYPRIVATE) \ + | (1u << PRAGMA_OMP_CLAUSE_NOWAIT)) + +static tree +c_parser_omp_single (c_parser *parser) +{ + tree stmt = make_node (OMP_SINGLE); + TREE_TYPE (stmt) = void_type_node; + + OMP_SINGLE_CLAUSES (stmt) + = c_parser_omp_all_clauses (parser, OMP_SINGLE_CLAUSE_MASK, + "#pragma omp single"); + OMP_SINGLE_BODY (stmt) = c_parser_omp_structured_block (parser); + + return add_stmt (stmt); +} + + +/* Main entry point to parsing most OpenMP pragmas. */ + +static void +c_parser_omp_construct (c_parser *parser) +{ + enum pragma_kind p_kind; + location_t loc; + tree stmt; + + loc = c_parser_peek_token (parser)->location; + p_kind = c_parser_peek_token (parser)->pragma_kind; + c_parser_consume_pragma (parser); + + switch (p_kind) + { + case PRAGMA_OMP_ATOMIC: + c_parser_omp_atomic (parser); + return; + case PRAGMA_OMP_CRITICAL: + stmt = c_parser_omp_critical (parser); + break; + case PRAGMA_OMP_FOR: + stmt = c_parser_omp_for (parser); + break; + case PRAGMA_OMP_MASTER: + stmt = c_parser_omp_master (parser); + break; + case PRAGMA_OMP_ORDERED: + stmt = c_parser_omp_ordered (parser); + break; + case PRAGMA_OMP_PARALLEL: + stmt = c_parser_omp_parallel (parser); + break; + case PRAGMA_OMP_SECTIONS: + stmt = c_parser_omp_sections (parser); + break; + case PRAGMA_OMP_SINGLE: + stmt = c_parser_omp_single (parser); + break; + default: + gcc_unreachable (); + } + + if (stmt) + SET_EXPR_LOCATION (stmt, loc); +} + + +/* OpenMP 2.5: + # pragma omp threadprivate (variable-list) */ + +static void +c_parser_omp_threadprivate (c_parser *parser) +{ + tree vars, t; + + c_parser_consume_pragma (parser); + vars = c_parser_omp_var_list_parens (parser, 0, NULL); + + if (!targetm.have_tls) + sorry ("threadprivate variables not supported in this target"); + + /* Mark every variable in VARS to be assigned thread local storage. */ + for (t = vars; t; t = TREE_CHAIN (t)) + { + tree v = TREE_PURPOSE (t); + + /* If V had already been marked threadprivate, it doesn't matter + whether it had been used prior to this point. */ + if (TREE_USED (v) && !C_DECL_THREADPRIVATE_P (v)) + error ("%qE declared %<threadprivate%> after first use", v); + else if (! TREE_STATIC (v) && ! DECL_EXTERNAL (v)) + error ("automatic variable %qE cannot be %<threadprivate%>", v); + else if (! COMPLETE_TYPE_P (TREE_TYPE (v))) + error ("%<threadprivate%> %qE has incomplete type", v); + else + { + if (! DECL_THREAD_LOCAL_P (v)) + { + DECL_TLS_MODEL (v) = decl_default_tls_model (v); + /* If rtl has been already set for this var, call + make_decl_rtl once again, so that encode_section_info + has a chance to look at the new decl flags. */ + if (DECL_RTL_SET_P (v)) + make_decl_rtl (v); + } + C_DECL_THREADPRIVATE_P (v) = 1; + } + } + + c_parser_skip_to_pragma_eol (parser); +} + + /* Parse a single source file. */ void diff --git a/gcc/c-pragma.c b/gcc/c-pragma.c index e2a46773a95..554e57f932b 100644 --- a/gcc/c-pragma.c +++ b/gcc/c-pragma.c @@ -724,6 +724,32 @@ c_invoke_pragma_handler (unsigned int id) void init_pragma (void) { + if (flag_openmp && !flag_preprocess_only) + { + struct omp_pragma_def { const char *name; unsigned int id; }; + static const struct omp_pragma_def omp_pragmas[] = { + { "atomic", PRAGMA_OMP_ATOMIC }, + { "barrier", PRAGMA_OMP_BARRIER }, + { "critical", PRAGMA_OMP_CRITICAL }, + { "flush", PRAGMA_OMP_FLUSH }, + { "for", PRAGMA_OMP_FOR }, + { "master", PRAGMA_OMP_MASTER }, + { "ordered", PRAGMA_OMP_ORDERED }, + { "parallel", PRAGMA_OMP_PARALLEL }, + { "section", PRAGMA_OMP_SECTION }, + { "sections", PRAGMA_OMP_SECTIONS }, + { "single", PRAGMA_OMP_SINGLE }, + { "threadprivate", PRAGMA_OMP_THREADPRIVATE } + }; + + const int n_omp_pragmas = sizeof (omp_pragmas) / sizeof (*omp_pragmas); + int i; + + for (i = 0; i < n_omp_pragmas; ++i) + cpp_register_deferred_pragma (parse_in, "omp", omp_pragmas[i].name, + omp_pragmas[i].id, true, true); + } + cpp_register_deferred_pragma (parse_in, "GCC", "pch_preprocess", PRAGMA_GCC_PCH_PREPROCESS, false, false); diff --git a/gcc/c-pragma.h b/gcc/c-pragma.h index 98765555ef4..28ef4e8f39b 100644 --- a/gcc/c-pragma.h +++ b/gcc/c-pragma.h @@ -29,6 +29,21 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA typedef enum pragma_kind { PRAGMA_NONE = 0, + PRAGMA_OMP_ATOMIC, + PRAGMA_OMP_BARRIER, + PRAGMA_OMP_CRITICAL, + PRAGMA_OMP_FLUSH, + PRAGMA_OMP_FOR, + PRAGMA_OMP_MASTER, + PRAGMA_OMP_ORDERED, + PRAGMA_OMP_PARALLEL, + PRAGMA_OMP_PARALLEL_FOR, + PRAGMA_OMP_PARALLEL_SECTIONS, + PRAGMA_OMP_SECTION, + PRAGMA_OMP_SECTIONS, + PRAGMA_OMP_SINGLE, + PRAGMA_OMP_THREADPRIVATE, + PRAGMA_GCC_PCH_PREPROCESS, PRAGMA_FIRST_EXTERNAL diff --git a/gcc/c-tree.h b/gcc/c-tree.h index db6c3828cc4..f67c4d20796 100644 --- a/gcc/c-tree.h +++ b/gcc/c-tree.h @@ -129,6 +129,10 @@ struct lang_type GTY(()) #define C_DECL_UNDEFINABLE_VM(EXP) \ DECL_LANG_FLAG_5 (LABEL_DECL_CHECK (EXP)) +/* Record whether a variable has been declared threadprivate by + #pragma omp threadprivate. */ +#define C_DECL_THREADPRIVATE_P(DECL) DECL_LANG_FLAG_3 (VAR_DECL_CHECK (DECL)) + /* Nonzero for a decl which either doesn't exist or isn't a prototype. N.B. Could be simplified if all built-in decls had complete prototypes (but this is presently difficult because some of them need FILE*). */ @@ -431,7 +435,6 @@ extern int global_bindings_p (void); extern void push_scope (void); extern tree pop_scope (void); extern void insert_block (tree); -extern tree pushdecl (tree); extern void c_expand_body (tree); extern void c_init_decl_processing (void); @@ -441,7 +444,7 @@ extern int quals_from_declspecs (const struct c_declspecs *); extern struct c_declarator *build_array_declarator (tree, struct c_declspecs *, bool, bool); extern tree build_enumerator (tree, tree); -extern void check_for_loop_decls (void); +extern tree check_for_loop_decls (void); extern void mark_forward_parm_decls (void); extern void declare_parm_level (void); extern void undeclared_variable (tree, location_t); @@ -461,7 +464,6 @@ extern void pending_xref_error (void); extern void c_push_function_context (struct function *); extern void c_pop_function_context (struct function *); extern void push_parm_decl (const struct c_parm *); -extern tree pushdecl_top_level (tree); extern struct c_declarator *set_array_declarator_inner (struct c_declarator *, struct c_declarator *, bool); @@ -529,7 +531,6 @@ extern tree default_conversion (tree); extern struct c_expr default_function_array_conversion (struct c_expr); extern tree composite_type (tree, tree); extern tree build_component_ref (tree, tree); -extern tree build_indirect_ref (tree, const char *); extern tree build_array_ref (tree, tree); extern tree build_external_ref (tree, int, location_t); extern void pop_maybe_used (bool); @@ -542,7 +543,6 @@ extern tree build_conditional_expr (tree, tree, tree); extern tree build_compound_expr (tree, tree); extern tree c_cast_expr (struct c_type_name *, tree); extern tree build_c_cast (tree, tree); -extern tree build_modify_expr (tree, enum tree_code, tree); extern void store_init_value (tree, tree); extern void error_init (const char *); extern void pedwarn_init (const char *); @@ -577,6 +577,9 @@ extern tree c_finish_goto_ptr (tree); extern void c_begin_vm_scope (unsigned int); extern void c_end_vm_scope (unsigned int); extern tree c_expr_to_decl (tree, bool *, bool *, bool *); +extern tree c_begin_omp_parallel (void); +extern tree c_finish_omp_parallel (tree, tree); +extern tree c_finish_omp_clauses (tree); /* Set to 0 at beginning of a function definition, set to 1 if a return statement that specifies a return value is seen. */ diff --git a/gcc/c-typeck.c b/gcc/c-typeck.c index e4428485dfe..06ffefd3028 100644 --- a/gcc/c-typeck.c +++ b/gcc/c-typeck.c @@ -2931,10 +2931,13 @@ build_unary_op (enum tree_code code, tree xarg, int flag) /* Report a read-only lvalue. */ if (TREE_READONLY (arg)) - readonly_error (arg, - ((code == PREINCREMENT_EXPR - || code == POSTINCREMENT_EXPR) - ? lv_increment : lv_decrement)); + { + readonly_error (arg, + ((code == PREINCREMENT_EXPR + || code == POSTINCREMENT_EXPR) + ? lv_increment : lv_decrement)); + return error_mark_node; + } if (TREE_CODE (TREE_TYPE (arg)) == BOOLEAN_TYPE) val = boolean_increment (code, arg); @@ -3645,7 +3648,10 @@ build_modify_expr (tree lhs, enum tree_code modifycode, tree rhs) || ((TREE_CODE (lhstype) == RECORD_TYPE || TREE_CODE (lhstype) == UNION_TYPE) && C_TYPE_FIELDS_READONLY (lhstype))) - readonly_error (lhs, lv_assign); + { + readonly_error (lhs, lv_assign); + return error_mark_node; + } /* If storing into a structure or union member, it has probably been given type `int'. @@ -7310,13 +7316,24 @@ c_finish_bc_stmt (tree *label_p, bool is_break) if (!skip) *label_p = label = create_artificial_label (); } - else if (TREE_CODE (label) != LABEL_DECL) + else if (TREE_CODE (label) == LABEL_DECL) + ; + else switch (TREE_INT_CST_LOW (label)) { + case 0: if (is_break) error ("break statement not within loop or switch"); else error ("continue statement not within a loop"); return NULL_TREE; + + case 1: + gcc_assert (is_break); + error ("break statement used with OpenMP for loop"); + return NULL_TREE; + + default: + gcc_unreachable (); } if (skip) @@ -8443,3 +8460,248 @@ c_expr_to_decl (tree expr, bool *tc ATTRIBUTE_UNUSED, else return expr; } + + +/* Like c_begin_compound_stmt, except force the retension of the BLOCK. */ + +tree +c_begin_omp_parallel (void) +{ + tree block; + + keep_next_level (); + block = c_begin_compound_stmt (true); + + return block; +} + +tree +c_finish_omp_parallel (tree clauses, tree block) +{ + tree stmt; + + block = c_end_compound_stmt (block, true); + + stmt = make_node (OMP_PARALLEL); + TREE_TYPE (stmt) = void_type_node; + OMP_PARALLEL_CLAUSES (stmt) = clauses; + OMP_PARALLEL_BODY (stmt) = block; + + return add_stmt (stmt); +} + +/* For all elements of CLAUSES, validate them vs OpenMP constraints. + Remove any elements from the list that are invalid. */ + +tree +c_finish_omp_clauses (tree clauses) +{ + bitmap_head generic_head, firstprivate_head, lastprivate_head; + tree c, t, *pc = &clauses; + const char *name; + + bitmap_obstack_initialize (NULL); + bitmap_initialize (&generic_head, &bitmap_default_obstack); + bitmap_initialize (&firstprivate_head, &bitmap_default_obstack); + bitmap_initialize (&lastprivate_head, &bitmap_default_obstack); + + for (pc = &clauses, c = clauses; c ; c = *pc) + { + bool remove = false; + bool need_complete = false; + bool need_implicitly_determined = false; + + switch (TREE_CODE (c)) + { + case OMP_CLAUSE_SHARED: + name = "shared"; + need_implicitly_determined = true; + goto check_dup_generic; + + case OMP_CLAUSE_PRIVATE: + name = "private"; + need_complete = true; + need_implicitly_determined = true; + goto check_dup_generic; + + case OMP_CLAUSE_REDUCTION: + name = "reduction"; + need_implicitly_determined = true; + t = OMP_CLAUSE_DECL (c); + if (AGGREGATE_TYPE_P (TREE_TYPE (t)) + || POINTER_TYPE_P (TREE_TYPE (t))) + { + error ("%qE has invalid type for %<reduction%>", t); + remove = true; + } + else if (FLOAT_TYPE_P (TREE_TYPE (t))) + { + enum tree_code r_code = OMP_CLAUSE_REDUCTION_CODE (c); + const char *r_name = NULL; + + switch (r_code) + { + case PLUS_EXPR: + case MULT_EXPR: + case MINUS_EXPR: + break; + case BIT_AND_EXPR: + r_name = "&"; + break; + case BIT_XOR_EXPR: + r_name = "^"; + break; + case BIT_IOR_EXPR: + r_name = "|"; + break; + case TRUTH_ANDIF_EXPR: + r_name = "&&"; + break; + case TRUTH_ORIF_EXPR: + r_name = "||"; + break; + default: + gcc_unreachable (); + } + if (r_name) + { + error ("%qE has invalid type for %<reduction(%s)%>", + t, r_name); + remove = true; + } + } + goto check_dup_generic; + + case OMP_CLAUSE_COPYPRIVATE: + name = "copyprivate"; + goto check_dup_generic; + + case OMP_CLAUSE_COPYIN: + name = "copyin"; + t = OMP_CLAUSE_DECL (c); + if (TREE_CODE (t) != VAR_DECL || !DECL_THREAD_LOCAL_P (t)) + { + error ("%qE must be %<threadprivate%> for %<copyin%>", t); + remove = true; + } + goto check_dup_generic; + + check_dup_generic: + t = OMP_CLAUSE_DECL (c); + if (TREE_CODE (t) != VAR_DECL && TREE_CODE (t) != PARM_DECL) + { + error ("%qE is not a variable in clause %qs", t, name); + remove = true; + } + else if (bitmap_bit_p (&generic_head, DECL_UID (t)) + || bitmap_bit_p (&firstprivate_head, DECL_UID (t)) + || bitmap_bit_p (&lastprivate_head, DECL_UID (t))) + { + error ("%qE appears more than once in data clauses", t); + remove = true; + } + else + bitmap_set_bit (&generic_head, DECL_UID (t)); + break; + + case OMP_CLAUSE_FIRSTPRIVATE: + name = "firstprivate"; + t = OMP_CLAUSE_DECL (c); + need_complete = true; + need_implicitly_determined = true; + if (TREE_CODE (t) != VAR_DECL && TREE_CODE (t) != PARM_DECL) + { + error ("%qE is not a variable in clause %<firstprivate%>", t); + remove = true; + } + else if (bitmap_bit_p (&generic_head, DECL_UID (t)) + || bitmap_bit_p (&firstprivate_head, DECL_UID (t))) + { + error ("%qE appears more than once in data clauses", t); + remove = true; + } + else + bitmap_set_bit (&firstprivate_head, DECL_UID (t)); + break; + + case OMP_CLAUSE_LASTPRIVATE: + name = "lastprivate"; + t = OMP_CLAUSE_DECL (c); + need_complete = true; + need_implicitly_determined = true; + if (TREE_CODE (t) != VAR_DECL && TREE_CODE (t) != PARM_DECL) + { + error ("%qE is not a variable in clause %<lastprivate%>", t); + remove = true; + } + else if (bitmap_bit_p (&generic_head, DECL_UID (t)) + || bitmap_bit_p (&lastprivate_head, DECL_UID (t))) + { + error ("%qE appears more than once in data clauses", t); + remove = true; + } + else + bitmap_set_bit (&lastprivate_head, DECL_UID (t)); + break; + + case OMP_CLAUSE_IF: + case OMP_CLAUSE_NUM_THREADS: + case OMP_CLAUSE_SCHEDULE: + case OMP_CLAUSE_NOWAIT: + case OMP_CLAUSE_ORDERED: + case OMP_CLAUSE_DEFAULT: + pc = &OMP_CLAUSE_CHAIN (c); + continue; + + default: + gcc_unreachable (); + } + + if (!remove) + { + t = OMP_CLAUSE_DECL (c); + + if (need_complete) + { + t = require_complete_type (t); + if (t == error_mark_node) + remove = true; + } + + if (need_implicitly_determined) + { + const char *share_name = NULL; + + if (TREE_CODE (t) == VAR_DECL && DECL_THREAD_LOCAL_P (t)) + share_name = "threadprivate"; + else switch (c_omp_predetermined_sharing (t)) + { + case OMP_CLAUSE_DEFAULT_UNSPECIFIED: + break; + case OMP_CLAUSE_DEFAULT_SHARED: + share_name = "shared"; + break; + case OMP_CLAUSE_DEFAULT_PRIVATE: + share_name = "private"; + break; + default: + gcc_unreachable (); + } + if (share_name) + { + error ("%qE is predetermined %qs for %qs", + t, share_name, name); + remove = true; + } + } + } + + if (remove) + *pc = OMP_CLAUSE_CHAIN (c); + else + pc = &OMP_CLAUSE_CHAIN (c); + } + + bitmap_obstack_release (NULL); + return clauses; +} diff --git a/gcc/c.opt b/gcc/c.opt index 1338b11d4b2..9d11ca6f052 100644 --- a/gcc/c.opt +++ b/gcc/c.opt @@ -617,6 +617,10 @@ fobjc-sjlj-exceptions ObjC ObjC++ Var(flag_objc_sjlj_exceptions) Init(-1) Enable Objective-C setjmp exception handling runtime +fopenmp +C ObjC C++ ObjC++ Var(flag_openmp) +Enable OpenMP + foperator-names C++ ObjC++ Recognize C++ kewords like \"compl\" and \"xor\" diff --git a/gcc/cgraph.c b/gcc/cgraph.c index 10a22334771..4e4add8dd04 100644 --- a/gcc/cgraph.c +++ b/gcc/cgraph.c @@ -113,6 +113,9 @@ struct cgraph_node *cgraph_nodes; /* Queue of cgraph nodes scheduled to be lowered. */ struct cgraph_node *cgraph_nodes_queue; +/* Queue of cgraph nodes scheduled to be analyzed. */ +struct cgraph_node *cgraph_analyze_queue; + /* Number of nodes in existence. */ int cgraph_n_nodes; @@ -1091,4 +1094,20 @@ cgraph_variable_initializer_availability (struct cgraph_varpool_node *node) return AVAIL_AVAILABLE; } + +/* Add the function FNDECL to the call graph. This assumes that the + body of FNDECL is in GENERIC form and ready to be processed by + cgraph_finalize_function. */ + +void +cgraph_add_new_function (tree fndecl) +{ + /* We're called while lowering another function. We can't do anything + at this time without recursing. Which would cause a GC at an + inappropriate time. */ + struct cgraph_node *n = cgraph_node (fndecl); + n->next_needed = cgraph_analyze_queue; + cgraph_analyze_queue = n; +} + #include "gt-cgraph.h" diff --git a/gcc/cgraph.h b/gcc/cgraph.h index 82cf2d9fe13..600b00e2193 100644 --- a/gcc/cgraph.h +++ b/gcc/cgraph.h @@ -239,6 +239,7 @@ extern GTY(()) int cgraph_max_uid; extern bool cgraph_global_info_ready; extern bool cgraph_function_flags_ready; extern GTY(()) struct cgraph_node *cgraph_nodes_queue; +extern GTY(()) struct cgraph_node *cgraph_analyze_queue; extern GTY(()) struct cgraph_varpool_node *cgraph_varpool_first_unanalyzed_node; extern GTY(()) struct cgraph_varpool_node *cgraph_varpool_nodes_queue; @@ -288,12 +289,12 @@ enum availability cgraph_function_body_availability (struct cgraph_node *); enum availability cgraph_variable_initializer_availability (struct cgraph_varpool_node *); bool cgraph_is_master_clone (struct cgraph_node *); struct cgraph_node *cgraph_master_clone (struct cgraph_node *); +void cgraph_add_new_function (tree); /* In cgraphunit.c */ bool cgraph_assemble_pending_functions (void); bool cgraph_varpool_assemble_pending_decls (void); void cgraph_finalize_function (tree, bool); -void cgraph_lower_function (struct cgraph_node *); void cgraph_finalize_compilation_unit (void); void cgraph_optimize (void); void cgraph_mark_needed_node (struct cgraph_node *); @@ -307,6 +308,7 @@ void cgraph_reset_static_var_maps (void); void init_cgraph (void); struct cgraph_node *cgraph_function_versioning (struct cgraph_node *, varray_type, varray_type); +void cgraph_analyze_function (struct cgraph_node *); struct cgraph_node *save_inline_function_body (struct cgraph_node *); /* In ipa.c */ diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c index ae9f690013e..995bcb9c5af 100644 --- a/gcc/cgraphunit.c +++ b/gcc/cgraphunit.c @@ -171,7 +171,6 @@ static void cgraph_expand_all_functions (void); static void cgraph_mark_functions_to_output (void); static void cgraph_expand_function (struct cgraph_node *); static tree record_reference (tree *, int *, void *); -static void cgraph_analyze_function (struct cgraph_node *node); /* Records tree nodes seen in record_reference. Simply using walk_tree_without_duplicates doesn't guarantee each node is visited @@ -410,6 +409,29 @@ cgraph_reset_node (struct cgraph_node *node) } } +static void +cgraph_lower_function (struct cgraph_node *node) +{ + if (node->lowered) + return; + tree_lowering_passes (node->decl); + node->lowered = true; +} + +static void +cgraph_finalize_pending_functions (void) +{ + struct cgraph_node *next, *node = cgraph_analyze_queue; + + cgraph_analyze_queue = NULL; + for (; node ; node = next) + { + next = node->next_needed; + node->next_needed = NULL; + cgraph_finalize_function (node->decl, true); + } +} + /* DECL has been parsed. Take it, queue it, compile it at the whim of the logic in effect. If NESTED is true, then our caller cannot stand to have the garbage collector run at the moment. We would need to either create @@ -436,6 +458,7 @@ cgraph_finalize_function (tree decl, bool nested) if (!flag_unit_at_a_time) { cgraph_analyze_function (node); + cgraph_finalize_pending_functions (); cgraph_decide_inlining_incrementally (node, false); } @@ -465,15 +488,6 @@ cgraph_finalize_function (tree decl, bool nested) do_warn_unused_parameter (decl); } -void -cgraph_lower_function (struct cgraph_node *node) -{ - if (node->lowered) - return; - tree_lowering_passes (node->decl); - node->lowered = true; -} - /* Walk tree and record all calls. Called via walk_tree. */ static tree record_reference (tree *tp, int *walk_subtrees, void *data) @@ -878,7 +892,7 @@ cgraph_output_pending_asms (void) } /* Analyze the function scheduled to be output. */ -static void +void cgraph_analyze_function (struct cgraph_node *node) { tree decl = node->decl; @@ -968,6 +982,7 @@ cgraph_finalize_compilation_unit (void) gcc_assert (DECL_SAVED_TREE (decl)); cgraph_analyze_function (node); + cgraph_finalize_pending_functions (); for (edge = node->callees; edge; edge = edge->next_callee) if (!edge->callee->reachable) diff --git a/gcc/config/darwin.h b/gcc/config/darwin.h index 70481bbea4c..ddd508e5bb1 100644 --- a/gcc/config/darwin.h +++ b/gcc/config/darwin.h @@ -834,4 +834,10 @@ void add_framework_path (char *); #define WINT_TYPE "int" +/* Every program on darwin links against libSystem which contains the pthread + routines, so there's no need to explicitly call out when doing threaded + work. */ +#undef GOMP_SELF_SPECS +#define GOMP_SELF_SPECS "" + #endif /* CONFIG_DARWIN_H */ diff --git a/gcc/diagnostic.h b/gcc/diagnostic.h index 3a3204bdcc3..242cf47b42f 100644 --- a/gcc/diagnostic.h +++ b/gcc/diagnostic.h @@ -203,5 +203,6 @@ extern void print_generic_decl (FILE *, tree, int); extern void debug_generic_expr (tree); extern void debug_generic_stmt (tree); +extern void debug_tree_chain (tree); extern void debug_c_tree (tree); #endif /* ! GCC_DIAGNOSTIC_H */ diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index c1c87f25089..629e4a9f4aa 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -765,7 +765,7 @@ See S/390 and zSeries Options. -fargument-noalias-global -fleading-underscore @gol -ftls-model=@var{model} @gol -ftrapv -fwrapv -fbounds-check @gol --fvisibility} +-fvisibility -fopenmp} @end table @menu @@ -4701,6 +4701,15 @@ instrumentation (and therefore faster execution) and still provides some protection against outright memory corrupting writes, but allows erroneously read data to propagate within a program. +@item -fopenmp +@opindex fopenmp +@cindex openmp parallel +Enable handling of OpenMP directives @code{#pragma omp} in C/C++ and +@code{!$omp} in Fortran. When @option{-fopenmp} is specified, the +compiler generates parallel code according to the OpenMP Application +Program Interface v2.5. To generate the final exectuable, the runtime +library @code{libgomp} must be linked in using @option{-lgomp}. + @item -fstrength-reduce @opindex fstrength-reduce Perform the optimizations of loop strength reduction and diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c index 572bfc9a5be..87d92e833e1 100644 --- a/gcc/dwarf2out.c +++ b/gcc/dwarf2out.c @@ -9015,7 +9015,6 @@ loc_descriptor_from_tree_1 (tree loc, int want_address) { dw_loc_descr_ref ret, ret1; int have_address = 0; - int unsignedp = TYPE_UNSIGNED (TREE_TYPE (loc)); enum dwarf_location_atom op; /* ??? Most of the time we do not take proper care for sign/zero @@ -9159,6 +9158,7 @@ loc_descriptor_from_tree_1 (tree loc, int want_address) HOST_WIDE_INT bitsize, bitpos, bytepos; enum machine_mode mode; int volatilep; + int unsignedp = TYPE_UNSIGNED (TREE_TYPE (loc)); obj = get_inner_reference (loc, &bitsize, &bitpos, &offset, &mode, &unsignedp, &volatilep, false); @@ -9257,7 +9257,7 @@ loc_descriptor_from_tree_1 (tree loc, int want_address) goto do_binop; case RSHIFT_EXPR: - op = (unsignedp ? DW_OP_shr : DW_OP_shra); + op = (TYPE_UNSIGNED (TREE_TYPE (loc)) ? DW_OP_shr : DW_OP_shra); goto do_binop; case PLUS_EXPR: diff --git a/gcc/gcc.c b/gcc/gcc.c index 392bac4b591..bff3ae8de4c 100644 --- a/gcc/gcc.c +++ b/gcc/gcc.c @@ -352,6 +352,7 @@ static const char *if_exists_spec_function (int, const char **); static const char *if_exists_else_spec_function (int, const char **); static const char *replace_outfile_spec_function (int, const char **); static const char *version_compare_spec_function (int, const char **); +static const char *include_spec_function (int, const char **); /* The Specs Language @@ -698,7 +699,8 @@ proper position among the other output files. */ %{!fsyntax-only:%{!c:%{!M:%{!MM:%{!E:%{!S:\ %(linker) %l " LINK_PIE_SPEC "%X %{o*} %{A} %{d} %{e*} %{m} %{N} %{n} %{r}\ %{s} %{t} %{u*} %{x} %{z} %{Z} %{!A:%{!nostdlib:%{!nostartfiles:%S}}}\ - %{static:} %{L*} %(mfwrap) %(link_libgcc) %o %(mflib)\ + %{static:} %{L*} %(mfwrap) %{fopenmp:%:include(libgomp.spec)%(link_gomp)}\ + %(link_libgcc) %o %(mflib)\ %{fprofile-arcs|fprofile-generate|coverage:-lgcov}\ %{!nostdlib:%{!nodefaultlibs:%(link_ssp) %(link_gcc_c_sequence)}}\ %{!A:%{!nostdlib:%{!nostartfiles:%E}}} %{T*} }}}}}}" @@ -737,6 +739,7 @@ static const char *link_spec = LINK_SPEC; static const char *lib_spec = LIB_SPEC; static const char *mfwrap_spec = MFWRAP_SPEC; static const char *mflib_spec = MFLIB_SPEC; +static const char *link_gomp_spec = ""; static const char *libgcc_spec = LIBGCC_SPEC; static const char *endfile_spec = ENDFILE_SPEC; static const char *startfile_spec = STARTFILE_SPEC; @@ -835,7 +838,15 @@ static const char *const multilib_defaults_raw[] = MULTILIB_DEFAULTS; #define DRIVER_SELF_SPECS "" #endif -static const char *const driver_self_specs[] = { DRIVER_SELF_SPECS }; +/* Adding -fopenmp should imply pthreads. This is particularly important + for targets that use different start files and suchlike. */ +#ifndef GOMP_SELF_SPECS +#define GOMP_SELF_SPECS "%{fopenmp: -pthread}" +#endif + +static const char *const driver_self_specs[] = { + DRIVER_SELF_SPECS, GOMP_SELF_SPECS +}; #ifndef OPTION_DEFAULT_SPECS #define OPTION_DEFAULT_SPECS { "", "" } @@ -1534,6 +1545,7 @@ static struct spec_list static_specs[] = INIT_STATIC_SPEC ("lib", &lib_spec), INIT_STATIC_SPEC ("mfwrap", &mfwrap_spec), INIT_STATIC_SPEC ("mflib", &mflib_spec), + INIT_STATIC_SPEC ("link_gomp", &link_gomp_spec), INIT_STATIC_SPEC ("libgcc", &libgcc_spec), INIT_STATIC_SPEC ("startfile", &startfile_spec), INIT_STATIC_SPEC ("switches_need_spaces", &switches_need_spaces), @@ -1581,6 +1593,7 @@ static const struct spec_function static_spec_functions[] = { "if-exists-else", if_exists_else_spec_function }, { "replace-outfile", replace_outfile_spec_function }, { "version-compare", version_compare_spec_function }, + { "include", include_spec_function }, { 0, 0 } }; @@ -3281,11 +3294,11 @@ process_command (int argc, const char **argv) } /* If there is a -V or -b option (or both), process it now, before - trying to interpret the rest of the command line. + trying to interpret the rest of the command line. Use heuristic that all configuration names must have at least one dash '-'. This allows us to pass options starting with -b. */ if (argc > 1 && argv[1][0] == '-' - && (argv[1][1] == 'V' || + && (argv[1][1] == 'V' || ((argv[1][1] == 'b') && (NULL != strchr(argv[1] + 2,'-'))))) { const char *new_version = DEFAULT_TARGET_VERSION; @@ -5518,10 +5531,10 @@ input_suffix_matches (const char *atom, const char *end_atom) && input_suffix[end_atom - atom] == '\0'); } -/* Inline subroutine of handle_braces. Returns true if a switch +/* Subroutine of handle_braces. Returns true if a switch matching the atom bracketed by ATOM and END_ATOM appeared on the command line. */ -static inline bool +static bool switch_matches (const char *atom, const char *end_atom, int starred) { int i; @@ -6458,7 +6471,7 @@ main (int argc, char **argv) if (combine_flag) combine_inputs = true; else - combine_inputs = false; + combine_inputs = false; for (i = 0; (int) i < n_infiles; i++) { @@ -6489,7 +6502,7 @@ main (int argc, char **argv) infiles[i].compiled = false; infiles[i].preprocessed = false; } - + if (!combine_inputs && have_c && have_o && lang_n_infiles > 1) fatal ("cannot specify -o with -c or -S with multiple files"); @@ -7780,3 +7793,22 @@ version_compare_spec_function (int argc, const char **argv) return argv[nargs + 2]; } + +/* %:include builtin spec function. This differs from %include in that it + can be nested inside a spec, and thus be conditionalized. It takes + one argument, the filename, and looks for it in the startfile path. + The result is always NULL, i.e. an empty expansion. */ + +static const char * +include_spec_function (int argc, const char **argv) +{ + char *file; + + if (argc != 1) + abort (); + + file = find_a_file (&startfile_prefixes, argv[0], R_OK, 0); + read_specs (file ? file : argv[0], FALSE); + + return NULL; +} diff --git a/gcc/gimple-low.c b/gcc/gimple-low.c index de0f6f24ead..2a49eedfd6b 100644 --- a/gcc/gimple-low.c +++ b/gcc/gimple-low.c @@ -69,13 +69,12 @@ lower_function_body (void) gcc_assert (TREE_CODE (bind) == BIND_EXPR); + memset (&data, 0, sizeof (data)); data.block = DECL_INITIAL (current_function_decl); BLOCK_SUBBLOCKS (data.block) = NULL_TREE; BLOCK_CHAIN (data.block) = NULL_TREE; TREE_ASM_WRITTEN (data.block) = 1; - data.return_statements = NULL_TREE; - *body_p = alloc_stmt_list (); i = tsi_start (*body_p); tsi_link_after (&i, bind, TSI_NEW_STMT); @@ -196,11 +195,6 @@ lower_stmt (tree_stmt_iterator *tsi, struct lower_data *data) break; default: -#ifdef ENABLE_CHECKING - print_node_brief (stderr, "", stmt, 0); - internal_error ("unexpected node"); -#endif - case COMPOUND_EXPR: gcc_unreachable (); } diff --git a/gcc/gimplify.c b/gcc/gimplify.c index 8783dc65df8..acd0468a338 100644 --- a/gcc/gimplify.c +++ b/gcc/gimplify.c @@ -46,21 +46,67 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "ggc.h" #include "toplev.h" #include "target.h" +#include "optabs.h" +#include "pointer-set.h" -static struct gimplify_ctx + +enum gimplify_omp_var_data +{ + GOVD_SEEN = 1, + GOVD_EXPLICIT = 2, + GOVD_SHARED = 4, + GOVD_PRIVATE = 8, + GOVD_FIRSTPRIVATE = 16, + GOVD_LASTPRIVATE = 32, + GOVD_REDUCTION = 64, + GOVD_LOCAL = 128, + GOVD_DEBUG_PRIVATE = 256, + GOVD_DATA_SHARE_CLASS = (GOVD_SHARED | GOVD_PRIVATE | GOVD_FIRSTPRIVATE + | GOVD_LASTPRIVATE | GOVD_REDUCTION | GOVD_LOCAL) +}; + +struct gimplify_omp_ctx { + struct gimplify_omp_ctx *outer_context; + splay_tree variables; + struct pointer_set_t *privatized_types; + location_t location; + enum omp_clause_default_kind default_kind; + bool is_parallel; +}; + +struct gimplify_ctx +{ + struct gimplify_ctx *prev_context; + tree current_bind_expr; tree temps; tree conditional_cleanups; tree exit_label; tree return_temp; + VEC(tree,heap) *case_labels; /* The formal temporary table. Should this be persistent? */ htab_t temp_htab; + int conditions; bool save_stack; bool into_ssa; -} *gimplify_ctxp; + + /* When gimplifying combined omp parallel directives (omp parallel + loop and omp parallel sections), any prefix code needed to setup + the associated worksharing construct needs to be emitted in the + pre-queue of its parent parallel, otherwise the lowering process + will move that code to the child function. Similarly, we need to + move up to the gimplification context of the parent parallel + directive so temporaries are declared in the right context. */ + tree *combined_pre_p; + struct gimplify_ctx *combined_ctxp; +}; + +static struct gimplify_ctx *gimplify_ctxp; +static struct gimplify_omp_ctx *gimplify_omp_ctxp; + /* Formal (expression) temporary table handling: Multiple occurrences of @@ -116,14 +162,14 @@ gimple_tree_eq (const void *p1, const void *p2) void push_gimplify_context (void) { - gcc_assert (!gimplify_ctxp); - gimplify_ctxp - = (struct gimplify_ctx *) xcalloc (1, sizeof (struct gimplify_ctx)); + struct gimplify_ctx *c; + + c = (struct gimplify_ctx *) xcalloc (1, sizeof (struct gimplify_ctx)); + c->prev_context = gimplify_ctxp; if (optimize) - gimplify_ctxp->temp_htab - = htab_create (1000, gimple_tree_hash, gimple_tree_eq, free); - else - gimplify_ctxp->temp_htab = NULL; + c->temp_htab = htab_create (1000, gimple_tree_hash, gimple_tree_eq, free); + + gimplify_ctxp = c; } /* Tear down a context for the gimplifier. If BODY is non-null, then @@ -133,28 +179,23 @@ push_gimplify_context (void) void pop_gimplify_context (tree body) { + struct gimplify_ctx *c = gimplify_ctxp; tree t; - gcc_assert (gimplify_ctxp && !gimplify_ctxp->current_bind_expr); + gcc_assert (c && !c->current_bind_expr); + gimplify_ctxp = c->prev_context; - for (t = gimplify_ctxp->temps; t ; t = TREE_CHAIN (t)) + for (t = c->temps; t ; t = TREE_CHAIN (t)) DECL_GIMPLE_FORMAL_TEMP_P (t) = 0; if (body) - declare_tmp_vars (gimplify_ctxp->temps, body); + declare_tmp_vars (c->temps, body); else - record_vars (gimplify_ctxp->temps); - -#if 0 - if (!quiet_flag && optimize) - fprintf (stderr, " collisions: %f ", - htab_collisions (gimplify_ctxp->temp_htab)); -#endif + record_vars (c->temps); if (optimize) - htab_delete (gimplify_ctxp->temp_htab); - free (gimplify_ctxp); - gimplify_ctxp = NULL; + htab_delete (c->temp_htab); + free (c); } static void @@ -214,6 +255,48 @@ gimple_pop_condition (tree *pre_p) } } +/* A stable comparison routine for use with splay trees and DECLs. */ + +static int +splay_tree_compare_decl_uid (splay_tree_key xa, splay_tree_key xb) +{ + tree a = (tree) xa; + tree b = (tree) xb; + + return DECL_UID (a) - DECL_UID (b); +} + +/* Create a new omp construct that deals with variable remapping. */ + +static struct gimplify_omp_ctx * +new_omp_context (bool is_parallel) +{ + struct gimplify_omp_ctx *c; + + c = XCNEW (struct gimplify_omp_ctx); + c->outer_context = gimplify_omp_ctxp; + c->variables = splay_tree_new (splay_tree_compare_decl_uid, 0, 0); + c->privatized_types = pointer_set_create (); + c->location = input_location; + c->is_parallel = is_parallel; + c->default_kind = OMP_CLAUSE_DEFAULT_SHARED; + + return c; +} + +/* Destroy an omp construct that deals with variable remapping. */ + +static void +delete_omp_context (struct gimplify_omp_ctx *c) +{ + splay_tree_delete (c->variables); + pointer_set_destroy (c->privatized_types); + XDELETE (c); +} + +static void omp_add_variable (struct gimplify_omp_ctx *, tree, unsigned int); +static bool omp_notice_variable (struct gimplify_omp_ctx *, tree, bool); + /* A subroutine of append_to_statement_list{,_force}. T is not NULL. */ static void @@ -601,6 +684,16 @@ gimple_add_tmp_var (tree tmp) { TREE_CHAIN (tmp) = gimplify_ctxp->temps; gimplify_ctxp->temps = tmp; + + /* Mark temporaries local within the nearest enclosing parallel. */ + if (gimplify_omp_ctxp) + { + struct gimplify_omp_ctx *ctx = gimplify_omp_ctxp; + while (ctx && !ctx->is_parallel) + ctx = ctx->outer_context; + if (ctx) + omp_add_variable (ctx, tmp, GOVD_LOCAL | GOVD_SEEN); + } } else if (cfun) record_vars (tmp); @@ -933,6 +1026,16 @@ gimplify_bind_expr (tree *expr_p, tree temp, tree *pre_p) DECL_COMPLEX_GIMPLE_REG_P (t) = 1; } + /* Mark variables seen in this bind expr as locals. */ + if (gimplify_omp_ctxp) + { + struct gimplify_omp_ctx *ctx = gimplify_omp_ctxp; + + for (t = BIND_EXPR_VARS (bind_expr); t ; t = TREE_CHAIN (t)) + if (TREE_CODE (t) == VAR_DECL && !is_global_var (t)) + omp_add_variable (ctx, t, GOVD_LOCAL | GOVD_SEEN); + } + gimple_push_bind_expr (bind_expr); gimplify_ctxp->save_stack = false; @@ -1282,9 +1385,16 @@ static enum gimplify_status gimplify_case_label_expr (tree *expr_p) { tree expr = *expr_p; + struct gimplify_ctx *ctxp; + + /* Invalid OpenMP programs can play Duff's Device type games with + #pragma omp parallel. At least in the C front end, we don't + detect such invalid branches until after gimplification. */ + for (ctxp = gimplify_ctxp; ; ctxp = ctxp->prev_context) + if (ctxp->case_labels) + break; - gcc_assert (gimplify_ctxp->case_labels); - VEC_safe_push (tree, heap, gimplify_ctxp->case_labels, expr); + VEC_safe_push (tree, heap, ctxp->case_labels, expr); *expr_p = build1 (LABEL_EXPR, void_type_node, CASE_LABEL (expr)); return GS_ALL_DONE; } @@ -1491,6 +1601,10 @@ gimplify_var_or_parm_decl (tree *expr_p) return GS_ERROR; } + /* When within an OpenMP context, notice uses of variables. */ + if (gimplify_omp_ctxp && omp_notice_variable (gimplify_omp_ctxp, decl, true)) + return GS_ALL_DONE; + /* If the decl is an alias for another expression, substitute it now. */ if (DECL_HAS_VALUE_EXPR_P (decl)) { @@ -4017,6 +4131,1049 @@ gimplify_to_stmt_list (tree *stmt_p) } } +/* Gimplify *EXPR_P as if it had been used inside the gimplification + context CTX_P. The other arguments are as in gimplify_expr. */ + +static enum gimplify_status +gimplify_expr_in_ctx (tree *expr_p, tree *pre_p, tree *post_p, + bool (* gimple_test_f) (tree), fallback_t fallback, + struct gimplify_ctx *ctx_p, + struct gimplify_omp_ctx *omp_ctx_p) +{ + enum gimplify_status ret; + struct gimplify_ctx *prev_ctxp; + struct gimplify_omp_ctx *prev_omp_ctxp; + + prev_ctxp = gimplify_ctxp; + gimplify_ctxp = ctx_p; + prev_omp_ctxp = gimplify_omp_ctxp; + gimplify_omp_ctxp = omp_ctx_p; + ret = gimplify_expr (expr_p, pre_p, post_p, gimple_test_f, fallback); + gimplify_ctxp = prev_ctxp; + gimplify_omp_ctxp = prev_omp_ctxp; + + return ret; +} + +/* Add FIRSTPRIVATE entries for DECL in the OpenMP the surrounding parallels + to CTX. If entries already exist, force them to be some flavor of private. + If there is no enclosing parallel, do nothing. */ + +void +omp_firstprivatize_variable (struct gimplify_omp_ctx *ctx, tree decl) +{ + splay_tree_node n; + + if (decl == NULL || !DECL_P (decl)) + return; + + do + { + n = splay_tree_lookup (ctx->variables, (splay_tree_key)decl); + if (n != NULL) + { + if (n->value & GOVD_SHARED) + n->value = GOVD_FIRSTPRIVATE | (n->value & GOVD_SEEN); + else + return; + } + else if (ctx->is_parallel) + omp_add_variable (ctx, decl, GOVD_FIRSTPRIVATE); + + ctx = ctx->outer_context; + } + while (ctx); +} + +/* Similarly for each of the type sizes of TYPE. */ + +static void +omp_firstprivatize_type_sizes (struct gimplify_omp_ctx *ctx, tree type) +{ + if (type == NULL || type == error_mark_node) + return; + type = TYPE_MAIN_VARIANT (type); + + if (pointer_set_insert (ctx->privatized_types, type)) + return; + + switch (TREE_CODE (type)) + { + case INTEGER_TYPE: + case ENUMERAL_TYPE: + case BOOLEAN_TYPE: + case CHAR_TYPE: + case REAL_TYPE: + omp_firstprivatize_variable (ctx, TYPE_MIN_VALUE (type)); + omp_firstprivatize_variable (ctx, TYPE_MAX_VALUE (type)); + break; + + case ARRAY_TYPE: + omp_firstprivatize_type_sizes (ctx, TREE_TYPE (type)); + omp_firstprivatize_type_sizes (ctx, TYPE_DOMAIN (type)); + break; + + case RECORD_TYPE: + case UNION_TYPE: + case QUAL_UNION_TYPE: + { + tree field; + for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field)) + if (TREE_CODE (field) == FIELD_DECL) + { + omp_firstprivatize_variable (ctx, DECL_FIELD_OFFSET (field)); + omp_firstprivatize_type_sizes (ctx, TREE_TYPE (field)); + } + } + break; + + case POINTER_TYPE: + case REFERENCE_TYPE: + omp_firstprivatize_type_sizes (ctx, TREE_TYPE (type)); + break; + + default: + break; + } + + omp_firstprivatize_variable (ctx, TYPE_SIZE (type)); + omp_firstprivatize_variable (ctx, TYPE_SIZE_UNIT (type)); + lang_hooks.types.omp_firstprivatize_type_sizes (ctx, type); +} + +/* Add an entry for DECL in the OpenMP context CTX with FLAGS. */ + +static void +omp_add_variable (struct gimplify_omp_ctx *ctx, tree decl, unsigned int flags) +{ + splay_tree_node n; + unsigned int nflags; + tree t; + + if (decl == error_mark_node || TREE_TYPE (decl) == error_mark_node) + return; + + /* Never elide decls whose type has TREE_ADDRESSABLE set. This means + there are constructors involved somewhere. */ + if (TREE_ADDRESSABLE (TREE_TYPE (decl)) + || TYPE_NEEDS_CONSTRUCTING (TREE_TYPE (decl))) + flags |= GOVD_SEEN; + + n = splay_tree_lookup (ctx->variables, (splay_tree_key)decl); + if (n != NULL) + { + /* We shouldn't be re-adding the decl with the same data + sharing class. */ + gcc_assert ((n->value & GOVD_DATA_SHARE_CLASS & flags) == 0); + /* The only combination of data sharing classes we should see is + FIRSTPRIVATE and LASTPRIVATE. */ + nflags = n->value | flags; + gcc_assert ((nflags & GOVD_DATA_SHARE_CLASS) + == (GOVD_FIRSTPRIVATE | GOVD_LASTPRIVATE)); + n->value = nflags; + return; + } + + /* When adding a variable-sized variable, we have to handle all sorts + of additional bits of data: the pointer replacement variable, and + the parameters of the type. */ + if (!TREE_CONSTANT (DECL_SIZE (decl))) + { + /* Add the pointer replacement variable as PRIVATE if the variable + replacement is private, else FIRSTPRIVATE since we'll need the + address of the original variable either for SHARED, or for the + copy into or out of the context. */ + if (!(flags & GOVD_LOCAL)) + { + nflags = flags & GOVD_PRIVATE ? GOVD_PRIVATE : GOVD_FIRSTPRIVATE; + nflags |= flags & GOVD_SEEN; + t = DECL_VALUE_EXPR (decl); + gcc_assert (TREE_CODE (t) == INDIRECT_REF); + t = TREE_OPERAND (t, 0); + gcc_assert (DECL_P (t)); + omp_add_variable (ctx, t, nflags); + } + + /* Add all of the variable and type parameters (which should have + been gimplified to a formal temporary) as FIRSTPRIVATE. */ + omp_firstprivatize_variable (ctx, DECL_SIZE_UNIT (decl)); + omp_firstprivatize_variable (ctx, DECL_SIZE (decl)); + omp_firstprivatize_type_sizes (ctx, TREE_TYPE (decl)); + + /* The variable-sized variable itself is never SHARED, only some form + of PRIVATE. The sharing would take place via the pointer variable + which we remapped above. */ + if (flags & GOVD_SHARED) + flags = GOVD_PRIVATE | GOVD_DEBUG_PRIVATE + | (flags & (GOVD_SEEN | GOVD_EXPLICIT)); + + /* We're going to make use of the TYPE_SIZE_UNIT at least in the + alloca statement we generate for the variable, so make sure it + is available. This isn't automatically needed for the SHARED + case, since we won't be allocating local storage then. */ + else + omp_notice_variable (ctx, TYPE_SIZE_UNIT (TREE_TYPE (decl)), true); + } + else if (lang_hooks.decls.omp_privatize_by_reference (decl)) + { + gcc_assert ((flags & GOVD_LOCAL) == 0); + omp_firstprivatize_type_sizes (ctx, TREE_TYPE (decl)); + + /* Similar to the direct variable sized case above, we'll need the + size of references being privatized. */ + if ((flags & GOVD_SHARED) == 0) + { + t = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (decl))); + if (!TREE_CONSTANT (t)) + omp_notice_variable (ctx, t, true); + } + } + + splay_tree_insert (ctx->variables, (splay_tree_key)decl, flags); +} + +/* Record the fact that DECL was used within the OpenMP context CTX. + IN_CODE is true when real code uses DECL, and false when we should + merely emit default(none) errors. Return true if DECL is going to + be remapped and thus DECL shouldn't be gimplified into its + DECL_VALUE_EXPR (if any). */ + +static bool +omp_notice_variable (struct gimplify_omp_ctx *ctx, tree decl, bool in_code) +{ + splay_tree_node n; + unsigned flags = in_code ? GOVD_SEEN : 0; + bool ret = false, shared; + + if (decl == error_mark_node || TREE_TYPE (decl) == error_mark_node) + return false; + + /* Threadprivate variables are predetermined. */ + if (is_global_var (decl)) + { + if (DECL_THREAD_LOCAL_P (decl)) + return false; + + if (DECL_HAS_VALUE_EXPR_P (decl)) + { + tree value = get_base_address (DECL_VALUE_EXPR (decl)); + + if (value && DECL_P (value) && DECL_THREAD_LOCAL_P (value)) + return false; + } + } + + n = splay_tree_lookup (ctx->variables, (splay_tree_key)decl); + if (n == NULL) + { + enum omp_clause_default_kind default_kind, kind; + + if (!ctx->is_parallel) + goto do_outer; + + /* ??? Some compiler-generated variables (like SAVE_EXPRs) could be + remapped firstprivate instead of shared. To some extent this is + addressed in omp_firstprivatize_type_sizes, but not effectively. */ + default_kind = ctx->default_kind; + kind = lang_hooks.decls.omp_predetermined_sharing (decl); + if (kind != OMP_CLAUSE_DEFAULT_UNSPECIFIED) + default_kind = kind; + + switch (default_kind) + { + case OMP_CLAUSE_DEFAULT_NONE: + error ("%qs not specified in enclosing parallel", + IDENTIFIER_POINTER (DECL_NAME (decl))); + error ("%Henclosing parallel", &ctx->location); + /* FALLTHRU */ + case OMP_CLAUSE_DEFAULT_SHARED: + flags |= GOVD_SHARED; + break; + case OMP_CLAUSE_DEFAULT_PRIVATE: + flags |= GOVD_PRIVATE; + break; + default: + gcc_unreachable (); + } + + omp_add_variable (ctx, decl, flags); + + shared = (flags & GOVD_SHARED) != 0; + ret = lang_hooks.decls.omp_disregard_value_expr (decl, shared); + goto do_outer; + } + + shared = ((flags | n->value) & GOVD_SHARED) != 0; + ret = lang_hooks.decls.omp_disregard_value_expr (decl, shared); + + /* If nothing changed, there's nothing left to do. */ + if ((n->value & flags) == flags) + return ret; + flags |= n->value; + n->value = flags; + + do_outer: + /* If the variable is private in the current context, then we don't + need to propagate anything to an outer context. */ + if (flags & GOVD_PRIVATE) + return ret; + if (ctx->outer_context + && omp_notice_variable (ctx->outer_context, decl, in_code)) + return true; + return ret; +} + +/* Verify that DECL is private within CTX. If there's specific information + to the contrary in the innermost scope, generate an error. */ + +static bool +omp_is_private (struct gimplify_omp_ctx *ctx, tree decl) +{ + splay_tree_node n; + + n = splay_tree_lookup (ctx->variables, (splay_tree_key)decl); + if (n != NULL) + { + if (n->value & GOVD_SHARED) + { + if (ctx == gimplify_omp_ctxp) + error ("iteration variable %qs should be private", + IDENTIFIER_POINTER (DECL_NAME (decl))); + n->value = GOVD_PRIVATE; + } + return true; + } + + if (ctx->outer_context) + return omp_is_private (ctx->outer_context, decl); + else if (ctx->is_parallel) + return false; + else + return !is_global_var (decl); +} + +/* Scan the OpenMP clauses in *LIST_P, installing mappings into a new + and previous omp contexts. */ + +static void +gimplify_scan_omp_clauses (tree *list_p, tree *pre_p, bool in_parallel) +{ + struct gimplify_omp_ctx *ctx, *outer_ctx; + tree c; + + ctx = new_omp_context (in_parallel); + outer_ctx = ctx->outer_context; + + while ((c = *list_p) != NULL) + { + enum gimplify_status gs; + bool remove = false; + bool notice_outer = true; + unsigned int flags; + tree decl; + + switch (TREE_CODE (c)) + { + case OMP_CLAUSE_PRIVATE: + flags = GOVD_PRIVATE | GOVD_EXPLICIT; + notice_outer = false; + goto do_add; + case OMP_CLAUSE_SHARED: + flags = GOVD_SHARED | GOVD_EXPLICIT; + goto do_add; + case OMP_CLAUSE_FIRSTPRIVATE: + flags = GOVD_FIRSTPRIVATE | GOVD_EXPLICIT; + goto do_add; + case OMP_CLAUSE_LASTPRIVATE: + flags = GOVD_LASTPRIVATE | GOVD_SEEN | GOVD_EXPLICIT; + goto do_add; + case OMP_CLAUSE_REDUCTION: + flags = GOVD_REDUCTION | GOVD_SEEN | GOVD_EXPLICIT; + goto do_add; + + do_add: + decl = OMP_CLAUSE_DECL (c); + if (decl == error_mark_node || TREE_TYPE (decl) == error_mark_node) + { + remove = true; + break; + } + omp_add_variable (ctx, decl, flags); + if (TREE_CODE (c) == OMP_CLAUSE_REDUCTION + && OMP_CLAUSE_REDUCTION_PLACEHOLDER (c)) + { + omp_add_variable (ctx, OMP_CLAUSE_REDUCTION_PLACEHOLDER (c), + GOVD_LOCAL); + gimplify_omp_ctxp = ctx; + push_gimplify_context (); + gimplify_stmt (&OMP_CLAUSE_REDUCTION_INIT (c)); + pop_gimplify_context (OMP_CLAUSE_REDUCTION_INIT (c)); + push_gimplify_context (); + gimplify_stmt (&OMP_CLAUSE_REDUCTION_MERGE (c)); + pop_gimplify_context (OMP_CLAUSE_REDUCTION_MERGE (c)); + gimplify_omp_ctxp = outer_ctx; + } + if (notice_outer) + goto do_notice; + break; + + case OMP_CLAUSE_COPYIN: + case OMP_CLAUSE_COPYPRIVATE: + decl = OMP_CLAUSE_DECL (c); + if (decl == error_mark_node || TREE_TYPE (decl) == error_mark_node) + { + remove = true; + break; + } + do_notice: + if (outer_ctx) + omp_notice_variable (outer_ctx, decl, true); + break; + + case OMP_CLAUSE_SCHEDULE: + if (gimplify_ctxp->combined_pre_p) + { + gcc_assert (gimplify_omp_ctxp == outer_ctx); + gs = gimplify_expr_in_ctx (&OMP_CLAUSE_SCHEDULE_CHUNK_EXPR (c), + gimplify_ctxp->combined_pre_p, NULL, + is_gimple_val, fb_rvalue, + gimplify_ctxp->combined_ctxp, + outer_ctx->outer_context); + if (gs == GS_ERROR) + remove = true; + break; + } + /* FALLTHRU */ + case OMP_CLAUSE_IF: + case OMP_CLAUSE_NUM_THREADS: + gs = gimplify_expr (&TREE_OPERAND (c, 0), pre_p, NULL, + is_gimple_val, fb_rvalue); + if (gs == GS_ERROR) + remove = true; + break; + + case OMP_CLAUSE_NOWAIT: + case OMP_CLAUSE_ORDERED: + break; + + case OMP_CLAUSE_DEFAULT: + ctx->default_kind = OMP_CLAUSE_DEFAULT_KIND (c); + break; + + default: + gcc_unreachable (); + } + + if (remove) + *list_p = OMP_CLAUSE_CHAIN (c); + else + list_p = &OMP_CLAUSE_CHAIN (c); + } + + gimplify_omp_ctxp = ctx; +} + +/* For all variables that were not actually used within the context, + remove PRIVATE, SHARED, and FIRSTPRIVATE clauses. */ + +static int +gimplify_adjust_omp_clauses_1 (splay_tree_node n, void *data) +{ + tree *list_p = (tree *) data; + tree decl = (tree) n->key; + unsigned flags = n->value; + enum tree_code code; + tree clause; + bool private_debug; + + if (flags & (GOVD_EXPLICIT | GOVD_LOCAL)) + return 0; + if ((flags & GOVD_SEEN) == 0) + return 0; + if (flags & GOVD_DEBUG_PRIVATE) + { + gcc_assert ((flags & GOVD_DATA_SHARE_CLASS) == GOVD_PRIVATE); + private_debug = true; + } + else + private_debug + = lang_hooks.decls.omp_private_debug_clause (decl, + !!(flags & GOVD_SHARED)); + if (private_debug) + code = OMP_CLAUSE_PRIVATE; + else if (flags & GOVD_SHARED) + { + if (is_global_var (decl)) + return 0; + code = OMP_CLAUSE_SHARED; + } + else if (flags & GOVD_PRIVATE) + code = OMP_CLAUSE_PRIVATE; + else if (flags & GOVD_FIRSTPRIVATE) + code = OMP_CLAUSE_FIRSTPRIVATE; + else + gcc_unreachable (); + + clause = build1 (code, void_type_node, decl); + OMP_CLAUSE_CHAIN (clause) = *list_p; + if (private_debug) + OMP_CLAUSE_PRIVATE_DEBUG (clause) = 1; + *list_p = clause; + + return 0; +} + +static void +gimplify_adjust_omp_clauses (tree *list_p) +{ + struct gimplify_omp_ctx *ctx = gimplify_omp_ctxp; + tree c, decl; + + while ((c = *list_p) != NULL) + { + splay_tree_node n; + bool remove = false; + + switch (TREE_CODE (c)) + { + case OMP_CLAUSE_PRIVATE: + case OMP_CLAUSE_SHARED: + case OMP_CLAUSE_FIRSTPRIVATE: + decl = OMP_CLAUSE_DECL (c); + n = splay_tree_lookup (ctx->variables, (splay_tree_key) decl); + remove = !(n->value & GOVD_SEEN); + if (! remove) + { + bool shared = TREE_CODE (c) == OMP_CLAUSE_SHARED; + if ((n->value & GOVD_DEBUG_PRIVATE) + || lang_hooks.decls.omp_private_debug_clause (decl, shared)) + { + gcc_assert ((n->value & GOVD_DEBUG_PRIVATE) == 0 + || ((n->value & GOVD_DATA_SHARE_CLASS) + == GOVD_PRIVATE)); + TREE_SET_CODE (c, OMP_CLAUSE_PRIVATE); + OMP_CLAUSE_PRIVATE_DEBUG (c) = 1; + } + } + break; + + case OMP_CLAUSE_LASTPRIVATE: + /* Make sure OMP_CLAUSE_LASTPRIVATE_FIRSTPRIVATE is set to + accurately reflect the presence of a FIRSTPRIVATE clause. */ + decl = OMP_CLAUSE_DECL (c); + n = splay_tree_lookup (ctx->variables, (splay_tree_key) decl); + OMP_CLAUSE_LASTPRIVATE_FIRSTPRIVATE (c) + = (n->value & GOVD_FIRSTPRIVATE) != 0; + break; + + case OMP_CLAUSE_REDUCTION: + case OMP_CLAUSE_COPYIN: + case OMP_CLAUSE_COPYPRIVATE: + case OMP_CLAUSE_IF: + case OMP_CLAUSE_NUM_THREADS: + case OMP_CLAUSE_SCHEDULE: + case OMP_CLAUSE_NOWAIT: + case OMP_CLAUSE_ORDERED: + case OMP_CLAUSE_DEFAULT: + break; + + default: + gcc_unreachable (); + } + + if (remove) + *list_p = OMP_CLAUSE_CHAIN (c); + else + list_p = &OMP_CLAUSE_CHAIN (c); + } + + /* Add in any implicit data sharing. */ + splay_tree_foreach (ctx->variables, gimplify_adjust_omp_clauses_1, list_p); + + gimplify_omp_ctxp = ctx->outer_context; + delete_omp_context (ctx); +} + +/* Gimplify the contents of an OMP_PARALLEL statement. This involves + gimplification of the body, as well as scanning the body for used + variables. We need to do this scan now, because variable-sized + decls will be decomposed during gimplification. */ + +static enum gimplify_status +gimplify_omp_parallel (tree *expr_p, tree *pre_p) +{ + tree expr = *expr_p; + + gimplify_scan_omp_clauses (&OMP_PARALLEL_CLAUSES (expr), pre_p, true); + + push_gimplify_context (); + + if (determine_parallel_type (expr) == IS_COMBINED_PARALLEL) + { + gimplify_ctxp->combined_pre_p = pre_p; + gimplify_ctxp->combined_ctxp = gimplify_ctxp->prev_context; + } + + gimplify_stmt (&OMP_PARALLEL_BODY (expr)); + pop_gimplify_context (OMP_PARALLEL_BODY (expr)); + + gimplify_ctxp->combined_pre_p = NULL; + gimplify_ctxp->combined_ctxp = NULL; + + gimplify_adjust_omp_clauses (&OMP_PARALLEL_CLAUSES (expr)); + + return GS_ALL_DONE; +} + +/* Gimplify the gross structure of an OMP_FOR statement. */ + +static enum gimplify_status +gimplify_omp_for (tree *expr_p, tree *pre_p) +{ + tree for_stmt, decl, t; + enum gimplify_status ret = 0; + struct gimplify_omp_ctx *outer_combined_omp_ctxp = NULL; + + for_stmt = *expr_p; + + if (gimplify_ctxp->combined_pre_p) + outer_combined_omp_ctxp = gimplify_omp_ctxp->outer_context; + + gimplify_scan_omp_clauses (&OMP_FOR_CLAUSES (for_stmt), pre_p, false); + + t = OMP_FOR_INIT (for_stmt); + gcc_assert (TREE_CODE (t) == MODIFY_EXPR); + decl = TREE_OPERAND (t, 0); + gcc_assert (DECL_P (decl)); + gcc_assert (INTEGRAL_TYPE_P (TREE_TYPE (decl))); + gcc_assert (!TYPE_UNSIGNED (TREE_TYPE (decl))); + + /* Make sure the iteration variable is private. */ + if (omp_is_private (gimplify_omp_ctxp, decl)) + omp_notice_variable (gimplify_omp_ctxp, decl, true); + else + omp_add_variable (gimplify_omp_ctxp, decl, GOVD_PRIVATE | GOVD_SEEN); + + /* Gimplify inside our parent's context if this is part of a combined + parallel+workshare directive. */ + if (gimplify_ctxp->combined_pre_p) + ret |= gimplify_expr_in_ctx (&TREE_OPERAND (t, 1), + gimplify_ctxp->combined_pre_p, NULL, + is_gimple_val, fb_rvalue, + gimplify_ctxp->combined_ctxp, + outer_combined_omp_ctxp); + else + ret |= gimplify_expr (&TREE_OPERAND (t, 1), &OMP_FOR_PRE_BODY (for_stmt), + NULL, is_gimple_val, fb_rvalue); + + t = OMP_FOR_COND (for_stmt); + gcc_assert (COMPARISON_CLASS_P (t)); + gcc_assert (TREE_OPERAND (t, 0) == decl); + + /* Gimplify inside our parent's context if this is part of a combined + parallel+workshare directive. */ + if (gimplify_ctxp->combined_pre_p) + ret |= gimplify_expr_in_ctx (&TREE_OPERAND (t, 1), + gimplify_ctxp->combined_pre_p, NULL, + is_gimple_val, fb_rvalue, + gimplify_ctxp->combined_ctxp, + outer_combined_omp_ctxp); + else + ret |= gimplify_expr (&TREE_OPERAND (t, 1), &OMP_FOR_PRE_BODY (for_stmt), + NULL, is_gimple_val, fb_rvalue); + + t = OMP_FOR_INCR (for_stmt); + switch (TREE_CODE (t)) + { + case PREINCREMENT_EXPR: + case POSTINCREMENT_EXPR: + t = build_int_cst (TREE_TYPE (decl), 1); + goto build_modify; + case PREDECREMENT_EXPR: + case POSTDECREMENT_EXPR: + t = build_int_cst (TREE_TYPE (decl), -1); + goto build_modify; + build_modify: + t = build2 (PLUS_EXPR, TREE_TYPE (decl), decl, t); + t = build2 (MODIFY_EXPR, void_type_node, decl, t); + OMP_FOR_INCR (for_stmt) = t; + break; + + case MODIFY_EXPR: + gcc_assert (TREE_OPERAND (t, 0) == decl); + t = TREE_OPERAND (t, 1); + switch (TREE_CODE (t)) + { + case PLUS_EXPR: + if (TREE_OPERAND (t, 1) == decl) + { + TREE_OPERAND (t, 1) = TREE_OPERAND (t, 0); + TREE_OPERAND (t, 0) = decl; + break; + } + case MINUS_EXPR: + gcc_assert (TREE_OPERAND (t, 0) == decl); + break; + default: + gcc_unreachable (); + } + + /* Gimplify inside our parent's context if this is part of a + combined parallel+workshare directive. */ + if (gimplify_ctxp->combined_pre_p) + ret |= gimplify_expr_in_ctx (&TREE_OPERAND (t, 1), + gimplify_ctxp->combined_pre_p, NULL, + is_gimple_val, fb_rvalue, + gimplify_ctxp->combined_ctxp, + outer_combined_omp_ctxp); + else + ret |= gimplify_expr (&TREE_OPERAND (t, 1), + &OMP_FOR_PRE_BODY (for_stmt), NULL, + is_gimple_val, fb_rvalue); + break; + + default: + gcc_unreachable (); + } + + gimplify_to_stmt_list (&OMP_FOR_BODY (for_stmt)); + gimplify_adjust_omp_clauses (&OMP_FOR_CLAUSES (for_stmt)); + + return ret == GS_ALL_DONE ? GS_ALL_DONE : GS_ERROR; +} + +/* Gimplify the gross structure of other OpenMP worksharing constructs. + In particular, OMP_SECTIONS and OMP_SINGLE. */ + +static enum gimplify_status +gimplify_omp_workshare (tree *expr_p, tree *pre_p) +{ + tree stmt = *expr_p; + + gimplify_scan_omp_clauses (&OMP_CLAUSES (stmt), pre_p, false); + gimplify_to_stmt_list (&OMP_BODY (stmt)); + gimplify_adjust_omp_clauses (&OMP_CLAUSES (stmt)); + + return GS_ALL_DONE; +} + +/* A subroutine of gimplify_omp_atomic. The front end is supposed to have + stabilized the lhs of the atomic operation as *ADDR. Return true if + EXPR is this stabilized form. */ + +static bool +goa_lhs_expr_p (tree expr, tree addr) +{ + /* Also include casts to other type variants. The C front end is fond + of adding these for e.g. volatile variables. This is like + STRIP_TYPE_NOPS but includes the main variant lookup. */ + while ((TREE_CODE (expr) == NOP_EXPR + || TREE_CODE (expr) == CONVERT_EXPR + || TREE_CODE (expr) == NON_LVALUE_EXPR) + && TREE_OPERAND (expr, 0) != error_mark_node + && (TYPE_MAIN_VARIANT (TREE_TYPE (expr)) + == TYPE_MAIN_VARIANT (TREE_TYPE (TREE_OPERAND (expr, 0))))) + expr = TREE_OPERAND (expr, 0); + + if (TREE_CODE (expr) == INDIRECT_REF && TREE_OPERAND (expr, 0) == addr) + return true; + if (TREE_CODE (addr) == ADDR_EXPR && expr == TREE_OPERAND (addr, 0)) + return true; + return false; +} + +/* A subroutine of gimplify_omp_atomic. Attempt to implement the atomic + operation as a __sync_fetch_and_op builtin. INDEX is log2 of the + size of the data type, and thus usable to find the index of the builtin + decl. Returns GS_UNHANDLED if the expression is not of the proper form. */ + +static enum gimplify_status +gimplify_omp_atomic_fetch_op (tree *expr_p, tree addr, tree rhs, int index) +{ + enum built_in_function base; + tree decl, args, itype; + enum insn_code *optab; + + /* Check for one of the supported fetch-op operations. */ + switch (TREE_CODE (rhs)) + { + case PLUS_EXPR: + base = BUILT_IN_FETCH_AND_ADD_N; + optab = sync_add_optab; + break; + case MINUS_EXPR: + base = BUILT_IN_FETCH_AND_SUB_N; + optab = sync_add_optab; + break; + case BIT_AND_EXPR: + base = BUILT_IN_FETCH_AND_AND_N; + optab = sync_and_optab; + break; + case BIT_IOR_EXPR: + base = BUILT_IN_FETCH_AND_OR_N; + optab = sync_ior_optab; + break; + case BIT_XOR_EXPR: + base = BUILT_IN_FETCH_AND_XOR_N; + optab = sync_xor_optab; + break; + default: + return GS_UNHANDLED; + } + + /* Make sure the expression is of the proper form. */ + if (goa_lhs_expr_p (TREE_OPERAND (rhs, 0), addr)) + rhs = TREE_OPERAND (rhs, 1); + else if (commutative_tree_code (TREE_CODE (rhs)) + && goa_lhs_expr_p (TREE_OPERAND (rhs, 1), addr)) + rhs = TREE_OPERAND (rhs, 0); + else + return GS_UNHANDLED; + + decl = built_in_decls[base + index + 1]; + itype = TREE_TYPE (TREE_TYPE (decl)); + + if (optab[TYPE_MODE (itype)] == CODE_FOR_nothing) + return GS_UNHANDLED; + + args = tree_cons (NULL, fold_convert (itype, rhs), NULL); + args = tree_cons (NULL, addr, args); + *expr_p = build_function_call_expr (decl, args); + return GS_OK; +} + +/* A subroutine of gimplify_omp_atomic_pipeline. Walk *EXPR_P and replace + appearences of *LHS_ADDR with LHS_VAR. If an expression does not involve + the lhs, evaluate it into a temporary. Return 1 if the lhs appeared as + a subexpression, 0 if it did not, or -1 if an error was encountered. */ + +static int +goa_stabilize_expr (tree *expr_p, tree *pre_p, tree lhs_addr, tree lhs_var) +{ + tree expr = *expr_p; + int saw_lhs; + + if (goa_lhs_expr_p (expr, lhs_addr)) + { + *expr_p = lhs_var; + return 1; + } + if (is_gimple_val (expr)) + return 0; + + saw_lhs = 0; + switch (TREE_CODE_CLASS (TREE_CODE (expr))) + { + case tcc_binary: + saw_lhs |= goa_stabilize_expr (&TREE_OPERAND (expr, 1), pre_p, + lhs_addr, lhs_var); + case tcc_unary: + saw_lhs |= goa_stabilize_expr (&TREE_OPERAND (expr, 0), pre_p, + lhs_addr, lhs_var); + break; + default: + break; + } + + if (saw_lhs == 0) + { + enum gimplify_status gs; + gs = gimplify_expr (expr_p, pre_p, NULL, is_gimple_val, fb_rvalue); + if (gs != GS_ALL_DONE) + saw_lhs = -1; + } + + return saw_lhs; +} + +/* A subroutine of gimplify_omp_atomic. Implement the atomic operation as: + + oldval = *addr; + repeat: + newval = rhs; // with oldval replacing *addr in rhs + oldval = __sync_val_compare_and_swap (addr, oldval, newval); + if (oldval != newval) + goto repeat; + + INDEX is log2 of the size of the data type, and thus usable to find the + index of the builtin decl. */ + +static enum gimplify_status +gimplify_omp_atomic_pipeline (tree *expr_p, tree *pre_p, tree addr, + tree rhs, int index) +{ + tree oldval, oldival, oldival2, newval, newival, label; + tree type, itype, cmpxchg, args, x, iaddr; + + cmpxchg = built_in_decls[BUILT_IN_VAL_COMPARE_AND_SWAP_N + index + 1]; + type = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (addr))); + itype = TREE_TYPE (TREE_TYPE (cmpxchg)); + + if (sync_compare_and_swap[TYPE_MODE (itype)] == CODE_FOR_nothing) + return GS_UNHANDLED; + + oldval = create_tmp_var (type, NULL); + newval = create_tmp_var (type, NULL); + + /* Precompute as much of RHS as possible. In the same walk, replace + occurrences of the lhs value with our temporary. */ + if (goa_stabilize_expr (&rhs, pre_p, addr, oldval) < 0) + return GS_ERROR; + + x = build_fold_indirect_ref (addr); + x = build2 (MODIFY_EXPR, void_type_node, oldval, x); + gimplify_and_add (x, pre_p); + + /* For floating-point values, we'll need to view-convert them to integers + so that we can perform the atomic compare and swap. Simplify the + following code by always setting up the "i"ntegral variables. */ + if (INTEGRAL_TYPE_P (type) || POINTER_TYPE_P (type)) + { + oldival = oldval; + newival = newval; + iaddr = addr; + } + else + { + oldival = create_tmp_var (itype, NULL); + newival = create_tmp_var (itype, NULL); + + x = build1 (VIEW_CONVERT_EXPR, itype, oldval); + x = build2 (MODIFY_EXPR, void_type_node, oldival, x); + gimplify_and_add (x, pre_p); + iaddr = fold_convert (build_pointer_type (itype), addr); + } + + oldival2 = create_tmp_var (itype, NULL); + + label = create_artificial_label (); + x = build1 (LABEL_EXPR, void_type_node, label); + gimplify_and_add (x, pre_p); + + x = build2 (MODIFY_EXPR, void_type_node, newval, rhs); + gimplify_and_add (x, pre_p); + + if (newval != newival) + { + x = build1 (VIEW_CONVERT_EXPR, itype, newval); + x = build2 (MODIFY_EXPR, void_type_node, newival, x); + gimplify_and_add (x, pre_p); + } + + x = build2 (MODIFY_EXPR, void_type_node, oldival2, oldival); + gimplify_and_add (x, pre_p); + + args = tree_cons (NULL, fold_convert (itype, newival), NULL); + args = tree_cons (NULL, fold_convert (itype, oldival), args); + args = tree_cons (NULL, iaddr, args); + x = build_function_call_expr (cmpxchg, args); + if (oldval == oldival) + x = fold_convert (type, x); + x = build2 (MODIFY_EXPR, void_type_node, oldival, x); + gimplify_and_add (x, pre_p); + + /* For floating point, be prepared for the loop backedge. */ + if (oldval != oldival) + { + x = build1 (VIEW_CONVERT_EXPR, type, oldival); + x = build2 (MODIFY_EXPR, void_type_node, oldval, x); + gimplify_and_add (x, pre_p); + } + + /* Note that we always perform the comparison as an integer, even for + floating point. This allows the atomic operation to properly + succeed even with NaNs and -0.0. */ + x = build3 (COND_EXPR, void_type_node, + build2 (NE_EXPR, boolean_type_node, oldival, oldival2), + build1 (GOTO_EXPR, void_type_node, label), NULL); + gimplify_and_add (x, pre_p); + + *expr_p = NULL; + return GS_ALL_DONE; +} + +/* A subroutine of gimplify_omp_atomic. Implement the atomic operation as: + + GOMP_atomic_start (); + *addr = rhs; + GOMP_atomic_end (); + + The result is not globally atomic, but works so long as all parallel + references are within #pragma omp atomic directives. According to + responses received from omp@openmp.org, appears to be within spec. + Which makes sense, since that's how several other compilers handle + this situation as well. */ + +static enum gimplify_status +gimplify_omp_atomic_mutex (tree *expr_p, tree *pre_p, tree addr, tree rhs) +{ + tree t; + + t = built_in_decls[BUILT_IN_GOMP_ATOMIC_START]; + t = build_function_call_expr (t, NULL); + gimplify_and_add (t, pre_p); + + t = build_fold_indirect_ref (addr); + t = build2 (MODIFY_EXPR, void_type_node, t, rhs); + gimplify_and_add (t, pre_p); + + t = built_in_decls[BUILT_IN_GOMP_ATOMIC_END]; + t = build_function_call_expr (t, NULL); + gimplify_and_add (t, pre_p); + + *expr_p = NULL; + return GS_ALL_DONE; +} + +/* Gimplify an OMP_ATOMIC statement. */ + +static enum gimplify_status +gimplify_omp_atomic (tree *expr_p, tree *pre_p) +{ + tree addr = TREE_OPERAND (*expr_p, 0); + tree rhs = TREE_OPERAND (*expr_p, 1); + tree type = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (addr))); + HOST_WIDE_INT index; + + /* Make sure the type is one of the supported sizes. */ + index = tree_low_cst (TYPE_SIZE_UNIT (type), 1); + index = exact_log2 (index); + if (index >= 0 && index <= 4) + { + enum gimplify_status gs; + unsigned int align; + + if (DECL_P (TREE_OPERAND (addr, 0))) + align = DECL_ALIGN_UNIT (TREE_OPERAND (addr, 0)); + else if (TREE_CODE (TREE_OPERAND (addr, 0)) == COMPONENT_REF + && TREE_CODE (TREE_OPERAND (TREE_OPERAND (addr, 0), 1)) + == FIELD_DECL) + align = DECL_ALIGN_UNIT (TREE_OPERAND (TREE_OPERAND (addr, 0), 1)); + else + align = TYPE_ALIGN_UNIT (type); + + /* __sync builtins require strict data alignment. */ + if (exact_log2 (align) >= index) + { + /* When possible, use specialized atomic update functions. */ + if (INTEGRAL_TYPE_P (type) || POINTER_TYPE_P (type)) + { + gs = gimplify_omp_atomic_fetch_op (expr_p, addr, rhs, index); + if (gs != GS_UNHANDLED) + return gs; + } + + /* If we don't have specialized __sync builtins, try and implement + as a compare and swap loop. */ + gs = gimplify_omp_atomic_pipeline (expr_p, pre_p, addr, rhs, index); + if (gs != GS_UNHANDLED) + return gs; + } + } + + /* The ultimate fallback is wrapping the operation in a mutex. */ + return gimplify_omp_atomic_mutex (expr_p, pre_p, addr, rhs); +} /* Gimplifies the expression tree pointed to by EXPR_P. Return 0 if gimplification failed. @@ -4441,6 +5598,30 @@ gimplify_expr (tree *expr_p, tree *pre_p, tree *post_p, ret = GS_ALL_DONE; break; + case OMP_PARALLEL: + ret = gimplify_omp_parallel (expr_p, pre_p); + break; + + case OMP_FOR: + ret = gimplify_omp_for (expr_p, pre_p); + break; + + case OMP_SECTIONS: + case OMP_SINGLE: + ret = gimplify_omp_workshare (expr_p, pre_p); + break; + + case OMP_SECTION: + case OMP_MASTER: + case OMP_ORDERED: + case OMP_CRITICAL: + gimplify_to_stmt_list (&OMP_BODY (*expr_p)); + break; + + case OMP_ATOMIC: + ret = gimplify_omp_atomic (expr_p, pre_p); + break; + default: switch (TREE_CODE_CLASS (TREE_CODE (*expr_p))) { @@ -4880,6 +6061,8 @@ gimplify_body (tree *body_p, tree fndecl, bool do_parms) tree body, parm_stmts; timevar_push (TV_TREE_GIMPLIFY); + + gcc_assert (gimplify_ctxp == NULL); push_gimplify_context (); /* Unshare most shared trees in the body and in that of any nested functions. @@ -4933,6 +6116,7 @@ gimplify_body (tree *body_p, tree fndecl, bool do_parms) *body_p = body; pop_gimplify_context (body); + gcc_assert (gimplify_ctxp == NULL); #ifdef ENABLE_CHECKING walk_tree (body_p, check_pointer_types_r, NULL, NULL); diff --git a/gcc/hooks.c b/gcc/hooks.c index 2ebd0718618..a5028de43fb 100644 --- a/gcc/hooks.c +++ b/gcc/hooks.c @@ -186,6 +186,12 @@ hook_bool_tree_tree_false (tree a ATTRIBUTE_UNUSED, tree b ATTRIBUTE_UNUSED) } bool +hook_bool_tree_bool_false (tree a ATTRIBUTE_UNUSED, bool b ATTRIBUTE_UNUSED) +{ + return false; +} + +bool hook_bool_rtx_false (rtx a ATTRIBUTE_UNUSED) { return false; @@ -256,6 +262,12 @@ hook_tree_tree_tree_bool_null (tree t0 ATTRIBUTE_UNUSED, tree t1 ATTRIBUTE_UNUSE return NULL; } +tree +hook_tree_tree_tree_null (tree t0 ATTRIBUTE_UNUSED, tree t1 ATTRIBUTE_UNUSED) +{ + return NULL; +} + /* Generic hook that takes a rtx and returns a NULL string. */ const char * hook_constcharptr_rtx_null (rtx r ATTRIBUTE_UNUSED) diff --git a/gcc/hooks.h b/gcc/hooks.h index 1ca909ae2fd..eab42ab5faa 100644 --- a/gcc/hooks.h +++ b/gcc/hooks.h @@ -40,6 +40,8 @@ extern bool hook_bool_uintp_uintp_false (unsigned int *, unsigned int *); extern bool hook_bool_rtx_int_int_intp_false (rtx, int, int, int *); extern bool hook_bool_constcharptr_size_t_false (const char *, size_t); extern bool hook_bool_size_t_constcharptr_int_true (size_t, const char *, int); +extern bool hook_bool_tree_tree_false (tree, tree); +extern bool hook_bool_tree_bool_false (tree, bool); extern void hook_void_void (void); extern void hook_void_constcharptr (const char *); @@ -53,19 +55,20 @@ extern int hook_int_rtx_0 (rtx); extern int hook_int_size_t_constcharptr_int_0 (size_t, const char *, int); extern int hook_int_void_no_regs (void); +extern tree hook_tree_tree_tree_null (tree, tree); +extern tree hook_tree_tree_tree_tree_3rd_identity (tree, tree, tree); +extern tree hook_tree_tree_tree_bool_null (tree, tree, bool); + extern unsigned hook_uint_uint_constcharptrptr_0 (unsigned, const char **); extern bool default_can_output_mi_thunk_no_vcall (tree, HOST_WIDE_INT, HOST_WIDE_INT, tree); -extern bool hook_bool_tree_tree_false (tree, tree); - extern rtx hook_rtx_rtx_identity (rtx); extern rtx hook_rtx_rtx_null (rtx); extern rtx hook_rtx_tree_int_null (tree, int); -extern tree hook_tree_tree_tree_tree_3rd_identity (tree, tree, tree); + extern const char *hook_constcharptr_tree_null (tree); -extern tree hook_tree_tree_tree_bool_null (tree, tree, bool); extern const char *hook_constcharptr_rtx_null (rtx); extern const char *hook_constcharptr_tree_tree_null (tree, tree); extern const char *hook_constcharptr_int_tree_null (int, tree); diff --git a/gcc/langhooks-def.h b/gcc/langhooks-def.h index a17927a2eb2..168220e31a1 100644 --- a/gcc/langhooks-def.h +++ b/gcc/langhooks-def.h @@ -88,6 +88,11 @@ extern tree lhd_callgraph_analyze_expr (tree *, int *, tree); /* Declarations for tree gimplification hooks. */ extern int lhd_gimplify_expr (tree *, tree *, tree *); +extern enum omp_clause_default_kind lhd_omp_predetermined_sharing (tree); +extern tree lhd_omp_assignment (tree, tree, tree); +struct gimplify_omp_ctx; +extern void lhd_omp_firstprivatize_type_sizes (struct gimplify_omp_ctx *, + tree); #define LANG_HOOKS_NAME "GNU unknown" #define LANG_HOOKS_IDENTIFIER_SIZE sizeof (struct lang_identifier) @@ -213,6 +218,8 @@ extern tree lhd_make_node (enum tree_code); #define LANG_HOOKS_TYPE_PROMOTES_TO lhd_type_promotes_to #define LANG_HOOKS_REGISTER_BUILTIN_TYPE lhd_register_builtin_type #define LANG_HOOKS_TYPE_MAX_SIZE lhd_return_null_tree +#define LANG_HOOKS_OMP_FIRSTPRIVATIZE_TYPE_SIZES \ + lhd_omp_firstprivatize_type_sizes #define LANG_HOOKS_HASH_TYPES true #define LANG_HOOKS_FOR_TYPES_INITIALIZER { \ @@ -226,6 +233,7 @@ extern tree lhd_make_node (enum tree_code); LANG_HOOKS_REGISTER_BUILTIN_TYPE, \ LANG_HOOKS_INCOMPLETE_TYPE_ERROR, \ LANG_HOOKS_TYPE_MAX_SIZE, \ + LANG_HOOKS_OMP_FIRSTPRIVATIZE_TYPE_SIZES, \ LANG_HOOKS_HASH_TYPES \ } @@ -239,6 +247,14 @@ extern tree lhd_make_node (enum tree_code); #define LANG_HOOKS_PREPARE_ASSEMBLE_VARIABLE NULL #define LANG_HOOKS_DECL_OK_FOR_SIBCALL lhd_decl_ok_for_sibcall #define LANG_HOOKS_COMDAT_GROUP lhd_comdat_group +#define LANG_HOOKS_OMP_PRIVATIZE_BY_REFERENCE hook_bool_tree_false +#define LANG_HOOKS_OMP_PREDETERMINED_SHARING lhd_omp_predetermined_sharing +#define LANG_HOOKS_OMP_DISREGARD_VALUE_EXPR hook_bool_tree_bool_false +#define LANG_HOOKS_OMP_PRIVATE_DEBUG_CLAUSE hook_bool_tree_bool_false +#define LANG_HOOKS_OMP_CLAUSE_DEFAULT_CTOR hook_tree_tree_tree_null +#define LANG_HOOKS_OMP_CLAUSE_COPY_CTOR lhd_omp_assignment +#define LANG_HOOKS_OMP_CLAUSE_ASSIGN_OP lhd_omp_assignment +#define LANG_HOOKS_OMP_CLAUSE_DTOR hook_tree_tree_tree_null #define LANG_HOOKS_DECLS { \ LANG_HOOKS_GLOBAL_BINDINGS_P, \ @@ -249,7 +265,15 @@ extern tree lhd_make_node (enum tree_code); LANG_HOOKS_WRITE_GLOBALS, \ LANG_HOOKS_PREPARE_ASSEMBLE_VARIABLE, \ LANG_HOOKS_DECL_OK_FOR_SIBCALL, \ - LANG_HOOKS_COMDAT_GROUP \ + LANG_HOOKS_COMDAT_GROUP, \ + LANG_HOOKS_OMP_PRIVATIZE_BY_REFERENCE, \ + LANG_HOOKS_OMP_PREDETERMINED_SHARING, \ + LANG_HOOKS_OMP_DISREGARD_VALUE_EXPR, \ + LANG_HOOKS_OMP_PRIVATE_DEBUG_CLAUSE, \ + LANG_HOOKS_OMP_CLAUSE_DEFAULT_CTOR, \ + LANG_HOOKS_OMP_CLAUSE_COPY_CTOR, \ + LANG_HOOKS_OMP_CLAUSE_ASSIGN_OP, \ + LANG_HOOKS_OMP_CLAUSE_DTOR \ } /* The whole thing. The structure is defined in langhooks.h. */ diff --git a/gcc/langhooks.c b/gcc/langhooks.c index d7ed7505300..69be1f571d4 100644 --- a/gcc/langhooks.c +++ b/gcc/langhooks.c @@ -550,3 +550,31 @@ lhd_expr_to_decl (tree expr, bool *tc ATTRIBUTE_UNUSED, { return expr; } + +/* Return sharing kind if OpenMP sharing attribute of DECL is + predetermined, OMP_CLAUSE_DEFAULT_UNSPECIFIED otherwise. */ + +enum omp_clause_default_kind +lhd_omp_predetermined_sharing (tree decl ATTRIBUTE_UNUSED) +{ + if (DECL_ARTIFICIAL (decl)) + return OMP_CLAUSE_DEFAULT_SHARED; + return OMP_CLAUSE_DEFAULT_UNSPECIFIED; +} + +/* Generate code to copy SRC to DST. */ + +tree +lhd_omp_assignment (tree clause ATTRIBUTE_UNUSED, tree dst, tree src) +{ + return build2 (MODIFY_EXPR, void_type_node, dst, src); +} + +/* Register language specific type size variables as potentially OpenMP + firstprivate variables. */ + +void +lhd_omp_firstprivatize_type_sizes (struct gimplify_omp_ctx *c ATTRIBUTE_UNUSED, + tree t ATTRIBUTE_UNUSED) +{ +} diff --git a/gcc/langhooks.h b/gcc/langhooks.h index 3d97c9362cb..eb6aaaff12e 100644 --- a/gcc/langhooks.h +++ b/gcc/langhooks.h @@ -25,6 +25,8 @@ Boston, MA 02110-1301, USA. */ struct diagnostic_context; +struct gimplify_omp_ctx; + /* A print hook for print_tree (). */ typedef void (*lang_print_tree_hook) (FILE *, tree, int indent); @@ -142,6 +144,10 @@ struct lang_hooks_for_types for a type. */ tree (*max_size) (tree); + /* Register language specific type size variables as potentially OpenMP + firstprivate variables. */ + void (*omp_firstprivatize_type_sizes) (struct gimplify_omp_ctx *, tree); + /* Nonzero if types that are identical are to be hashed so that only one copy is kept. If a language requires unique types for each user-specified type, such as Ada, this should be set to TRUE. */ @@ -192,6 +198,38 @@ struct lang_hooks_for_decls value will be the string already stored in an IDENTIFIER_NODE.) */ const char * (*comdat_group) (tree); + + /* True if OpenMP should privatize what this DECL points to rather + than the DECL itself. */ + bool (*omp_privatize_by_reference) (tree); + + /* Return sharing kind if OpenMP sharing attribute of DECL is + predetermined, OMP_CLAUSE_DEFAULT_UNSPECIFIED otherwise. */ + enum omp_clause_default_kind (*omp_predetermined_sharing) (tree); + + /* Return true if DECL's DECL_VALUE_EXPR (if any) should be + disregarded in OpenMP construct, because it is going to be + remapped during OpenMP lowering. SHARED is true if DECL + is going to be shared, false if it is going to be privatized. */ + bool (*omp_disregard_value_expr) (tree, bool); + + /* Return true if DECL that is shared iff SHARED is true should + be put into OMP_CLAUSE_PRIVATE_DEBUG. */ + bool (*omp_private_debug_clause) (tree, bool); + + /* Build and return code for a default constructor for DECL in + response to CLAUSE. Return NULL if nothing to be done. */ + tree (*omp_clause_default_ctor) (tree clause, tree decl); + + /* Build and return code for a copy constructor from SRC to DST. */ + tree (*omp_clause_copy_ctor) (tree clause, tree dst, tree src); + + /* Similarly, except use an assignment operator instead. */ + tree (*omp_clause_assign_op) (tree clause, tree dst, tree src); + + /* Build and return code destructing DECL. Return NULL if nothing + to be done. */ + tree (*omp_clause_dtor) (tree clause, tree decl); }; /* Language-specific hooks. See langhooks-def.h for defaults. */ diff --git a/gcc/omp-builtins.def b/gcc/omp-builtins.def new file mode 100644 index 00000000000..596beae97aa --- /dev/null +++ b/gcc/omp-builtins.def @@ -0,0 +1,152 @@ +/* This file contains the definitions and documentation for the + OpenMP builtins used in the GNU compiler. + Copyright (C) 2005 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 2, 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 COPYING. If not, write to the Free +Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301, USA. */ + +/* Before including this file, you should define a macro: + + DEF_GOMP_BUILTIN (ENUM, NAME, TYPE, ATTRS) + + See builtins.def for details. */ + +DEF_GOMP_BUILTIN (BUILT_IN_OMP_GET_THREAD_NUM, "omp_get_thread_num", + BT_FN_INT, ATTR_CONST_NOTHROW_LIST) +DEF_GOMP_BUILTIN (BUILT_IN_OMP_GET_NUM_THREADS, "omp_get_num_threads", + BT_FN_INT, ATTR_CONST_NOTHROW_LIST) + +DEF_GOMP_BUILTIN (BUILT_IN_GOMP_ATOMIC_START, "GOMP_atomic_start", + BT_FN_VOID, ATTR_NOTHROW_LIST) +DEF_GOMP_BUILTIN (BUILT_IN_GOMP_ATOMIC_END, "GOMP_atomic_end", + BT_FN_VOID, ATTR_NOTHROW_LIST) +DEF_GOMP_BUILTIN (BUILT_IN_GOMP_BARRIER, "GOMP_barrier", + BT_FN_VOID, ATTR_NOTHROW_LIST) +DEF_GOMP_BUILTIN (BUILT_IN_GOMP_CRITICAL_START, "GOMP_critical_start", + BT_FN_VOID, ATTR_NOTHROW_LIST) +DEF_GOMP_BUILTIN (BUILT_IN_GOMP_CRITICAL_END, "GOMP_critical_end", + BT_FN_VOID, ATTR_NOTHROW_LIST) +DEF_GOMP_BUILTIN (BUILT_IN_GOMP_CRITICAL_NAME_START, + "GOMP_critical_name_start", + BT_FN_VOID_PTRPTR, ATTR_NOTHROW_LIST) +DEF_GOMP_BUILTIN (BUILT_IN_GOMP_CRITICAL_NAME_END, + "GOMP_critical_name_end", + BT_FN_VOID_PTRPTR, ATTR_NOTHROW_LIST) +/* NOTE: Do not change the order of BUILT_IN_GOMP_LOOP_*_START. They + are used in index arithmetic with enum omp_clause_schedule_kind + in omp-low.c. */ +DEF_GOMP_BUILTIN (BUILT_IN_GOMP_LOOP_STATIC_START, + "GOMP_loop_static_start", + BT_FN_BOOL_LONG_LONG_LONG_LONG_LONGPTR_LONGPTR, + ATTR_NOTHROW_LIST) +DEF_GOMP_BUILTIN (BUILT_IN_GOMP_LOOP_DYNAMIC_START, + "GOMP_loop_dynamic_start", + BT_FN_BOOL_LONG_LONG_LONG_LONG_LONGPTR_LONGPTR, + ATTR_NOTHROW_LIST) +DEF_GOMP_BUILTIN (BUILT_IN_GOMP_LOOP_GUIDED_START, + "GOMP_loop_guided_start", + BT_FN_BOOL_LONG_LONG_LONG_LONG_LONGPTR_LONGPTR, + ATTR_NOTHROW_LIST) +DEF_GOMP_BUILTIN (BUILT_IN_GOMP_LOOP_RUNTIME_START, + "GOMP_loop_runtime_start", + BT_FN_BOOL_LONG_LONG_LONG_LONGPTR_LONGPTR, + ATTR_NOTHROW_LIST) +DEF_GOMP_BUILTIN (BUILT_IN_GOMP_LOOP_ORDERED_STATIC_START, + "GOMP_loop_ordered_static_start", + BT_FN_BOOL_LONG_LONG_LONG_LONG_LONGPTR_LONGPTR, + ATTR_NOTHROW_LIST) +DEF_GOMP_BUILTIN (BUILT_IN_GOMP_LOOP_ORDERED_DYNAMIC_START, + "GOMP_loop_ordered_dynamic_start", + BT_FN_BOOL_LONG_LONG_LONG_LONG_LONGPTR_LONGPTR, + ATTR_NOTHROW_LIST) +DEF_GOMP_BUILTIN (BUILT_IN_GOMP_LOOP_ORDERED_GUIDED_START, + "GOMP_loop_ordered_guided_start", + BT_FN_BOOL_LONG_LONG_LONG_LONG_LONGPTR_LONGPTR, + ATTR_NOTHROW_LIST) +DEF_GOMP_BUILTIN (BUILT_IN_GOMP_LOOP_ORDERED_RUNTIME_START, + "GOMP_loop_ordered_runtime_start", + BT_FN_BOOL_LONG_LONG_LONG_LONGPTR_LONGPTR, + ATTR_NOTHROW_LIST) +DEF_GOMP_BUILTIN (BUILT_IN_GOMP_LOOP_STATIC_NEXT, "GOMP_loop_static_next", + BT_FN_BOOL_LONGPTR_LONGPTR, ATTR_NOTHROW_LIST) +DEF_GOMP_BUILTIN (BUILT_IN_GOMP_LOOP_DYNAMIC_NEXT, "GOMP_loop_dynamic_next", + BT_FN_BOOL_LONGPTR_LONGPTR, ATTR_NOTHROW_LIST) +DEF_GOMP_BUILTIN (BUILT_IN_GOMP_LOOP_GUIDED_NEXT, "GOMP_loop_guided_next", + BT_FN_BOOL_LONGPTR_LONGPTR, ATTR_NOTHROW_LIST) +DEF_GOMP_BUILTIN (BUILT_IN_GOMP_LOOP_RUNTIME_NEXT, "GOMP_loop_runtime_next", + BT_FN_BOOL_LONGPTR_LONGPTR, ATTR_NOTHROW_LIST) +DEF_GOMP_BUILTIN (BUILT_IN_GOMP_LOOP_ORDERED_STATIC_NEXT, + "GOMP_loop_ordered_static_next", + BT_FN_BOOL_LONGPTR_LONGPTR, ATTR_NOTHROW_LIST) +DEF_GOMP_BUILTIN (BUILT_IN_GOMP_LOOP_ORDERED_DYNAMIC_NEXT, + "GOMP_loop_ordered_dynamic_next", + BT_FN_BOOL_LONGPTR_LONGPTR, ATTR_NOTHROW_LIST) +DEF_GOMP_BUILTIN (BUILT_IN_GOMP_LOOP_ORDERED_GUIDED_NEXT, + "GOMP_loop_ordered_guided_next", + BT_FN_BOOL_LONGPTR_LONGPTR, ATTR_NOTHROW_LIST) +DEF_GOMP_BUILTIN (BUILT_IN_GOMP_LOOP_ORDERED_RUNTIME_NEXT, + "GOMP_loop_ordered_runtime_next", + BT_FN_BOOL_LONGPTR_LONGPTR, ATTR_NOTHROW_LIST) +/* NOTE: Do not change the order of BUILT_IN_GOMP_PARALLEL_LOOP_*_START. + They are used in index arithmetic with enum omp_clause_schedule_kind + in omp-low.c. */ +DEF_GOMP_BUILTIN (BUILT_IN_GOMP_PARALLEL_LOOP_STATIC_START, + "GOMP_parallel_loop_static_start", + BT_FN_VOID_OMPFN_PTR_UINT_LONG_LONG_LONG_LONG, + ATTR_NOTHROW_LIST) +DEF_GOMP_BUILTIN (BUILT_IN_GOMP_PARALLEL_LOOP_DYNAMIC_START, + "GOMP_parallel_loop_dynamic_start", + BT_FN_VOID_OMPFN_PTR_UINT_LONG_LONG_LONG_LONG, + ATTR_NOTHROW_LIST) +DEF_GOMP_BUILTIN (BUILT_IN_GOMP_PARALLEL_LOOP_GUIDED_START, + "GOMP_parallel_loop_guided_start", + BT_FN_VOID_OMPFN_PTR_UINT_LONG_LONG_LONG_LONG, + ATTR_NOTHROW_LIST) +DEF_GOMP_BUILTIN (BUILT_IN_GOMP_PARALLEL_LOOP_RUNTIME_START, + "GOMP_parallel_loop_runtime_start", + BT_FN_VOID_OMPFN_PTR_UINT_LONG_LONG_LONG, + ATTR_NOTHROW_LIST) +DEF_GOMP_BUILTIN (BUILT_IN_GOMP_LOOP_END, "GOMP_loop_end", + BT_FN_VOID, ATTR_NOTHROW_LIST) +DEF_GOMP_BUILTIN (BUILT_IN_GOMP_LOOP_END_NOWAIT, "GOMP_loop_end_nowait", + BT_FN_VOID, ATTR_NOTHROW_LIST) +DEF_GOMP_BUILTIN (BUILT_IN_GOMP_ORDERED_START, "GOMP_ordered_start", + BT_FN_VOID, ATTR_NOTHROW_LIST) +DEF_GOMP_BUILTIN (BUILT_IN_GOMP_ORDERED_END, "GOMP_ordered_end", + BT_FN_VOID, ATTR_NOTHROW_LIST) +DEF_GOMP_BUILTIN (BUILT_IN_GOMP_PARALLEL_START, "GOMP_parallel_start", + BT_FN_VOID_OMPFN_PTR_UINT, ATTR_NOTHROW_LIST) +DEF_GOMP_BUILTIN (BUILT_IN_GOMP_PARALLEL_END, "GOMP_parallel_end", + BT_FN_VOID, ATTR_NOTHROW_LIST) +DEF_GOMP_BUILTIN (BUILT_IN_GOMP_SECTIONS_START, "GOMP_sections_start", + BT_FN_UINT_UINT, ATTR_NOTHROW_LIST) +DEF_GOMP_BUILTIN (BUILT_IN_GOMP_SECTIONS_NEXT, "GOMP_sections_next", + BT_FN_UINT, ATTR_NOTHROW_LIST) +DEF_GOMP_BUILTIN (BUILT_IN_GOMP_PARALLEL_SECTIONS_START, + "GOMP_parallel_sections_start", + BT_FN_VOID_OMPFN_PTR_UINT_UINT, ATTR_NOTHROW_LIST) +DEF_GOMP_BUILTIN (BUILT_IN_GOMP_SECTIONS_END, "GOMP_sections_end", + BT_FN_VOID, ATTR_NOTHROW_LIST) +DEF_GOMP_BUILTIN (BUILT_IN_GOMP_SECTIONS_END_NOWAIT, + "GOMP_sections_end_nowait", + BT_FN_VOID, ATTR_NOTHROW_LIST) +DEF_GOMP_BUILTIN (BUILT_IN_GOMP_SINGLE_START, "GOMP_single_start", + BT_FN_BOOL, ATTR_NOTHROW_LIST) +DEF_GOMP_BUILTIN (BUILT_IN_GOMP_SINGLE_COPY_START, "GOMP_single_copy_start", + BT_FN_PTR, ATTR_NOTHROW_LIST) +DEF_GOMP_BUILTIN (BUILT_IN_GOMP_SINGLE_COPY_END, "GOMP_single_copy_end", + BT_FN_VOID_PTR, ATTR_NOTHROW_LIST) diff --git a/gcc/omp-low.c b/gcc/omp-low.c new file mode 100644 index 00000000000..65907f0089c --- /dev/null +++ b/gcc/omp-low.c @@ -0,0 +1,3309 @@ +/* Lowering pass for OpenMP directives. Converts OpenMP directives + into explicit calls to the runtime library (libgomp) and data + marshalling to implement data sharing and copying clauses. + Contributed by Diego Novillo <dnovillo@redhat.com> + + Copyright (C) 2005 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 2, 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 COPYING. If not, write to the Free +Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301, USA. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "tree.h" +#include "rtl.h" +#include "tree-gimple.h" +#include "tree-inline.h" +#include "langhooks.h" +#include "diagnostic.h" +#include "tree-flow.h" +#include "timevar.h" +#include "flags.h" +#include "function.h" +#include "expr.h" +#include "toplev.h" +#include "tree-pass.h" +#include "ggc.h" +#include "except.h" + + +/* Lowering of OpenMP parallel and workshare constructs proceeds in two + phases. The first phase scans the function looking for OMP statements + and then for variables that must be replaced to satisfy data sharing + clauses. The second phase expands code for the constructs, as well as + re-gimplifing things when variables have been replaced with complex + expressions. + + Lowering of a parallel statement results in the contents of the + parallel being moved to a new function, to be invoked by the thread + library. The variable remapping process is complex enough that only + one level of parallel statement is handled at one time. If there are + nested parallel statements, those nested statements are handled when + the new function is lowered and optimized. The result is not 100% + optimal, but lexically nested parallels effectively only happens in + test suites. */ + +/* Context structure. Used to store information about each parallel + directive in the code. */ + +typedef struct omp_context +{ + /* This field must be at the beginning, as we do "inheritance": Some + callback functions for tree-inline.c (e.g., omp_copy_decl) + receive a copy_body_data pointer that is up-casted to an + omp_context pointer. */ + copy_body_data cb; + + /* The tree of contexts corresponding to the encountered constructs. */ + struct omp_context *outer; + tree stmt; + + /* Map variables to fields in a structure that allows communication + between sending and receiving threads. */ + splay_tree field_map; + tree record_type; + tree sender_decl; + tree receiver_decl; + + /* A chain of variables to add to the top-level block surrounding the + construct. In the case of a parallel, this is in the child function. */ + tree block_vars; + + /* What to do with variables with implicitly determined sharing + attributes. */ + enum omp_clause_default_kind default_kind; + + /* Nesting depth of this context. Used to beautify error messages re + invalid gotos. The outermost ctx is depth 1, with depth 0 being + reserved for the main body of the function. */ + int depth; + + /* Type of parallel construct. Used to distinguish regular parallel + regions from combined parallel+workshare directives (parallel, + parallel loop and parallel sections). */ + enum omp_parallel_type parallel_type; + + /* True if this parallel directive is nested within another. */ + bool is_nested; + + /* For combined parallel constructs, the built-in index for the + library call used to launch the children threads. */ + int parallel_start_ix; + + /* If the combined parallel directive needs additional arguments for + the call to GOMP_parallel_start_foo, they are added here. */ + tree parallel_start_additional_args; +} omp_context; + + +/* A structure describing the main elements of a parallel loop. + Mostly used to communicate between the various subroutines of + expand_omp_for_1. */ + +struct expand_omp_for_data +{ + tree v, n1, n2, step, chunk_size, for_stmt; + enum tree_code cond_code; + tree pre; + omp_context *ctx; + bool have_nowait, have_ordered; + enum omp_clause_schedule_kind sched_kind; +}; + +static splay_tree all_contexts; +static int parallel_nesting_level; + +static void scan_omp (tree *, omp_context *); +static void expand_omp (tree *, omp_context *); + + +/* Find an OpenMP clause of type KIND within CLAUSES. */ + +tree +find_omp_clause (tree clauses, enum tree_code kind) +{ + for (; clauses ; clauses = OMP_CLAUSE_CHAIN (clauses)) + if (TREE_CODE (clauses) == kind) + return clauses; + + return NULL_TREE; +} + +/* Return true if CTX is for an omp parallel. */ + +static inline bool +is_parallel_ctx (omp_context *ctx) +{ + return ctx->parallel_type != IS_NOT_PARALLEL; +} + +/* Return true if CTX is inside a combined omp parallel + workshare. */ + +static inline bool +is_in_combined_parallel_ctx (omp_context *ctx) +{ + return ctx->outer && ctx->outer->parallel_type == IS_COMBINED_PARALLEL; +} + +/* Return true if EXPR is variable sized. */ + +static inline bool +is_variable_sized (tree expr) +{ + return !TREE_CONSTANT (TYPE_SIZE_UNIT (TREE_TYPE (expr))); +} + +/* Return true if DECL is a reference type. */ + +static inline bool +is_reference (tree decl) +{ + return lang_hooks.decls.omp_privatize_by_reference (decl); +} + +/* Lookup variables in the decl or field splay trees. The "maybe" form + allows for the variable form to not have been entered, otherwise we + assert that the variable must have been entered. */ + +static inline tree +lookup_decl (tree var, omp_context *ctx) +{ + splay_tree_node n; + n = splay_tree_lookup (ctx->cb.decl_map, (splay_tree_key) var); + return (tree) n->value; +} + +static inline tree +maybe_lookup_decl (tree var, omp_context *ctx) +{ + splay_tree_node n; + n = splay_tree_lookup (ctx->cb.decl_map, (splay_tree_key) var); + return n ? (tree) n->value : NULL_TREE; +} + +static inline tree +lookup_field (tree var, omp_context *ctx) +{ + splay_tree_node n; + n = splay_tree_lookup (ctx->field_map, (splay_tree_key) var); + return (tree) n->value; +} + +static inline tree +maybe_lookup_field (tree var, omp_context *ctx) +{ + splay_tree_node n; + n = splay_tree_lookup (ctx->field_map, (splay_tree_key) var); + return n ? (tree) n->value : NULL_TREE; +} + +/* Return true if DECL should be copied by pointer. SHARED_P is true + if DECL is to be shared. */ + +static bool +use_pointer_for_field (tree decl, bool shared_p) +{ + if (AGGREGATE_TYPE_P (TREE_TYPE (decl))) + return true; + + /* We can only use copy-in/copy-out semantics for shared varibles + when we know the value is not accessible from an outer scope. */ + if (shared_p) + { + /* ??? Trivially accessible from anywhere. But why would we even + be passing an address in this case? Should we simply assert + this to be false, or should we have a cleanup pass that removes + these from the list of mappings? */ + if (TREE_STATIC (decl) || DECL_EXTERNAL (decl)) + return true; + + /* For variables with DECL_HAS_VALUE_EXPR_P set, we cannot tell + without analyzing the expression whether or not its location + is accessible to anyone else. In the case of nested parallel + regions it certainly may be. */ + if (DECL_HAS_VALUE_EXPR_P (decl)) + return true; + + /* Do not use copy-in/copy-out for variables that have their + address taken. */ + if (TREE_ADDRESSABLE (decl)) + return true; + } + + return false; +} + +/* Construct a new automatic decl similar to VAR. */ + +static tree +omp_copy_decl_2 (tree var, tree name, tree type, omp_context *ctx) +{ + tree copy = build_decl (VAR_DECL, name, type); + + TREE_ADDRESSABLE (copy) = TREE_ADDRESSABLE (var); + DECL_COMPLEX_GIMPLE_REG_P (copy) = DECL_COMPLEX_GIMPLE_REG_P (var); + DECL_ARTIFICIAL (copy) = DECL_ARTIFICIAL (var); + DECL_IGNORED_P (copy) = DECL_IGNORED_P (var); + TREE_USED (copy) = 1; + DECL_CONTEXT (copy) = ctx->cb.dst_fn; + DECL_SEEN_IN_BIND_EXPR_P (copy) = 1; + + TREE_CHAIN (copy) = ctx->block_vars; + ctx->block_vars = copy; + + return copy; +} + +static tree +omp_copy_decl_1 (tree var, omp_context *ctx) +{ + return omp_copy_decl_2 (var, DECL_NAME (var), TREE_TYPE (var), ctx); +} + +/* Build tree nodes to access the field for VAR on the receiver side. */ + +static tree +build_receiver_ref (tree var, bool by_ref, omp_context *ctx) +{ + tree x, field = lookup_field (var, ctx); + + /* If the receiver record type was remapped in the child function, + remap the field into the new record type. */ + x = maybe_lookup_field (field, ctx); + if (x != NULL) + field = x; + + x = build_fold_indirect_ref (ctx->receiver_decl); + x = build3 (COMPONENT_REF, TREE_TYPE (field), x, field, NULL); + if (by_ref) + x = build_fold_indirect_ref (x); + + return x; +} + +/* Build tree nodes to access VAR in the scope outer to CTX. In the case + of a parallel, this is a component reference; for workshare constructs + this is some variable. */ + +static tree +build_outer_var_ref (tree var, omp_context *ctx) +{ + tree x; + + if (is_global_var (var)) + x = var; + else if (is_variable_sized (var)) + { + x = TREE_OPERAND (DECL_VALUE_EXPR (var), 0); + x = build_outer_var_ref (x, ctx); + x = build_fold_indirect_ref (x); + } + else if (is_parallel_ctx (ctx)) + { + bool by_ref = use_pointer_for_field (var, false); + x = build_receiver_ref (var, by_ref, ctx); + } + else if (ctx->outer) + x = lookup_decl (var, ctx->outer); + else + gcc_unreachable (); + + if (is_reference (var)) + x = build_fold_indirect_ref (x); + + return x; +} + +/* Build tree nodes to access the field for VAR on the sender side. */ + +static tree +build_sender_ref (tree var, omp_context *ctx) +{ + tree field = lookup_field (var, ctx); + return build3 (COMPONENT_REF, TREE_TYPE (field), + ctx->sender_decl, field, NULL); +} + +/* Add a new field for VAR inside the structure CTX->SENDER_DECL. */ + +static void +install_var_field (tree var, bool by_ref, omp_context *ctx) +{ + tree field, type; + + gcc_assert (!splay_tree_lookup (ctx->field_map, (splay_tree_key) var)); + + type = TREE_TYPE (var); + if (by_ref) + type = build_pointer_type (type); + + field = build_decl (FIELD_DECL, DECL_NAME (var), type); + + /* Remember what variable this field was created for. This does have a + side effect of making dwarf2out ignore this member, so for helpful + debugging we clear it later in delete_omp_context. */ + DECL_ABSTRACT_ORIGIN (field) = var; + + insert_field_into_struct (ctx->record_type, field); + + splay_tree_insert (ctx->field_map, (splay_tree_key) var, + (splay_tree_value) field); +} + +static tree +install_var_local (tree var, omp_context *ctx) +{ + tree new_var = omp_copy_decl_1 (var, ctx); + insert_decl_map (&ctx->cb, var, new_var); + return new_var; +} + +/* Adjust the replacement for DECL in CTX for the new context. This means + copying the DECL_VALUE_EXPR, and fixing up the type. */ + +static void +fixup_remapped_decl (tree decl, omp_context *ctx, bool private_debug) +{ + tree new_decl, size; + + new_decl = lookup_decl (decl, ctx); + + TREE_TYPE (new_decl) = remap_type (TREE_TYPE (decl), &ctx->cb); + + if ((!TREE_CONSTANT (DECL_SIZE (new_decl)) || private_debug) + && DECL_HAS_VALUE_EXPR_P (decl)) + { + tree ve = DECL_VALUE_EXPR (decl); + walk_tree (&ve, copy_body_r, &ctx->cb, NULL); + SET_DECL_VALUE_EXPR (new_decl, ve); + DECL_HAS_VALUE_EXPR_P (new_decl) = 1; + } + + if (!TREE_CONSTANT (DECL_SIZE (new_decl))) + { + size = remap_decl (DECL_SIZE (decl), &ctx->cb); + if (size == error_mark_node) + size = TYPE_SIZE (TREE_TYPE (new_decl)); + DECL_SIZE (new_decl) = size; + + size = remap_decl (DECL_SIZE_UNIT (decl), &ctx->cb); + if (size == error_mark_node) + size = TYPE_SIZE_UNIT (TREE_TYPE (new_decl)); + DECL_SIZE_UNIT (new_decl) = size; + } +} + +/* The callback for remap_decl. Search all containing contexts for a + mapping of the variable; this avoids having to duplicate the splay + tree ahead of time. We know a mapping doesn't already exist in the + given context. Create new mappings to implement default semantics. */ + +static tree +omp_copy_decl (tree var, copy_body_data *cb) +{ + omp_context *ctx = (omp_context *) cb; + tree new_var; + + if (is_global_var (var) || decl_function_context (var) != ctx->cb.src_fn) + return var; + + if (TREE_CODE (var) == LABEL_DECL) + { + new_var = create_artificial_label (); + DECL_CONTEXT (new_var) = ctx->cb.dst_fn; + insert_decl_map (&ctx->cb, var, new_var); + return new_var; + } + + while (!is_parallel_ctx (ctx)) + { + ctx = ctx->outer; + if (ctx == NULL) + return var; + new_var = maybe_lookup_decl (var, ctx); + if (new_var) + return new_var; + } + + return error_mark_node; +} + +/* Create a new context, with OUTER_CTX being the surrounding context. */ + +static omp_context * +new_omp_context (tree stmt, omp_context *outer_ctx) +{ + omp_context *ctx = XCNEW (omp_context); + + splay_tree_insert (all_contexts, (splay_tree_key) stmt, + (splay_tree_value) ctx); + ctx->stmt = stmt; + + if (outer_ctx) + { + ctx->outer = outer_ctx; + ctx->cb = outer_ctx->cb; + ctx->cb.block = NULL; + ctx->depth = outer_ctx->depth + 1; + } + else + { + ctx->cb.src_fn = current_function_decl; + ctx->cb.dst_fn = current_function_decl; + ctx->cb.src_node = cgraph_node (current_function_decl); + ctx->cb.dst_node = ctx->cb.src_node; + ctx->cb.src_cfun = cfun; + ctx->cb.copy_decl = omp_copy_decl; + ctx->cb.eh_region = -1; + ctx->cb.transform_call_graph_edges = CB_CGE_MOVE; + ctx->depth = 1; + } + + ctx->cb.decl_map = splay_tree_new (splay_tree_compare_pointers, 0, 0); + + return ctx; +} + +/* Destroy a omp_context data structures. Called through the splay tree + value delete callback. */ + +static void +delete_omp_context (splay_tree_value value) +{ + omp_context *ctx = (omp_context *) value; + + splay_tree_delete (ctx->cb.decl_map); + + if (ctx->field_map) + splay_tree_delete (ctx->field_map); + + /* We hijacked DECL_ABSTRACT_ORIGIN earlier. We need to clear it before + it produces corrupt debug information. */ + if (ctx->record_type) + { + tree t; + for (t = TYPE_FIELDS (ctx->record_type); t ; t = TREE_CHAIN (t)) + DECL_ABSTRACT_ORIGIN (t) = NULL; + } + + XDELETE (ctx); +} + +/* Fix up RECEIVER_DECL with a type that has been remapped to the child + context. */ + +static void +fixup_child_record_type (omp_context *ctx) +{ + tree f, type = ctx->record_type; + + /* ??? It isn't sufficient to just call remap_type here, because + variably_modified_type_p doesn't work the way we expect for + record types. Testing each field for whether it needs remapping + and creating a new record by hand works, however. */ + for (f = TYPE_FIELDS (type); f ; f = TREE_CHAIN (f)) + if (variably_modified_type_p (TREE_TYPE (f), ctx->cb.src_fn)) + break; + if (f) + { + tree name, new_fields = NULL; + + type = lang_hooks.types.make_type (RECORD_TYPE); + name = DECL_NAME (TYPE_NAME (ctx->record_type)); + name = build_decl (TYPE_DECL, name, type); + TYPE_NAME (type) = name; + + for (f = TYPE_FIELDS (ctx->record_type); f ; f = TREE_CHAIN (f)) + { + tree new_f = copy_node (f); + DECL_CONTEXT (new_f) = type; + TREE_TYPE (new_f) = remap_type (TREE_TYPE (f), &ctx->cb); + TREE_CHAIN (new_f) = new_fields; + new_fields = new_f; + + /* Arrange to be able to look up the receiver field + given the sender field. */ + splay_tree_insert (ctx->field_map, (splay_tree_key) f, + (splay_tree_value) new_f); + } + TYPE_FIELDS (type) = nreverse (new_fields); + layout_type (type); + } + + TREE_TYPE (ctx->receiver_decl) = build_pointer_type (type); +} + +/* Instantiate decls as necessary in CTX to satisfy the data sharing + specified by CLAUSES. */ + +static void +scan_sharing_clauses (tree clauses, omp_context *ctx) +{ + tree c, decl; + bool scan_array_reductions = false; + + for (c = clauses; c; c = OMP_CLAUSE_CHAIN (c)) + { + bool by_ref; + + switch (TREE_CODE (c)) + { + case OMP_CLAUSE_PRIVATE: + decl = OMP_CLAUSE_DECL (c); + if (!is_variable_sized (decl)) + install_var_local (decl, ctx); + break; + + case OMP_CLAUSE_SHARED: + gcc_assert (is_parallel_ctx (ctx)); + decl = OMP_CLAUSE_DECL (c); + gcc_assert (!is_variable_sized (decl)); + by_ref = use_pointer_for_field (decl, true); + if (! TREE_READONLY (decl) + || TREE_ADDRESSABLE (decl) + || by_ref + || is_reference (decl)) + { + install_var_field (decl, by_ref, ctx); + install_var_local (decl, ctx); + break; + } + /* We don't need to copy const scalar vars back. */ + TREE_SET_CODE (c, OMP_CLAUSE_FIRSTPRIVATE); + goto do_private; + + case OMP_CLAUSE_LASTPRIVATE: + /* Let the corresponding firstprivate clause create + the variable. */ + if (OMP_CLAUSE_LASTPRIVATE_FIRSTPRIVATE (c)) + break; + /* FALLTHRU */ + + case OMP_CLAUSE_FIRSTPRIVATE: + case OMP_CLAUSE_REDUCTION: + decl = OMP_CLAUSE_DECL (c); + do_private: + if (is_variable_sized (decl)) + break; + else if (is_parallel_ctx (ctx)) + { + by_ref = use_pointer_for_field (decl, false); + install_var_field (decl, by_ref, ctx); + } + install_var_local (decl, ctx); + break; + + case OMP_CLAUSE_COPYPRIVATE: + if (ctx->outer) + scan_omp (&OMP_CLAUSE_DECL (c), ctx->outer); + /* FALLTHRU */ + + case OMP_CLAUSE_COPYIN: + decl = OMP_CLAUSE_DECL (c); + by_ref = use_pointer_for_field (decl, false); + install_var_field (decl, by_ref, ctx); + break; + + case OMP_CLAUSE_DEFAULT: + ctx->default_kind = OMP_CLAUSE_DEFAULT_KIND (c); + break; + + case OMP_CLAUSE_IF: + case OMP_CLAUSE_NUM_THREADS: + case OMP_CLAUSE_SCHEDULE: + if (ctx->outer) + scan_omp (&TREE_OPERAND (c, 0), ctx->outer); + break; + + case OMP_CLAUSE_NOWAIT: + case OMP_CLAUSE_ORDERED: + break; + + default: + gcc_unreachable (); + } + } + + for (c = clauses; c; c = OMP_CLAUSE_CHAIN (c)) + { + switch (TREE_CODE (c)) + { + case OMP_CLAUSE_LASTPRIVATE: + /* Let the corresponding firstprivate clause create + the variable. */ + if (OMP_CLAUSE_LASTPRIVATE_FIRSTPRIVATE (c)) + break; + /* FALLTHRU */ + + case OMP_CLAUSE_PRIVATE: + case OMP_CLAUSE_FIRSTPRIVATE: + case OMP_CLAUSE_REDUCTION: + decl = OMP_CLAUSE_DECL (c); + if (is_variable_sized (decl)) + install_var_local (decl, ctx); + fixup_remapped_decl (decl, ctx, + TREE_CODE (c) == OMP_CLAUSE_PRIVATE + && OMP_CLAUSE_PRIVATE_DEBUG (c)); + if (TREE_CODE (c) == OMP_CLAUSE_REDUCTION + && OMP_CLAUSE_REDUCTION_PLACEHOLDER (c)) + scan_array_reductions = true; + break; + + case OMP_CLAUSE_SHARED: + decl = OMP_CLAUSE_DECL (c); + fixup_remapped_decl (decl, ctx, false); + break; + + case OMP_CLAUSE_COPYPRIVATE: + case OMP_CLAUSE_COPYIN: + case OMP_CLAUSE_DEFAULT: + case OMP_CLAUSE_IF: + case OMP_CLAUSE_NUM_THREADS: + case OMP_CLAUSE_SCHEDULE: + case OMP_CLAUSE_NOWAIT: + case OMP_CLAUSE_ORDERED: + break; + + default: + gcc_unreachable (); + } + } + + if (scan_array_reductions) + for (c = clauses; c; c = OMP_CLAUSE_CHAIN (c)) + if (TREE_CODE (c) == OMP_CLAUSE_REDUCTION + && OMP_CLAUSE_REDUCTION_PLACEHOLDER (c)) + { + scan_omp (&OMP_CLAUSE_REDUCTION_INIT (c), ctx); + scan_omp (&OMP_CLAUSE_REDUCTION_MERGE (c), ctx); + } +} + +/* Create a new name for omp child function. Returns an identifier. */ + +static GTY(()) unsigned int tmp_ompfn_id_num; + +static tree +create_omp_child_function_name (void) +{ + tree name = DECL_ASSEMBLER_NAME (current_function_decl); + size_t len = IDENTIFIER_LENGTH (name); + char *tmp_name, *prefix; + + prefix = alloca (len + sizeof ("_omp_fn")); + memcpy (prefix, IDENTIFIER_POINTER (name), len); + strcpy (prefix + len, "_omp_fn"); +#ifndef NO_DOT_IN_LABEL + prefix[len] = '.'; +#elif !defined NO_DOLLAR_IN_LABEL + prefix[len] = '$'; +#endif + ASM_FORMAT_PRIVATE_NAME (tmp_name, prefix, tmp_ompfn_id_num++); + return get_identifier (tmp_name); +} + +/* Build a decl for the omp child function. It'll not contain a body + yet, just the bare decl. */ + +static void +create_omp_child_function (omp_context *ctx) +{ + tree decl, type, name, t; + + name = create_omp_child_function_name (); + type = build_function_type_list (void_type_node, ptr_type_node, NULL_TREE); + + decl = build_decl (FUNCTION_DECL, name, type); + decl = lang_hooks.decls.pushdecl (decl); + + ctx->cb.dst_fn = decl; + + TREE_STATIC (decl) = 1; + TREE_USED (decl) = 1; + DECL_ARTIFICIAL (decl) = 1; + DECL_IGNORED_P (decl) = 0; + TREE_PUBLIC (decl) = 0; + DECL_UNINLINABLE (decl) = 1; + DECL_EXTERNAL (decl) = 0; + DECL_CONTEXT (decl) = NULL_TREE; + + t = build_decl (RESULT_DECL, NULL_TREE, void_type_node); + DECL_ARTIFICIAL (t) = 1; + DECL_IGNORED_P (t) = 1; + DECL_RESULT (decl) = t; + + t = build_decl (PARM_DECL, get_identifier (".omp_data_i"), ptr_type_node); + DECL_ARTIFICIAL (t) = 1; + DECL_ARG_TYPE (t) = ptr_type_node; + DECL_CONTEXT (t) = decl; + TREE_USED (t) = 1; + DECL_ARGUMENTS (decl) = t; + ctx->receiver_decl = t; + + /* Allocate memory for the function structure. The call to + allocate_struct_function clobbers cfun, so we need to restore + it afterward. */ + allocate_struct_function (decl); + DECL_SOURCE_LOCATION (decl) = EXPR_LOCATION (ctx->stmt); + cfun->function_end_locus = EXPR_LOCATION (ctx->stmt); + cfun = ctx->cb.src_cfun; +} + +/* Given an OMP_PARALLEL statement, determine whether it is a combined + parallel+worksharing directive. This is simply done by examining + the body of the directive. If the body contains a single OMP_FOR + or a single OMP_SECTIONS then this is a combined directive. + Otherwise, it is a regular parallel directive. */ + +enum omp_parallel_type +determine_parallel_type (tree stmt) +{ + enum omp_parallel_type par_type; + tree body = BIND_EXPR_BODY (OMP_PARALLEL_BODY (stmt)); + tree t; + + par_type = IS_PARALLEL; + + t = expr_only (body); + if (t && TREE_CODE (t) == OMP_SECTIONS) + par_type = IS_COMBINED_PARALLEL; + else + par_type = IS_PARALLEL; + + return par_type; +} + + +/* Scan an OpenMP parallel directive. */ + +static void +scan_omp_parallel (tree *stmt_p, omp_context *outer_ctx) +{ + omp_context *ctx; + tree name; + + /* Ignore parallel directives with empty bodies, unless there + are copyin clauses. */ + if (optimize > 0 + && empty_body_p (OMP_PARALLEL_BODY (*stmt_p)) + && find_omp_clause (OMP_CLAUSES (*stmt_p), OMP_CLAUSE_COPYIN) == NULL) + { + *stmt_p = build_empty_stmt (); + return; + } + + ctx = new_omp_context (*stmt_p, outer_ctx); + ctx->field_map = splay_tree_new (splay_tree_compare_pointers, 0, 0); + ctx->parallel_type = determine_parallel_type (*stmt_p); + ctx->default_kind = OMP_CLAUSE_DEFAULT_SHARED; + ctx->record_type = lang_hooks.types.make_type (RECORD_TYPE); + ctx->parallel_start_ix = BUILT_IN_GOMP_PARALLEL_START; + ctx->parallel_start_additional_args = NULL_TREE; + name = create_tmp_var_name (".omp_data_s"); + name = build_decl (TYPE_DECL, name, ctx->record_type); + TYPE_NAME (ctx->record_type) = name; + create_omp_child_function (ctx); + + scan_sharing_clauses (OMP_PARALLEL_CLAUSES (*stmt_p), ctx); + scan_omp (&OMP_PARALLEL_BODY (*stmt_p), ctx); + + if (TYPE_FIELDS (ctx->record_type) == NULL) + ctx->record_type = ctx->receiver_decl = NULL; + else + { + layout_type (ctx->record_type); + fixup_child_record_type (ctx); + } +} + + +/* Extract the header elements of parallel loop FOR_STMT and store + them into *FD. */ + +static void +extract_omp_for_data (tree for_stmt, omp_context *ctx, + struct expand_omp_for_data *fd) +{ + tree t; + + fd->for_stmt = for_stmt; + fd->pre = NULL; + fd->ctx = ctx; + + t = OMP_FOR_INIT (for_stmt); + gcc_assert (TREE_CODE (t) == MODIFY_EXPR); + fd->v = TREE_OPERAND (t, 0); + gcc_assert (DECL_P (fd->v)); + gcc_assert (TREE_CODE (TREE_TYPE (fd->v)) == INTEGER_TYPE); + fd->n1 = TREE_OPERAND (t, 1); + + t = OMP_FOR_COND (for_stmt); + fd->cond_code = TREE_CODE (t); + gcc_assert (TREE_OPERAND (t, 0) == fd->v); + fd->n2 = TREE_OPERAND (t, 1); + switch (fd->cond_code) + { + case LT_EXPR: + case GT_EXPR: + break; + case LE_EXPR: + fd->n2 = fold_build2 (PLUS_EXPR, TREE_TYPE (fd->n2), fd->n2, + build_int_cst (TREE_TYPE (fd->n2), 1)); + fd->cond_code = LT_EXPR; + break; + case GE_EXPR: + fd->n2 = fold_build2 (MINUS_EXPR, TREE_TYPE (fd->n2), fd->n2, + build_int_cst (TREE_TYPE (fd->n2), 1)); + fd->cond_code = GT_EXPR; + break; + default: + gcc_unreachable (); + } + + t = OMP_FOR_INCR (fd->for_stmt); + gcc_assert (TREE_CODE (t) == MODIFY_EXPR); + gcc_assert (TREE_OPERAND (t, 0) == fd->v); + t = TREE_OPERAND (t, 1); + gcc_assert (TREE_OPERAND (t, 0) == fd->v); + switch (TREE_CODE (t)) + { + case PLUS_EXPR: + fd->step = TREE_OPERAND (t, 1); + break; + case MINUS_EXPR: + fd->step = TREE_OPERAND (t, 1); + fd->step = fold_build1 (NEGATE_EXPR, TREE_TYPE (fd->step), fd->step); + break; + default: + gcc_unreachable (); + } + + fd->have_nowait = fd->have_ordered = false; + fd->sched_kind = OMP_CLAUSE_SCHEDULE_STATIC; + fd->chunk_size = NULL_TREE; + + for (t = OMP_FOR_CLAUSES (for_stmt); t ; t = OMP_CLAUSE_CHAIN (t)) + switch (TREE_CODE (t)) + { + case OMP_CLAUSE_NOWAIT: + fd->have_nowait = true; + break; + case OMP_CLAUSE_ORDERED: + fd->have_ordered = true; + break; + case OMP_CLAUSE_SCHEDULE: + fd->sched_kind = OMP_CLAUSE_SCHEDULE_KIND (t); + fd->chunk_size = OMP_CLAUSE_SCHEDULE_CHUNK_EXPR (t); + break; + default: + break; + } + + if (fd->sched_kind == OMP_CLAUSE_SCHEDULE_RUNTIME) + gcc_assert (fd->chunk_size == NULL); + else if (fd->chunk_size == NULL) + { + /* We only need to compute a default chunk size for ordered + static loops and dynamic loops. */ + if (fd->sched_kind != OMP_CLAUSE_SCHEDULE_STATIC || fd->have_ordered) + fd->chunk_size = (fd->sched_kind == OMP_CLAUSE_SCHEDULE_STATIC) + ? integer_zero_node : integer_one_node; + } +} + + +/* Scan an OpenMP loop directive. */ + +static void +scan_omp_for (tree *stmt_p, omp_context *outer_ctx) +{ + omp_context *ctx; + tree stmt = *stmt_p; + + ctx = new_omp_context (stmt, outer_ctx); + + /* If this is a combined parallel loop directive, we need to extract + the bounds, step and chunk size for the loop so that we can build + the call to GOMP_parallel_loop_foo_start. Do this before + scanning the loop header to avoid getting the mapped variables + from the child context. */ + if (is_in_combined_parallel_ctx (ctx)) + { + struct expand_omp_for_data fd; + tree t, additional_args; + + extract_omp_for_data (stmt, ctx, &fd); + + additional_args = NULL_TREE; + if (fd.chunk_size) + { + t = fold_convert (long_integer_type_node, fd.chunk_size); + additional_args = tree_cons (NULL, t, additional_args); + } + t = fold_convert (long_integer_type_node, fd.step); + additional_args = tree_cons (NULL, t, additional_args); + t = fold_convert (long_integer_type_node, fd.n2); + additional_args = tree_cons (NULL, t, additional_args); + t = fold_convert (long_integer_type_node, fd.n1); + additional_args = tree_cons (NULL, t, additional_args); + outer_ctx->parallel_start_additional_args = additional_args; + } + + scan_sharing_clauses (OMP_FOR_CLAUSES (stmt), ctx); + + /* FIXME. When expanding into a combined parallel loop, we may not + need to map some of the variables in the loop header (in + particular, FD.N1 and FD.N2 for dynamic loops). */ + scan_omp (&OMP_FOR_PRE_BODY (stmt), ctx); + scan_omp (&OMP_FOR_INIT (stmt), ctx); + scan_omp (&OMP_FOR_COND (stmt), ctx); + scan_omp (&OMP_FOR_INCR (stmt), ctx); + scan_omp (&OMP_FOR_BODY (stmt), ctx); +} + +/* Scan an OpenMP sections directive. */ + +static void +scan_omp_sections (tree *stmt_p, omp_context *outer_ctx) +{ + tree stmt = *stmt_p; + omp_context *ctx; + + ctx = new_omp_context (stmt, outer_ctx); + scan_sharing_clauses (OMP_SECTIONS_CLAUSES (stmt), ctx); + scan_omp (&OMP_SECTIONS_BODY (stmt), ctx); +} + +/* Scan an OpenMP single directive. */ + +static void +scan_omp_single (tree *stmt_p, omp_context *outer_ctx) +{ + tree stmt = *stmt_p; + omp_context *ctx; + tree name; + + ctx = new_omp_context (stmt, outer_ctx); + ctx->field_map = splay_tree_new (splay_tree_compare_pointers, 0, 0); + ctx->record_type = lang_hooks.types.make_type (RECORD_TYPE); + name = create_tmp_var_name (".omp_copy_s"); + name = build_decl (TYPE_DECL, name, ctx->record_type); + TYPE_NAME (ctx->record_type) = name; + + scan_sharing_clauses (OMP_SINGLE_CLAUSES (stmt), ctx); + scan_omp (&OMP_SINGLE_BODY (stmt), ctx); + + if (TYPE_FIELDS (ctx->record_type) == NULL) + ctx->record_type = NULL; + else + layout_type (ctx->record_type); +} + +/* Similar, except this is either a parallel nested within another + parallel, or a workshare construct nested within a nested parallel. + In this case we want to do minimal processing, as the real work + will be done during lowering of the function generated by the + outermost parallel. + + The minimal amount of work is processing private clauses, and simply + scanning the rest. Private clauses are the only ones that don't + also imply a reference in the outer parallel. We must set up a + translation lest the default behaviour in omp_copy_decl substitute + error_mark_node. */ + +static void +scan_omp_nested (tree *stmt_p, omp_context *outer_ctx) +{ + omp_context *ctx; + tree var_sized_list = NULL; + tree c, decl, stmt = *stmt_p; + + ctx = new_omp_context (stmt, outer_ctx); + ctx->is_nested = true; + + for (c = OMP_CLAUSES (stmt); c ; c = OMP_CLAUSE_CHAIN (c)) + { + switch (TREE_CODE (c)) + { + case OMP_CLAUSE_PRIVATE: + decl = OMP_CLAUSE_DECL (c); + if (is_variable_sized (decl)) + var_sized_list = tree_cons (NULL, c, var_sized_list); + OMP_CLAUSE_DECL (c) = install_var_local (decl, ctx); + break; + + case OMP_CLAUSE_FIRSTPRIVATE: + case OMP_CLAUSE_LASTPRIVATE: + case OMP_CLAUSE_REDUCTION: + case OMP_CLAUSE_SHARED: + case OMP_CLAUSE_COPYPRIVATE: + case OMP_CLAUSE_IF: + case OMP_CLAUSE_NUM_THREADS: + case OMP_CLAUSE_SCHEDULE: + scan_omp (&TREE_OPERAND (c, 0), ctx->outer); + break; + + case OMP_CLAUSE_COPYIN: + case OMP_CLAUSE_NOWAIT: + case OMP_CLAUSE_ORDERED: + case OMP_CLAUSE_DEFAULT: + break; + + default: + gcc_unreachable (); + } + } + + /* Instantiate the VALUE_EXPR for variable sized variables. We have + to do this as a separate pass, since we need the pointer and size + decls installed first. */ + for (c = var_sized_list; c ; c = TREE_CHAIN (c)) + fixup_remapped_decl (OMP_CLAUSE_DECL (TREE_VALUE (c)), ctx, + OMP_CLAUSE_PRIVATE_DEBUG (TREE_VALUE (c))); + + scan_omp (&OMP_BODY (stmt), ctx); + + if (TREE_CODE (stmt) == OMP_FOR) + { + scan_omp (&OMP_FOR_PRE_BODY (stmt), ctx); + scan_omp (&OMP_FOR_INIT (stmt), ctx); + scan_omp (&OMP_FOR_COND (stmt), ctx); + scan_omp (&OMP_FOR_INCR (stmt), ctx); + } +} + + +/* Callback for walk_stmts used to scan for OpenMP directives at TP. */ + +static tree +scan_omp_1 (tree *tp, int *walk_subtrees, void *data) +{ + struct walk_stmt_info *wi = data; + omp_context *ctx = wi->info; + tree t = *tp; + + if (EXPR_HAS_LOCATION (t)) + input_location = EXPR_LOCATION (t); + + *walk_subtrees = 0; + switch (TREE_CODE (t)) + { + case OMP_PARALLEL: + if (++parallel_nesting_level == 1) + scan_omp_parallel (tp, ctx); + else + scan_omp_nested (tp, ctx); + parallel_nesting_level--; + break; + + case OMP_FOR: + if (parallel_nesting_level <= 1) + scan_omp_for (tp, ctx); + else + scan_omp_nested (tp, ctx); + break; + + case OMP_SECTIONS: + if (parallel_nesting_level <= 1) + scan_omp_sections (tp, ctx); + else + scan_omp_nested (tp, ctx); + break; + + case OMP_SINGLE: + if (parallel_nesting_level <= 1) + scan_omp_single (tp, ctx); + else + scan_omp_nested (tp, ctx); + break; + + case OMP_SECTION: + case OMP_MASTER: + case OMP_ORDERED: + case OMP_CRITICAL: + ctx = new_omp_context (*tp, ctx); + scan_omp (&OMP_BODY (*tp), ctx); + break; + + case BIND_EXPR: + { + tree var; + *walk_subtrees = 1; + + for (var = BIND_EXPR_VARS (t); var ; var = TREE_CHAIN (var)) + { + if (DECL_CONTEXT (var) == ctx->cb.src_fn) + DECL_CONTEXT (var) = ctx->cb.dst_fn; + insert_decl_map (&ctx->cb, var, var); + } + } + break; + + case VAR_DECL: + case PARM_DECL: + case LABEL_DECL: + if (ctx) + *tp = remap_decl (t, &ctx->cb); + break; + + default: + if (ctx && TYPE_P (t)) + *tp = remap_type (t, &ctx->cb); + else if (!DECL_P (t)) + *walk_subtrees = 1; + break; + } + + return NULL_TREE; +} + + +/* Scan all the statements starting at STMT_P. CTX contains context + information about the OpenMP directives and clauses found during + the scan. */ + +static void +scan_omp (tree *stmt_p, omp_context *ctx) +{ + location_t saved_location; + struct walk_stmt_info wi; + + memset (&wi, 0, sizeof (wi)); + wi.callback = scan_omp_1; + wi.info = ctx; + wi.want_bind_expr = (ctx != NULL); + wi.want_locations = true; + + saved_location = input_location; + walk_stmts (&wi, stmt_p); + input_location = saved_location; +} + +/* Re-gimplification and code generation routines. */ + +/* Build a call to GOMP_barrier. */ + +static void +build_omp_barrier (tree *stmt_list) +{ + tree t; + + t = built_in_decls[BUILT_IN_GOMP_BARRIER]; + t = build_function_call_expr (t, NULL); + gimplify_and_add (t, stmt_list); +} + +/* If a context was created for STMT when it was scanned, return it. */ + +static omp_context * +maybe_lookup_ctx (tree stmt) +{ + splay_tree_node n; + n = splay_tree_lookup (all_contexts, (splay_tree_key) stmt); + return n ? (omp_context *) n->value : NULL; +} + +/* Construct the initialization value for reduction CLAUSE. */ + +tree +omp_reduction_init (tree clause, tree type) +{ + switch (OMP_CLAUSE_REDUCTION_CODE (clause)) + { + case PLUS_EXPR: + case MINUS_EXPR: + case BIT_IOR_EXPR: + case BIT_XOR_EXPR: + case TRUTH_OR_EXPR: + case TRUTH_ORIF_EXPR: + case TRUTH_XOR_EXPR: + case NE_EXPR: + return fold_convert (type, integer_zero_node); + + case MULT_EXPR: + case TRUTH_AND_EXPR: + case TRUTH_ANDIF_EXPR: + case EQ_EXPR: + return fold_convert (type, integer_one_node); + + case BIT_AND_EXPR: + return fold_convert (type, integer_minus_one_node); + + case MAX_EXPR: + if (SCALAR_FLOAT_TYPE_P (type)) + { + REAL_VALUE_TYPE max, min; + if (HONOR_INFINITIES (TYPE_MODE (type))) + { + real_inf (&max); + real_arithmetic (&min, NEGATE_EXPR, &max, NULL); + } + else + real_maxval (&min, 1, TYPE_MODE (type)); + return build_real (type, min); + } + else + { + gcc_assert (INTEGRAL_TYPE_P (type)); + return TYPE_MIN_VALUE (type); + } + + case MIN_EXPR: + if (SCALAR_FLOAT_TYPE_P (type)) + { + REAL_VALUE_TYPE max; + if (HONOR_INFINITIES (TYPE_MODE (type))) + real_inf (&max); + else + real_maxval (&max, 0, TYPE_MODE (type)); + return build_real (type, max); + } + else + { + gcc_assert (INTEGRAL_TYPE_P (type)); + return TYPE_MAX_VALUE (type); + } + + default: + gcc_unreachable (); + } +} + +/* Generate code to implement the input clauses, FIRSTPRIVATE and COPYIN, + from the receiver (aka child) side and initializers for REFERENCE_TYPE + private variables. Initialization statements go in ILIST, while calls + to destructors go in DLIST. */ + +static void +expand_rec_input_clauses (tree clauses, tree *ilist, tree *dlist, + omp_context *ctx) +{ + tree_stmt_iterator diter; + tree c, dtor, copyin_seq, x, args, ptr; + bool copyin_by_ref = false; + int pass; + + *dlist = alloc_stmt_list (); + diter = tsi_start (*dlist); + copyin_seq = NULL; + + /* Do all the fixed sized types in the first pass, and the variable sized + types in the second pass. This makes sure that the scalar arguments to + the variable sized types are processed before we use them in the + variable sized operations. */ + for (pass = 0; pass < 2; ++pass) + { + for (c = clauses; c ; c = OMP_CLAUSE_CHAIN (c)) + { + enum tree_code c_kind = TREE_CODE (c); + tree var, new_var; + bool by_ref; + + switch (c_kind) + { + case OMP_CLAUSE_PRIVATE: + if (OMP_CLAUSE_PRIVATE_DEBUG (c)) + continue; + break; + case OMP_CLAUSE_SHARED: + case OMP_CLAUSE_FIRSTPRIVATE: + case OMP_CLAUSE_LASTPRIVATE: + case OMP_CLAUSE_COPYIN: + case OMP_CLAUSE_REDUCTION: + break; + default: + continue; + } + + new_var = var = OMP_CLAUSE_DECL (c); + if (c_kind != OMP_CLAUSE_COPYIN) + new_var = lookup_decl (var, ctx); + + if (c_kind == OMP_CLAUSE_SHARED || c_kind == OMP_CLAUSE_COPYIN) + { + if (pass != 0) + continue; + } + /* For variable sized types, we need to allocate the actual + storage here. Call alloca and store the result in the pointer + decl that we created elsewhere. */ + else if (is_variable_sized (var)) + { + if (pass == 0) + continue; + + ptr = DECL_VALUE_EXPR (new_var); + gcc_assert (TREE_CODE (ptr) == INDIRECT_REF); + ptr = TREE_OPERAND (ptr, 0); + gcc_assert (DECL_P (ptr)); + + x = TYPE_SIZE_UNIT (TREE_TYPE (new_var)); + args = tree_cons (NULL, x, NULL); + x = built_in_decls[BUILT_IN_ALLOCA]; + x = build_function_call_expr (x, args); + x = fold_convert (TREE_TYPE (ptr), x); + x = build2 (MODIFY_EXPR, void_type_node, ptr, x); + gimplify_and_add (x, ilist); + } + /* For references that are being privatized for Fortran, allocate + new backing storage for the new pointer variable. This allows + us to avoid changing all the code that expects a pointer to + something that expects a direct variable. Note that this + doesn't apply to C++, since reference types are disallowed in + data sharing clauses there. */ + else if (is_reference (var)) + { + if (pass == 0) + continue; + + x = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (new_var))); + if (TREE_CONSTANT (x)) + { + const char *name = NULL; + if (DECL_NAME (var)) + name = IDENTIFIER_POINTER (DECL_NAME (new_var)); + + x = create_tmp_var (TREE_TYPE (TREE_TYPE (new_var)), name); + x = build_fold_addr_expr_with_type (x, TREE_TYPE (new_var)); + } + else + { + args = tree_cons (NULL, x, NULL); + x = built_in_decls[BUILT_IN_ALLOCA]; + x = build_function_call_expr (x, args); + x = fold_convert (TREE_TYPE (new_var), x); + } + + x = build2 (MODIFY_EXPR, void_type_node, new_var, x); + gimplify_and_add (x, ilist); + + new_var = build_fold_indirect_ref (new_var); + } + else if (c_kind == OMP_CLAUSE_REDUCTION + && OMP_CLAUSE_REDUCTION_PLACEHOLDER (c)) + { + if (pass == 0) + continue; + } + else if (pass != 0) + continue; + + switch (TREE_CODE (c)) + { + case OMP_CLAUSE_SHARED: + /* Set up the DECL_VALUE_EXPR for shared variables now. This + needs to be delayed until after fixup_child_record_type so + that we get the correct type during the dereference. */ + by_ref = use_pointer_for_field (var, true); + x = build_receiver_ref (var, by_ref, ctx); + SET_DECL_VALUE_EXPR (new_var, x); + DECL_HAS_VALUE_EXPR_P (new_var) = 1; + + /* ??? If VAR is not passed by reference, and the variable + hasn't been initialized yet, then we'll get a warning for + the store into the omp_data_s structure. Ideally, we'd be + able to notice this and not store anything at all, but + we're generating code too early. Suppress the warning. */ + if (!by_ref) + TREE_NO_WARNING (var) = 1; + break; + + case OMP_CLAUSE_LASTPRIVATE: + if (OMP_CLAUSE_LASTPRIVATE_FIRSTPRIVATE (c)) + break; + /* FALLTHRU */ + + case OMP_CLAUSE_PRIVATE: + x = lang_hooks.decls.omp_clause_default_ctor (c, new_var); + if (x) + gimplify_and_add (x, ilist); + /* FALLTHRU */ + + do_dtor: + x = lang_hooks.decls.omp_clause_dtor (c, new_var); + if (x) + { + dtor = x; + gimplify_stmt (&dtor); + tsi_link_before (&diter, dtor, TSI_SAME_STMT); + } + break; + + case OMP_CLAUSE_FIRSTPRIVATE: + x = build_outer_var_ref (var, ctx); + x = lang_hooks.decls.omp_clause_copy_ctor (c, new_var, x); + gimplify_and_add (x, ilist); + goto do_dtor; + break; + + case OMP_CLAUSE_COPYIN: + by_ref = use_pointer_for_field (var, false); + x = build_receiver_ref (var, by_ref, ctx); + x = lang_hooks.decls.omp_clause_assign_op (c, new_var, x); + append_to_statement_list (x, ©in_seq); + copyin_by_ref |= by_ref; + break; + + case OMP_CLAUSE_REDUCTION: + if (OMP_CLAUSE_REDUCTION_PLACEHOLDER (c)) + { + gimplify_and_add (OMP_CLAUSE_REDUCTION_INIT (c), ilist); + OMP_CLAUSE_REDUCTION_INIT (c) = NULL; + } + else + { + x = omp_reduction_init (c, TREE_TYPE (new_var)); + gcc_assert (TREE_CODE (TREE_TYPE (new_var)) != ARRAY_TYPE); + x = build2 (MODIFY_EXPR, void_type_node, new_var, x); + gimplify_and_add (x, ilist); + } + break; + + default: + gcc_unreachable (); + } + } + } + + /* The copyin sequence is not to be executed by the main thread, since + that would result in self-copies. Perhaps not visible to scalars, + but it certainly is to C++ operator=. */ + if (copyin_seq) + { + x = built_in_decls[BUILT_IN_OMP_GET_THREAD_NUM]; + x = build_function_call_expr (x, NULL); + x = build2 (NE_EXPR, boolean_type_node, x, + build_int_cst (TREE_TYPE (x), 0)); + x = build3 (COND_EXPR, void_type_node, x, copyin_seq, NULL); + gimplify_and_add (x, ilist); + } + + /* If any copyin variable is passed by reference, we must ensure the + master thread doesn't modify it before it is copied over in all + threads. */ + if (copyin_by_ref) + build_omp_barrier (ilist); +} + +/* Generate code to implement the LASTPRIVATE clauses. This is used for + both parallel and workshare constructs. PREDICATE may be NULL if it's + always true. */ + +static void +expand_lastprivate_clauses (tree clauses, tree predicate, tree *stmt_list, + omp_context *ctx) +{ + tree sub_list, x, c; + + /* Early exit if there are no lastprivate clauses. */ + clauses = find_omp_clause (clauses, OMP_CLAUSE_LASTPRIVATE); + if (clauses == NULL) + { + /* If this was a workshare clause, see if it had been combined + with its parallel. In that case, look for the clauses on the + parallel statement itself. */ + if (is_parallel_ctx (ctx)) + return; + + ctx = ctx->outer; + if (ctx == NULL || !is_parallel_ctx (ctx)) + return; + + clauses = find_omp_clause (OMP_PARALLEL_CLAUSES (ctx->stmt), + OMP_CLAUSE_LASTPRIVATE); + if (clauses == NULL) + return; + } + + sub_list = alloc_stmt_list (); + + for (c = clauses; c ; c = OMP_CLAUSE_CHAIN (c)) + { + tree var, new_var; + + if (TREE_CODE (c) != OMP_CLAUSE_LASTPRIVATE) + continue; + + var = OMP_CLAUSE_DECL (c); + new_var = lookup_decl (var, ctx); + + x = build_outer_var_ref (var, ctx); + if (is_reference (var)) + new_var = build_fold_indirect_ref (new_var); + x = lang_hooks.decls.omp_clause_assign_op (c, x, new_var); + append_to_statement_list (x, &sub_list); + } + + if (predicate) + x = build3 (COND_EXPR, void_type_node, predicate, sub_list, NULL); + else + x = sub_list; + gimplify_and_add (x, stmt_list); +} + +/* Generate code to implement the REDUCTION clauses. */ + +static void +expand_reduction_clauses (tree clauses, tree *stmt_list, omp_context *ctx) +{ + tree sub_list = NULL, x, c; + int count = 0; + + /* First see if there is exactly one reduction clause. Use OMP_ATOMIC + update in that case, otherwise use a lock. */ + for (c = clauses; c && count < 2; c = OMP_CLAUSE_CHAIN (c)) + if (TREE_CODE (c) == OMP_CLAUSE_REDUCTION) + { + if (OMP_CLAUSE_REDUCTION_PLACEHOLDER (c)) + { + /* Never use OMP_ATOMIC for array reductions. */ + count = -1; + break; + } + count++; + } + + if (count == 0) + return; + + for (c = clauses; c ; c = OMP_CLAUSE_CHAIN (c)) + { + tree var, ref, new_var; + enum tree_code code; + + if (TREE_CODE (c) != OMP_CLAUSE_REDUCTION) + continue; + + var = OMP_CLAUSE_DECL (c); + new_var = lookup_decl (var, ctx); + if (is_reference (var)) + new_var = build_fold_indirect_ref (new_var); + ref = build_outer_var_ref (var, ctx); + code = OMP_CLAUSE_REDUCTION_CODE (c); + /* reduction(-:var) sums up the partial results, so it acts identically + to reduction(+:var). */ + if (code == MINUS_EXPR) + code = PLUS_EXPR; + + if (count == 1) + { + tree addr = build_fold_addr_expr (ref); + + addr = save_expr (addr); + ref = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (addr)), addr); + x = fold_build2 (code, TREE_TYPE (ref), ref, new_var); + x = build2 (OMP_ATOMIC, void_type_node, addr, x); + gimplify_and_add (x, stmt_list); + return; + } + + if (OMP_CLAUSE_REDUCTION_PLACEHOLDER (c)) + { + tree placeholder = OMP_CLAUSE_REDUCTION_PLACEHOLDER (c); + + if (is_reference (var)) + ref = build_fold_addr_expr (ref); + SET_DECL_VALUE_EXPR (placeholder, ref); + DECL_HAS_VALUE_EXPR_P (placeholder) = 1; + gimplify_and_add (OMP_CLAUSE_REDUCTION_MERGE (c), &sub_list); + OMP_CLAUSE_REDUCTION_MERGE (c) = NULL; + OMP_CLAUSE_REDUCTION_PLACEHOLDER (c) = NULL; + } + else + { + x = build2 (code, TREE_TYPE (ref), ref, new_var); + ref = build_outer_var_ref (var, ctx); + x = build2 (MODIFY_EXPR, void_type_node, ref, x); + append_to_statement_list (x, &sub_list); + } + } + + x = built_in_decls[BUILT_IN_GOMP_ATOMIC_START]; + x = build_function_call_expr (x, NULL); + gimplify_and_add (x, stmt_list); + + gimplify_and_add (sub_list, stmt_list); + + x = built_in_decls[BUILT_IN_GOMP_ATOMIC_END]; + x = build_function_call_expr (x, NULL); + gimplify_and_add (x, stmt_list); +} + +/* Generate code to implement the COPYPRIVATE clauses. */ + +static void +expand_copyprivate_clauses (tree clauses, tree *slist, tree *rlist, + omp_context *ctx) +{ + tree c; + + for (c = clauses; c ; c = OMP_CLAUSE_CHAIN (c)) + { + tree var, ref, x; + bool by_ref; + + if (TREE_CODE (c) != OMP_CLAUSE_COPYPRIVATE) + continue; + + var = OMP_CLAUSE_DECL (c); + by_ref = use_pointer_for_field (var, false); + + ref = build_sender_ref (var, ctx); + x = by_ref ? build_fold_addr_expr (var) : var; + x = build2 (MODIFY_EXPR, void_type_node, ref, x); + gimplify_and_add (x, slist); + + ref = build_receiver_ref (var, by_ref, ctx); + if (is_reference (var)) + { + ref = build_fold_indirect_ref (ref); + var = build_fold_indirect_ref (var); + } + x = lang_hooks.decls.omp_clause_assign_op (c, var, ref); + gimplify_and_add (x, rlist); + } +} + +/* Generate code to implement the clauses, FIRSTPRIVATE, COPYIN, LASTPRIVATE, + and REDUCTION from the sender (aka parent) side. */ + +static void +expand_send_clauses (tree clauses, tree *ilist, tree *olist, omp_context *ctx) +{ + tree c; + + for (c = clauses; c ; c = OMP_CLAUSE_CHAIN (c)) + { + tree val, ref, x; + bool by_ref, do_in = false, do_out = false; + + switch (TREE_CODE (c)) + { + case OMP_CLAUSE_FIRSTPRIVATE: + case OMP_CLAUSE_COPYIN: + case OMP_CLAUSE_LASTPRIVATE: + case OMP_CLAUSE_REDUCTION: + break; + default: + continue; + } + + val = OMP_CLAUSE_DECL (c); + if (is_variable_sized (val)) + continue; + by_ref = use_pointer_for_field (val, false); + + switch (TREE_CODE (c)) + { + case OMP_CLAUSE_FIRSTPRIVATE: + case OMP_CLAUSE_COPYIN: + do_in = true; + break; + + case OMP_CLAUSE_LASTPRIVATE: + if (by_ref || is_reference (val)) + { + if (OMP_CLAUSE_LASTPRIVATE_FIRSTPRIVATE (c)) + continue; + do_in = true; + } + else + do_out = true; + break; + + case OMP_CLAUSE_REDUCTION: + do_in = true; + do_out = !(by_ref || is_reference (val)); + break; + + default: + gcc_unreachable (); + } + + if (do_in) + { + ref = build_sender_ref (val, ctx); + x = by_ref ? build_fold_addr_expr (val) : val; + x = build2 (MODIFY_EXPR, void_type_node, ref, x); + gimplify_and_add (x, ilist); + } + if (do_out) + { + ref = build_sender_ref (val, ctx); + x = build2 (MODIFY_EXPR, void_type_node, val, ref); + gimplify_and_add (x, olist); + } + } +} + +/* Generate code to implement SHARED from the sender (aka parent) side. + This is trickier, since OMP_PARALLEL_CLAUSES doesn't list things that + got automatically shared. */ + +static void +expand_send_shared_vars (tree *ilist, tree *olist, omp_context *ctx) +{ + tree ovar, nvar, f, x; + + if (ctx->record_type == NULL) + return; + + for (f = TYPE_FIELDS (ctx->record_type); f ; f = TREE_CHAIN (f)) + { + ovar = DECL_ABSTRACT_ORIGIN (f); + nvar = maybe_lookup_decl (ovar, ctx); + if (!nvar || !DECL_HAS_VALUE_EXPR_P (nvar)) + continue; + + if (use_pointer_for_field (ovar, true)) + { + x = build_sender_ref (ovar, ctx); + ovar = build_fold_addr_expr (ovar); + x = build2 (MODIFY_EXPR, void_type_node, x, ovar); + gimplify_and_add (x, ilist); + } + else + { + x = build_sender_ref (ovar, ctx); + x = build2 (MODIFY_EXPR, void_type_node, x, ovar); + gimplify_and_add (x, ilist); + + x = build_sender_ref (ovar, ctx); + x = build2 (MODIFY_EXPR, void_type_node, ovar, x); + gimplify_and_add (x, olist); + } + } +} + +/* Build the function calls to GOMP_parallel_start etc to actually + generate the parallel operation. */ + +static void +build_parallel_call (tree clauses, tree *stmt_list, omp_context *ctx) +{ + tree t, args, val, cond, c; + + /* By default, the value of NUM_THREADS is zero (selected at run time) + and there is no conditional. */ + cond = NULL_TREE; + val = build_int_cst (unsigned_type_node, 0); + + c = find_omp_clause (clauses, OMP_CLAUSE_IF); + if (c) + cond = OMP_CLAUSE_IF_EXPR (c); + + c = find_omp_clause (clauses, OMP_CLAUSE_NUM_THREADS); + if (c) + val = OMP_CLAUSE_NUM_THREADS_EXPR (c); + + /* Ensure 'val' is of the correct type. */ + val = fold_convert (unsigned_type_node, val); + + /* If we found the clause 'if (cond)', build either + (cond != 0) or (cond ? val : 1u). */ + if (cond) + { + if (integer_zerop (val)) + val = build2 (EQ_EXPR, unsigned_type_node, cond, + build_int_cst (TREE_TYPE (cond), 0)); + else + val = build3 (COND_EXPR, unsigned_type_node, cond, val, + build_int_cst (unsigned_type_node, 1)); + } + + args = tree_cons (NULL, val, NULL); + t = ctx->sender_decl; + if (t == NULL) + t = null_pointer_node; + else + t = build_fold_addr_expr (t); + args = tree_cons (NULL, t, args); + t = build_fold_addr_expr (ctx->cb.dst_fn); + args = tree_cons (NULL, t, args); + if (ctx->parallel_start_additional_args) + args = chainon (args, ctx->parallel_start_additional_args); + t = built_in_decls[ctx->parallel_start_ix]; + t = build_function_call_expr (t, args); + gimplify_and_add (t, stmt_list); + + t = ctx->sender_decl; + if (t == NULL) + t = null_pointer_node; + else + t = build_fold_addr_expr (t); + args = tree_cons (NULL, t, NULL); + t = build_function_call_expr (ctx->cb.dst_fn, args); + gimplify_and_add (t, stmt_list); + + t = built_in_decls[BUILT_IN_GOMP_PARALLEL_END]; + t = build_function_call_expr (t, NULL); + gimplify_and_add (t, stmt_list); +} + +/* If exceptions are enabled, wrap *STMT_P in a MUST_NOT_THROW catch + handler. This prevents programs from violating the structured + block semantics with throws. */ + +static void +maybe_catch_exception (tree *stmt_p) +{ + tree f, t; + + if (!flag_exceptions) + return; + + if (lang_protect_cleanup_actions) + t = lang_protect_cleanup_actions (); + else + { + t = built_in_decls[BUILT_IN_TRAP]; + t = build_function_call_expr (t, NULL); + } + f = build2 (EH_FILTER_EXPR, void_type_node, NULL, NULL); + EH_FILTER_MUST_NOT_THROW (f) = 1; + gimplify_and_add (t, &EH_FILTER_FAILURE (f)); + + t = build2 (TRY_CATCH_EXPR, void_type_node, *stmt_p, NULL); + append_to_statement_list (f, &TREE_OPERAND (t, 1)); + + *stmt_p = NULL; + append_to_statement_list (t, stmt_p); +} + + +/* Expand the OpenMP parallel directive pointed to by STMT_P. CTX + holds context information for *STMT_P. Expansion proceeds in + two main phases: + + (1) The body of the parallel is expanded in-situ. + All the input and reduction clauses are expanded (from the + child's perspective). The body of the parallel is then + inserted as the body of CTX->CB.DST_FUN (the function spawned + to execute each child thread). + + (2) Back in the original function, the original body of the + directive is replaced with the expansion of clauses (from the + parent's perspective), and the thread library call to launch + all the children threads. */ + +static void +expand_omp_parallel (tree *stmt_p, omp_context *ctx) +{ + tree clauses, block, bind, body, olist; + + current_function_decl = ctx->cb.dst_fn; + cfun = DECL_STRUCT_FUNCTION (current_function_decl); + + push_gimplify_context (); + + /* First phase. Expand the body of the children threads, emit + receiving code for data copying clauses. */ + clauses = OMP_PARALLEL_CLAUSES (*stmt_p); + bind = OMP_PARALLEL_BODY (*stmt_p); + block = BIND_EXPR_BLOCK (bind); + body = BIND_EXPR_BODY (bind); + BIND_EXPR_BODY (bind) = alloc_stmt_list (); + + expand_rec_input_clauses (clauses, &BIND_EXPR_BODY (bind), &olist, ctx); + + expand_omp (&body, ctx); + append_to_statement_list (body, &BIND_EXPR_BODY (bind)); + + expand_reduction_clauses (clauses, &BIND_EXPR_BODY (bind), ctx); + append_to_statement_list (olist, &BIND_EXPR_BODY (bind)); + maybe_catch_exception (&BIND_EXPR_BODY (bind)); + + pop_gimplify_context (bind); + BIND_EXPR_VARS (bind) = chainon (BIND_EXPR_VARS (bind), ctx->block_vars); + BLOCK_VARS (block) = BIND_EXPR_VARS (bind); + + DECL_INITIAL (ctx->cb.dst_fn) = block; + DECL_SAVED_TREE (ctx->cb.dst_fn) = bind; + cgraph_add_new_function (ctx->cb.dst_fn); + + current_function_decl = ctx->cb.src_fn; + cfun = DECL_STRUCT_FUNCTION (current_function_decl); + + block = make_node (BLOCK); + bind = build3 (BIND_EXPR, void_type_node, NULL, NULL, block); + *stmt_p = bind; + + push_gimplify_context (); + + /* Second phase. Build the sender decl now that we're in the + correct context. Replace the original body of the directive with + sending code for data copying clauses and the parallel call to + launch children threads. */ + if (ctx->record_type) + ctx->sender_decl = create_tmp_var (ctx->record_type, ".omp_data_o"); + + olist = NULL; + expand_send_clauses (clauses, &BIND_EXPR_BODY (bind), &olist, ctx); + expand_send_shared_vars (&BIND_EXPR_BODY (bind), &olist, ctx); + build_parallel_call (clauses, &BIND_EXPR_BODY (bind), ctx); + append_to_statement_list (olist, &BIND_EXPR_BODY (bind)); + + pop_gimplify_context (bind); + BLOCK_VARS (block) = BIND_EXPR_VARS (bind); +} + +/* A subroutine of expand_omp_for_1. Generate code to emit the + for for a lastprivate clause. Given a loop control predicate + of (V cond N2), we gate the clause on (!(V cond N2)). */ + +static void +expand_omp_for_lastprivate (struct expand_omp_for_data *fd) +{ + tree clauses, cond; + enum tree_code cond_code; + + cond_code = fd->cond_code; + cond_code = cond_code == LT_EXPR ? GE_EXPR : LE_EXPR; + + /* When possible, use a strict equality expression. This can let VRP + type optimizations deduce the value and remove a copy. */ + if (host_integerp (fd->step, 0)) + { + HOST_WIDE_INT step = TREE_INT_CST_LOW (fd->step); + if (step == 1 || step == -1) + cond_code = EQ_EXPR; + } + + cond = build2 (cond_code, boolean_type_node, fd->v, fd->n2); + + clauses = OMP_FOR_CLAUSES (fd->for_stmt); + expand_lastprivate_clauses (clauses, cond, &fd->pre, fd->ctx); +} + +/* A subroutine of expand_omp_for_1. Generate code for a parallel + loop with any schedule. Given parameters: + + for (V = N1; V cond N2; V += STEP) BODY; + + where COND is "<" or ">", we generate pseudocode + + more = GOMP_loop_foo_start (N1, N2, STEP, CHUNK, &istart0, &iend0); + if (more) goto L0; else goto L2; + L0: + V = istart0; + iend = iend0; + L1: + BODY; + V += STEP; + if (V cond iend) goto L1; + more = GOMP_loop_foo_next (&istart0, &iend0); + if (more) goto L0; + lastprivate; + L2: + + If this is a combined omp parallel loop, we can skip the call + to GOMP_loop_foo_start and generate + + L0: + if (!GOMP_loop_foo_next (&istart0, &iend0)) goto L2; + V = istart0; + iend = iend0; + L1: + BODY; + V += STEP; + if (V cond iend) goto L1; + goto L0; + L2: + lastprivate; +*/ + +static void +expand_omp_for_generic (struct expand_omp_for_data *fd, + enum built_in_function start_fn, + enum built_in_function next_fn) +{ + tree l0, l1, l2; + tree type, istart0, iend0, iend; + tree t, args; + bool in_combined_parallel = is_in_combined_parallel_ctx (fd->ctx); + + type = TREE_TYPE (fd->v); + + istart0 = create_tmp_var (long_integer_type_node, ".istart0"); + iend0 = create_tmp_var (long_integer_type_node, ".iend0"); + + l0 = create_artificial_label (); + l1 = create_artificial_label (); + l2 = create_artificial_label (); + iend = create_tmp_var (type, NULL); + + /* If this is a combined parallel loop, skip the call to + GOMP_loop_foo_start and call GOMP_loop_foo_next directly. */ + if (in_combined_parallel) + { + t = build1 (LABEL_EXPR, void_type_node, l0); + gimplify_and_add (t, &fd->pre); + t = build_fold_addr_expr (iend0); + args = tree_cons (NULL, t, NULL); + t = build_fold_addr_expr (istart0); + args = tree_cons (NULL, t, args); + t = build_function_call_expr (built_in_decls[next_fn], args); + t = build1 (TRUTH_NOT_EXPR, TREE_TYPE (t), t); + t = build3 (COND_EXPR, void_type_node, t, build_and_jump (&l2), NULL); + gimplify_and_add (t, &fd->pre); + } + else + { + t = build_fold_addr_expr (iend0); + args = tree_cons (NULL, t, NULL); + t = build_fold_addr_expr (istart0); + args = tree_cons (NULL, t, args); + if (fd->chunk_size) + { + t = fold_convert (long_integer_type_node, fd->chunk_size); + args = tree_cons (NULL, t, args); + } + t = fold_convert (long_integer_type_node, fd->step); + args = tree_cons (NULL, t, args); + t = fold_convert (long_integer_type_node, fd->n2); + args = tree_cons (NULL, t, args); + t = fold_convert (long_integer_type_node, fd->n1); + args = tree_cons (NULL, t, args); + t = build_function_call_expr (built_in_decls[start_fn], args); + t = build3 (COND_EXPR, void_type_node, t, + build_and_jump (&l0), build_and_jump (&l2)); + gimplify_and_add (t, &fd->pre); + t = build1 (LABEL_EXPR, void_type_node, l0); + gimplify_and_add (t, &fd->pre); + } + + t = fold_convert (type, istart0); + t = build2 (MODIFY_EXPR, void_type_node, fd->v, t); + gimplify_and_add (t, &fd->pre); + + t = fold_convert (type, iend0); + t = build2 (MODIFY_EXPR, void_type_node, iend, t); + gimplify_and_add (t, &fd->pre); + + t = build1 (LABEL_EXPR, void_type_node, l1); + gimplify_and_add (t, &fd->pre); + + append_to_statement_list (OMP_FOR_BODY (fd->for_stmt), &fd->pre); + + t = build2 (PLUS_EXPR, type, fd->v, fd->step); + t = build2 (MODIFY_EXPR, void_type_node, fd->v, t); + gimplify_and_add (t, &fd->pre); + + t = build2 (fd->cond_code, boolean_type_node, fd->v, iend); + t = build3 (COND_EXPR, void_type_node, t, build_and_jump (&l1), NULL); + gimplify_and_add (t, &fd->pre); + + /* If emitting a combined parallel loop, we only need to emit a jump + back to L0 to call GOMP_loop_foo_next again. */ + if (in_combined_parallel) + { + t = build_and_jump (&l0); + gimplify_and_add (t, &fd->pre); + } + else + { + t = build_fold_addr_expr (iend0); + args = tree_cons (NULL, t, NULL); + t = build_fold_addr_expr (istart0); + args = tree_cons (NULL, t, args); + t = build_function_call_expr (built_in_decls[next_fn], args); + t = build3 (COND_EXPR, void_type_node, t, build_and_jump (&l0), NULL); + gimplify_and_add (t, &fd->pre); + } + + expand_omp_for_lastprivate (fd); + + t = build1 (LABEL_EXPR, void_type_node, l2); + gimplify_and_add (t, &fd->pre); +} + + +/* A subroutine of expand_omp_for_1. Generate code for a parallel + loop with static schedule and no specified chunk size. Given parameters: + + for (V = N1; V cond N2; V += STEP) BODY; + + where COND is "<" or ">", we generate pseudocode + + if (cond is <) + adj = STEP - 1; + else + adj = STEP + 1; + n = (adj + N2 - N1) / STEP; + q = n / nthreads; + q += (q * nthreads != n); + s0 = q * threadid; + e0 = min(s0 + q, n); + if (s0 >= e0) goto L2; else goto L0; + L0: + V = s0 * STEP + N1; + e = e0 * STEP + N1; + L1: + BODY; + V += STEP; + if (V cond e) goto L1; + lastprivate; + L2: +*/ + +static void +expand_omp_for_static_nochunk (struct expand_omp_for_data *fd) +{ + tree l0, l1, l2, n, q, s0, e0, e, t, nthreads, threadid; + tree type, utype; + + l0 = create_artificial_label (); + l1 = create_artificial_label (); + l2 = create_artificial_label (); + + type = TREE_TYPE (fd->v); + utype = lang_hooks.types.unsigned_type (type); + + t = built_in_decls[BUILT_IN_OMP_GET_NUM_THREADS]; + t = build_function_call_expr (t, NULL); + t = fold_convert (utype, t); + nthreads = get_formal_tmp_var (t, &fd->pre); + + t = built_in_decls[BUILT_IN_OMP_GET_THREAD_NUM]; + t = build_function_call_expr (t, NULL); + t = fold_convert (utype, t); + threadid = get_formal_tmp_var (t, &fd->pre); + + fd->n1 = fold_convert (type, fd->n1); + if (!is_gimple_val (fd->n1)) + fd->n1 = get_formal_tmp_var (fd->n1, &fd->pre); + + fd->n2 = fold_convert (type, fd->n2); + if (!is_gimple_val (fd->n2)) + fd->n2 = get_formal_tmp_var (fd->n2, &fd->pre); + + fd->step = fold_convert (type, fd->step); + if (!is_gimple_val (fd->step)) + fd->step = get_formal_tmp_var (fd->step, &fd->pre); + + t = build_int_cst (type, (fd->cond_code == LT_EXPR ? -1 : 1)); + t = fold_build2 (PLUS_EXPR, type, fd->step, t); + t = fold_build2 (PLUS_EXPR, type, t, fd->n2); + t = fold_build2 (MINUS_EXPR, type, t, fd->n1); + t = fold_build2 (TRUNC_DIV_EXPR, type, t, fd->step); + t = fold_convert (utype, t); + if (is_gimple_val (t)) + n = t; + else + n = get_formal_tmp_var (t, &fd->pre); + + t = build2 (TRUNC_DIV_EXPR, utype, n, nthreads); + q = get_formal_tmp_var (t, &fd->pre); + + t = build2 (MULT_EXPR, utype, q, nthreads); + t = build2 (NE_EXPR, utype, t, n); + t = build2 (PLUS_EXPR, utype, q, t); + q = get_formal_tmp_var (t, &fd->pre); + + t = build2 (MULT_EXPR, utype, q, threadid); + s0 = get_formal_tmp_var (t, &fd->pre); + + t = build2 (PLUS_EXPR, utype, s0, q); + t = build2 (MIN_EXPR, utype, t, n); + e0 = get_formal_tmp_var (t, &fd->pre); + + t = build2 (GE_EXPR, boolean_type_node, s0, e0); + t = build3 (COND_EXPR, void_type_node, t, + build_and_jump (&l2), build_and_jump (&l0)); + gimplify_and_add (t, &fd->pre); + + t = build1 (LABEL_EXPR, void_type_node, l0); + gimplify_and_add (t, &fd->pre); + + t = fold_convert (type, s0); + t = build2 (MULT_EXPR, type, t, fd->step); + t = build2 (PLUS_EXPR, type, t, fd->n1); + t = build2 (MODIFY_EXPR, void_type_node, fd->v, t); + gimplify_and_add (t, &fd->pre); + + t = fold_convert (type, e0); + t = build2 (MULT_EXPR, type, t, fd->step); + t = build2 (PLUS_EXPR, type, t, fd->n1); + e = get_formal_tmp_var (t, &fd->pre); + + t = build1 (LABEL_EXPR, void_type_node, l1); + gimplify_and_add (t, &fd->pre); + + append_to_statement_list (OMP_FOR_BODY (fd->for_stmt), &fd->pre); + + t = build2 (PLUS_EXPR, type, fd->v, fd->step); + t = build2 (MODIFY_EXPR, void_type_node, fd->v, t); + gimplify_and_add (t, &fd->pre); + + t = build2 (fd->cond_code, boolean_type_node, fd->v, e); + t = build3 (COND_EXPR, void_type_node, t, build_and_jump (&l1), NULL); + gimplify_and_add (t, &fd->pre); + + expand_omp_for_lastprivate (fd); + + t = build1 (LABEL_EXPR, void_type_node, l2); + gimplify_and_add (t, &fd->pre); +} + +/* A subroutine of expand_omp_for_1. Generate code for a parallel + loop with static schedule and a specified chunk size. Given parameters: + + for (V = N1; V cond N2; V += STEP) BODY; + + where COND is "<" or ">", we generate pseudocode + + if (cond is <) + adj = STEP - 1; + else + adj = STEP + 1; + n = (adj + N2 - N1) / STEP; + trip = 0; + L0: + s0 = (trip * nthreads + threadid) * CHUNK; + e0 = min(s0 + CHUNK, n); + if (s0 < n) goto L1; else goto L4; + L1: + V = s0 * STEP + N1; + e = e0 * STEP + N1; + L2: + BODY; + V += STEP; + if (V cond e) goto L2; else goto L3; + L3: + trip += 1; + goto L0; + L4: + if (trip == 0) goto L5; + lastprivate; + L5: +*/ + +static void +expand_omp_for_static_chunk (struct expand_omp_for_data *fd) +{ + tree l0, l1, l2, l3, l4, l5, n, s0, e0, e, t; + tree trip, nthreads, threadid; + tree type, utype; + + l0 = create_artificial_label (); + l1 = create_artificial_label (); + l2 = create_artificial_label (); + l3 = create_artificial_label (); + l4 = create_artificial_label (); + l5 = create_artificial_label (); + + type = TREE_TYPE (fd->v); + utype = lang_hooks.types.unsigned_type (type); + + t = built_in_decls[BUILT_IN_OMP_GET_NUM_THREADS]; + t = build_function_call_expr (t, NULL); + t = fold_convert (utype, t); + nthreads = get_formal_tmp_var (t, &fd->pre); + + t = built_in_decls[BUILT_IN_OMP_GET_THREAD_NUM]; + t = build_function_call_expr (t, NULL); + t = fold_convert (utype, t); + threadid = get_formal_tmp_var (t, &fd->pre); + + fd->n1 = fold_convert (type, fd->n1); + if (!is_gimple_val (fd->n1)) + fd->n1 = get_formal_tmp_var (fd->n1, &fd->pre); + + fd->n2 = fold_convert (type, fd->n2); + if (!is_gimple_val (fd->n2)) + fd->n2 = get_formal_tmp_var (fd->n2, &fd->pre); + + fd->step = fold_convert (type, fd->step); + if (!is_gimple_val (fd->step)) + fd->step = get_formal_tmp_var (fd->step, &fd->pre); + + fd->chunk_size = fold_convert (utype, fd->chunk_size); + if (!is_gimple_val (fd->chunk_size)) + fd->chunk_size = get_formal_tmp_var (fd->chunk_size, &fd->pre); + + t = build_int_cst (type, (fd->cond_code == LT_EXPR ? -1 : 1)); + t = fold_build2 (PLUS_EXPR, type, fd->step, t); + t = fold_build2 (PLUS_EXPR, type, t, fd->n2); + t = fold_build2 (MINUS_EXPR, type, t, fd->n1); + t = fold_build2 (TRUNC_DIV_EXPR, type, t, fd->step); + t = fold_convert (utype, t); + if (is_gimple_val (t)) + n = t; + else + n = get_formal_tmp_var (t, &fd->pre); + + t = build_int_cst (utype, 0); + trip = get_initialized_tmp_var (t, &fd->pre, NULL); + + t = build1 (LABEL_EXPR, void_type_node, l0); + gimplify_and_add (t, &fd->pre); + + t = build2 (MULT_EXPR, utype, trip, nthreads); + t = build2 (PLUS_EXPR, utype, t, threadid); + t = build2 (MULT_EXPR, utype, t, fd->chunk_size); + s0 = get_formal_tmp_var (t, &fd->pre); + + t = build2 (PLUS_EXPR, utype, s0, fd->chunk_size); + t = build2 (MIN_EXPR, utype, t, n); + e0 = get_formal_tmp_var (t, &fd->pre); + + t = build2 (LT_EXPR, boolean_type_node, s0, n); + t = build3 (COND_EXPR, void_type_node, t, + build_and_jump (&l1), build_and_jump (&l4)); + gimplify_and_add (t, &fd->pre); + + t = build1 (LABEL_EXPR, void_type_node, l1); + gimplify_and_add (t, &fd->pre); + + t = fold_convert (type, s0); + t = build2 (MULT_EXPR, type, t, fd->step); + t = build2 (PLUS_EXPR, type, t, fd->n1); + t = build2 (MODIFY_EXPR, void_type_node, fd->v, t); + gimplify_and_add (t, &fd->pre); + + t = fold_convert (type, e0); + t = build2 (MULT_EXPR, type, t, fd->step); + t = build2 (PLUS_EXPR, type, t, fd->n1); + e = get_formal_tmp_var (t, &fd->pre); + + t = build1 (LABEL_EXPR, void_type_node, l2); + gimplify_and_add (t, &fd->pre); + + append_to_statement_list (OMP_FOR_BODY (fd->for_stmt), &fd->pre); + + t = build2 (PLUS_EXPR, type, fd->v, fd->step); + t = build2 (MODIFY_EXPR, void_type_node, fd->v, t); + gimplify_and_add (t, &fd->pre); + + t = build2 (fd->cond_code, boolean_type_node, fd->v, e); + t = build3 (COND_EXPR, void_type_node, t, + build_and_jump (&l2), build_and_jump (&l3)); + gimplify_and_add (t, &fd->pre); + + t = build1 (LABEL_EXPR, void_type_node, l3); + gimplify_and_add (t, &fd->pre); + + t = build_int_cst (utype, 1); + t = build2 (PLUS_EXPR, utype, trip, t); + t = build2 (MODIFY_EXPR, void_type_node, trip, t); + gimplify_and_add (t, &fd->pre); + + t = build1 (GOTO_EXPR, void_type_node, l0); + gimplify_and_add (t, &fd->pre); + + t = build1 (LABEL_EXPR, void_type_node, l4); + gimplify_and_add (t, &fd->pre); + + t = build_int_cst (utype, 0); + t = build2 (EQ_EXPR, boolean_type_node, trip, t); + t = build3 (COND_EXPR, void_type_node, t, build_and_jump (&l5), NULL); + + expand_omp_for_lastprivate (fd); + + t = build1 (LABEL_EXPR, void_type_node, l5); + gimplify_and_add (t, &fd->pre); +} + +/* A subroutine of expand_omp_for. Expand the logic of the loop itself. */ + +static tree +expand_omp_for_1 (tree *stmt_p, omp_context *ctx) +{ + struct expand_omp_for_data fd; + tree dlist; + + extract_omp_for_data (*stmt_p, ctx, &fd); + + expand_rec_input_clauses (OMP_FOR_CLAUSES (fd.for_stmt), + &fd.pre, &dlist, ctx); + + expand_omp (&OMP_FOR_PRE_BODY (fd.for_stmt), ctx); + append_to_statement_list (OMP_FOR_PRE_BODY (fd.for_stmt), &fd.pre); + + if (fd.sched_kind == OMP_CLAUSE_SCHEDULE_STATIC && !fd.have_ordered) + { + if (fd.chunk_size == NULL) + expand_omp_for_static_nochunk (&fd); + else + expand_omp_for_static_chunk (&fd); + } + else + { + int fn_index; + + fn_index = fd.sched_kind + fd.have_ordered * 4; + + expand_omp_for_generic (&fd, BUILT_IN_GOMP_LOOP_STATIC_START + fn_index, + BUILT_IN_GOMP_LOOP_STATIC_NEXT + fn_index); + } + + expand_reduction_clauses (OMP_FOR_CLAUSES (fd.for_stmt), &fd.pre, ctx); + append_to_statement_list (dlist, &fd.pre); + + /* If this parallel loop was part of a combined parallel loop + directive, inform the parent parallel what flavour of + GOMP_parallel_loop_XXX_start to use. */ + if (is_in_combined_parallel_ctx (ctx)) + { + int start_ix = BUILT_IN_GOMP_PARALLEL_LOOP_STATIC_START + fd.sched_kind; + ctx->outer->parallel_start_ix = start_ix; + } + else if (!fd.have_nowait) + build_omp_barrier (&fd.pre); + + return fd.pre; +} + +/* Expand code for an OpenMP loop directive. */ + +static void +expand_omp_for (tree *stmt_p, omp_context *ctx) +{ + tree bind, block, stmt_list; + + push_gimplify_context (); + + expand_omp (&OMP_FOR_BODY (*stmt_p), ctx); + + stmt_list = expand_omp_for_1 (stmt_p, ctx); + block = make_node (BLOCK); + bind = build3 (BIND_EXPR, void_type_node, NULL, stmt_list, block); + maybe_catch_exception (&BIND_EXPR_BODY (bind)); + *stmt_p = bind; + + pop_gimplify_context (bind); + BIND_EXPR_VARS (bind) = chainon (BIND_EXPR_VARS (bind), ctx->block_vars); + BLOCK_VARS (block) = BIND_EXPR_VARS (bind); +} + +/* Expand code for an OpenMP sections directive. In pseudo code, we generate + + firstprivate; + v = GOMP_sections_start (n); + L0: + switch (v) + { + case 0: + goto L2; + case 1: + section 1; + goto L1; + case 2: + ... + case n: + ... + lastprivate; + default: + abort (); + } + L1: + v = GOMP_sections_next (); + goto L0; + L2: + reduction; + + If this is a combined parallel sections skip the call to + GOMP_sections_start and emit the call to GOMP_sections_next right + before the switch(). */ + +static void +expand_omp_sections (tree *stmt_p, omp_context *ctx) +{ + tree sec_stmt, label_vec, bind, block, stmt_list, l0, l1, l2, t, u, v; + tree_stmt_iterator tsi; + tree dlist; + unsigned i, len; + bool in_combined_parallel = is_in_combined_parallel_ctx (ctx); + + sec_stmt = *stmt_p; + stmt_list = NULL; + + push_gimplify_context (); + + expand_rec_input_clauses (OMP_SECTIONS_CLAUSES (sec_stmt), + &stmt_list, &dlist, ctx); + + tsi = tsi_start (OMP_SECTIONS_BODY (sec_stmt)); + for (len = 0; !tsi_end_p (tsi); len++, tsi_next (&tsi)) + continue; + + l0 = create_artificial_label (); + l1 = create_artificial_label (); + l2 = create_artificial_label (); + v = create_tmp_var (unsigned_type_node, ".section"); + label_vec = make_tree_vec (len + 2); + + t = build_int_cst (unsigned_type_node, len); + t = tree_cons (NULL, t, NULL); + + if (in_combined_parallel) + { + /* Nothing to do. Just inform our parent of the additional + arguments to invoke GOMP_parallel_sections_start. */ + ctx->outer->parallel_start_ix = BUILT_IN_GOMP_PARALLEL_SECTIONS_START; + ctx->outer->parallel_start_additional_args = t; + } + else + { + u = built_in_decls[BUILT_IN_GOMP_SECTIONS_START]; + t = build_function_call_expr (u, t); + t = build2 (MODIFY_EXPR, void_type_node, v, t); + gimplify_and_add (t, &stmt_list); + } + + t = build1 (LABEL_EXPR, void_type_node, l0); + gimplify_and_add (t, &stmt_list); + + if (in_combined_parallel) + { + /* Combined parallel sections need the call to GOMP_sections_next + before the switch(). */ + t = built_in_decls[BUILT_IN_GOMP_SECTIONS_NEXT]; + t = build_function_call_expr (t, NULL); + t = build2 (MODIFY_EXPR, void_type_node, v, t); + gimplify_and_add (t, &stmt_list); + } + + t = build3 (SWITCH_EXPR, void_type_node, v, NULL, label_vec); + gimplify_and_add (t, &stmt_list); + + t = build3 (CASE_LABEL_EXPR, void_type_node, + build_int_cst (unsigned_type_node, 0), NULL, l2); + TREE_VEC_ELT (label_vec, 0) = t; + + tsi = tsi_start (OMP_SECTIONS_BODY (sec_stmt)); + for (i = 0; i < len; i++, tsi_next (&tsi)) + { + omp_context *sctx; + + t = create_artificial_label (); + u = build_int_cst (unsigned_type_node, i + 1); + u = build3 (CASE_LABEL_EXPR, void_type_node, u, NULL, t); + TREE_VEC_ELT (label_vec, i + 1) = u; + t = build1 (LABEL_EXPR, void_type_node, t); + gimplify_and_add (t, &stmt_list); + + t = tsi_stmt (tsi); + sctx = maybe_lookup_ctx (t); + gcc_assert (sctx); + expand_omp (&OMP_SECTION_BODY (t), sctx); + append_to_statement_list (OMP_SECTION_BODY (t), &stmt_list); + + if (i == len - 1) + expand_lastprivate_clauses (OMP_SECTIONS_CLAUSES (sec_stmt), + NULL, &stmt_list, ctx); + + t = build1 (GOTO_EXPR, void_type_node, l1); + gimplify_and_add (t, &stmt_list); + } + + t = create_artificial_label (); + u = build3 (CASE_LABEL_EXPR, void_type_node, NULL, NULL, t); + TREE_VEC_ELT (label_vec, len + 1) = u; + t = build1 (LABEL_EXPR, void_type_node, t); + gimplify_and_add (t, &stmt_list); + + t = built_in_decls[BUILT_IN_TRAP]; + t = build_function_call_expr (t, NULL); + gimplify_and_add (t, &stmt_list); + + t = build1 (LABEL_EXPR, void_type_node, l1); + gimplify_and_add (t, &stmt_list); + + if (!in_combined_parallel) + { + t = built_in_decls[BUILT_IN_GOMP_SECTIONS_NEXT]; + t = build_function_call_expr (t, NULL); + t = build2 (MODIFY_EXPR, void_type_node, v, t); + gimplify_and_add (t, &stmt_list); + } + + t = build1 (GOTO_EXPR, void_type_node, l0); + gimplify_and_add (t, &stmt_list); + + t = build1 (LABEL_EXPR, void_type_node, l2); + gimplify_and_add (t, &stmt_list); + + expand_reduction_clauses (OMP_SECTIONS_CLAUSES (sec_stmt), &stmt_list, ctx); + append_to_statement_list (dlist, &stmt_list); + + /* Unless there's a nowait clause, add a barrier afterward. */ + if (!find_omp_clause (OMP_SECTIONS_CLAUSES (sec_stmt), OMP_CLAUSE_NOWAIT)) + build_omp_barrier (&stmt_list); + + block = make_node (BLOCK); + bind = build3 (BIND_EXPR, void_type_node, NULL, stmt_list, block); + maybe_catch_exception (&BIND_EXPR_BODY (bind)); + *stmt_p = bind; + + pop_gimplify_context (bind); + BIND_EXPR_VARS (bind) = chainon (BIND_EXPR_VARS (bind), ctx->block_vars); + BLOCK_VARS (block) = BIND_EXPR_VARS (bind); +} + + +/* A subroutine of expand_omp_single. Expand the simple form of + an OMP_SINGLE, without a copyprivate clause: + + if (GOMP_single_start ()) + BODY; + [ GOMP_barrier (); ] -> unless 'nowait' is present. +*/ + +static void +expand_omp_single_simple (tree single_stmt, tree *pre_p) +{ + tree t; + + t = built_in_decls[BUILT_IN_GOMP_SINGLE_START]; + t = build_function_call_expr (t, NULL); + t = build3 (COND_EXPR, void_type_node, t, + OMP_SINGLE_BODY (single_stmt), NULL); + gimplify_and_add (t, pre_p); + + if (!find_omp_clause (OMP_SINGLE_CLAUSES (single_stmt), OMP_CLAUSE_NOWAIT)) + build_omp_barrier (pre_p); +} + +/* A subroutine of expand_omp_single. Expand the simple form of + an OMP_SINGLE, with a copyprivate clause: + + #pragma omp single copyprivate (a, b, c) + + Create a new structure to hold copies of 'a', 'b' and 'c' and emit: + + { + if ((copyout_p = GOMP_single_copy_start ()) == NULL) + { + BODY; + copyout.a = a; + copyout.b = b; + copyout.c = c; + GOMP_single_copy_end (©out); + } + else + { + a = copyout_p->a; + b = copyout_p->b; + c = copyout_p->c; + } + GOMP_barrier (); + } +*/ + +static void +expand_omp_single_copy (tree single_stmt, tree *pre_p, omp_context *ctx) +{ + tree ptr_type, t, args, l0, l1, l2, copyin_seq; + + ctx->sender_decl = create_tmp_var (ctx->record_type, ".omp_copy_o"); + + ptr_type = build_pointer_type (ctx->record_type); + ctx->receiver_decl = create_tmp_var (ptr_type, ".omp_copy_i"); + + l0 = create_artificial_label (); + l1 = create_artificial_label (); + l2 = create_artificial_label (); + + t = built_in_decls[BUILT_IN_GOMP_SINGLE_COPY_START]; + t = build_function_call_expr (t, NULL); + t = fold_convert (ptr_type, t); + t = build2 (MODIFY_EXPR, void_type_node, ctx->receiver_decl, t); + gimplify_and_add (t, pre_p); + + t = build2 (EQ_EXPR, boolean_type_node, ctx->receiver_decl, + build_int_cst (ptr_type, 0)); + t = build3 (COND_EXPR, void_type_node, t, + build_and_jump (&l0), build_and_jump (&l1)); + gimplify_and_add (t, pre_p); + + t = build1 (LABEL_EXPR, void_type_node, l0); + gimplify_and_add (t, pre_p); + + append_to_statement_list (OMP_SINGLE_BODY (single_stmt), pre_p); + + copyin_seq = NULL; + expand_copyprivate_clauses (OMP_SINGLE_CLAUSES (single_stmt), pre_p, + ©in_seq, ctx); + + t = build_fold_addr_expr (ctx->sender_decl); + args = tree_cons (NULL, t, NULL); + t = built_in_decls[BUILT_IN_GOMP_SINGLE_COPY_END]; + t = build_function_call_expr (t, args); + gimplify_and_add (t, pre_p); + + t = build_and_jump (&l2); + gimplify_and_add (t, pre_p); + + t = build1 (LABEL_EXPR, void_type_node, l1); + gimplify_and_add (t, pre_p); + + append_to_statement_list (copyin_seq, pre_p); + + t = build1 (LABEL_EXPR, void_type_node, l2); + gimplify_and_add (t, pre_p); + + build_omp_barrier (pre_p); +} + +/* Expand code for an OpenMP single directive. */ + +static void +expand_omp_single (tree *stmt_p, omp_context *ctx) +{ + tree bind, block, single_stmt = *stmt_p, dlist; + + push_gimplify_context (); + + block = make_node (BLOCK); + bind = build3 (BIND_EXPR, void_type_node, NULL, NULL, block); + *stmt_p = bind; + + expand_rec_input_clauses (OMP_SINGLE_CLAUSES (single_stmt), + &BIND_EXPR_BODY (bind), &dlist, ctx); + + expand_omp (&OMP_SINGLE_BODY (single_stmt), ctx); + + if (ctx->record_type) + expand_omp_single_copy (single_stmt, &BIND_EXPR_BODY (bind), ctx); + else + expand_omp_single_simple (single_stmt, &BIND_EXPR_BODY (bind)); + + append_to_statement_list (dlist, &BIND_EXPR_BODY (bind)); + + maybe_catch_exception (&BIND_EXPR_BODY (bind)); + pop_gimplify_context (bind); + BIND_EXPR_VARS (bind) = chainon (BIND_EXPR_VARS (bind), ctx->block_vars); + BLOCK_VARS (block) = BIND_EXPR_VARS (bind); +} + +/* Expand code for an OpenMP master directive. */ + +static void +expand_omp_master (tree *stmt_p, omp_context *ctx) +{ + tree bind, block, stmt = *stmt_p, lab = NULL, x; + + push_gimplify_context (); + + block = make_node (BLOCK); + bind = build3 (BIND_EXPR, void_type_node, NULL, NULL, block); + *stmt_p = bind; + + x = built_in_decls[BUILT_IN_OMP_GET_THREAD_NUM]; + x = build_function_call_expr (x, NULL); + x = build2 (EQ_EXPR, boolean_type_node, x, integer_zero_node); + x = build3 (COND_EXPR, void_type_node, x, NULL, build_and_jump (&lab)); + gimplify_and_add (x, &BIND_EXPR_BODY (bind)); + + expand_omp (&OMP_MASTER_BODY (stmt), ctx); + append_to_statement_list (OMP_MASTER_BODY (stmt), &BIND_EXPR_BODY (bind)); + + x = build1 (LABEL_EXPR, void_type_node, lab); + gimplify_and_add (x, &BIND_EXPR_BODY (bind)); + + maybe_catch_exception (&BIND_EXPR_BODY (bind)); + pop_gimplify_context (bind); + BIND_EXPR_VARS (bind) = chainon (BIND_EXPR_VARS (bind), ctx->block_vars); + BLOCK_VARS (block) = BIND_EXPR_VARS (bind); +} + +/* Expand code for an OpenMP ordered directive. */ + +static void +expand_omp_ordered (tree *stmt_p, omp_context *ctx) +{ + tree bind, block, stmt = *stmt_p, x; + + push_gimplify_context (); + + block = make_node (BLOCK); + bind = build3 (BIND_EXPR, void_type_node, NULL, NULL, block); + *stmt_p = bind; + + x = built_in_decls[BUILT_IN_GOMP_ORDERED_START]; + x = build_function_call_expr (x, NULL); + gimplify_and_add (x, &BIND_EXPR_BODY (bind)); + + expand_omp (&OMP_ORDERED_BODY (stmt), ctx); + append_to_statement_list (OMP_ORDERED_BODY (stmt), &BIND_EXPR_BODY (bind)); + + x = built_in_decls[BUILT_IN_GOMP_ORDERED_END]; + x = build_function_call_expr (x, NULL); + gimplify_and_add (x, &BIND_EXPR_BODY (bind)); + + maybe_catch_exception (&BIND_EXPR_BODY (bind)); + pop_gimplify_context (bind); + BIND_EXPR_VARS (bind) = chainon (BIND_EXPR_VARS (bind), ctx->block_vars); + BLOCK_VARS (block) = BIND_EXPR_VARS (bind); +} + +/* Expand code for an OpenMP critical directive. */ + +/* Gimplify an OMP_CRITICAL statement. This is a relatively simple + substitution of a couple of function calls. But in the NAMED case, + requires that languages coordinate a symbol name. It is therefore + best put here in common code. */ + +static GTY((param1_is (tree), param2_is (tree))) + splay_tree critical_name_mutexes; + +static void +expand_omp_critical (tree *stmt_p, omp_context *ctx) +{ + tree bind, block, stmt = *stmt_p; + tree lock, unlock, name; + + name = OMP_CRITICAL_NAME (stmt); + if (name) + { + tree decl, args; + splay_tree_node n; + + if (!critical_name_mutexes) + critical_name_mutexes + = splay_tree_new_ggc (splay_tree_compare_pointers); + + n = splay_tree_lookup (critical_name_mutexes, (splay_tree_key) name); + if (n == NULL) + { + char *new_str; + + decl = create_tmp_var_raw (ptr_type_node, NULL); + + new_str = ACONCAT ((".gomp_critical_user_", + IDENTIFIER_POINTER (name), NULL)); + DECL_NAME (decl) = get_identifier (new_str); + TREE_PUBLIC (decl) = 1; + TREE_STATIC (decl) = 1; + DECL_COMMON (decl) = 1; + DECL_ARTIFICIAL (decl) = 1; + DECL_IGNORED_P (decl) = 1; + cgraph_varpool_finalize_decl (decl); + + splay_tree_insert (critical_name_mutexes, (splay_tree_key) name, + (splay_tree_value) decl); + } + else + decl = (tree) n->value; + + args = tree_cons (NULL, build_fold_addr_expr (decl), NULL); + lock = built_in_decls[BUILT_IN_GOMP_CRITICAL_NAME_START]; + lock = build_function_call_expr (lock, args); + + args = tree_cons (NULL, build_fold_addr_expr (decl), NULL); + unlock = built_in_decls[BUILT_IN_GOMP_CRITICAL_NAME_END]; + unlock = build_function_call_expr (unlock, args); + } + else + { + lock = built_in_decls[BUILT_IN_GOMP_CRITICAL_START]; + lock = build_function_call_expr (lock, NULL); + + unlock = built_in_decls[BUILT_IN_GOMP_CRITICAL_END]; + unlock = build_function_call_expr (unlock, NULL); + } + + push_gimplify_context (); + + block = make_node (BLOCK); + bind = build3 (BIND_EXPR, void_type_node, NULL, NULL, block); + *stmt_p = bind; + + gimplify_and_add (lock, &BIND_EXPR_BODY (bind)); + + expand_omp (&OMP_CRITICAL_BODY (stmt), ctx); + maybe_catch_exception (&OMP_CRITICAL_BODY (stmt)); + append_to_statement_list (OMP_CRITICAL_BODY (stmt), &BIND_EXPR_BODY (bind)); + + gimplify_and_add (unlock, &BIND_EXPR_BODY (bind)); + + pop_gimplify_context (bind); + BIND_EXPR_VARS (bind) = chainon (BIND_EXPR_VARS (bind), ctx->block_vars); + BLOCK_VARS (block) = BIND_EXPR_VARS (bind); +} + +/* Pass *TP back through the gimplifier within the context determined by WI. + This handles replacement of DECL_VALUE_EXPR, as well as adjusting the + flags on ADDR_EXPR. */ + +static void +expand_regimplify (tree *tp, struct walk_stmt_info *wi) +{ + enum gimplify_status gs; + tree pre = NULL; + + if (wi->is_lhs) + gs = gimplify_expr (tp, &pre, NULL, is_gimple_lvalue, fb_lvalue); + else if (wi->val_only) + gs = gimplify_expr (tp, &pre, NULL, is_gimple_val, fb_rvalue); + else + gs = gimplify_expr (tp, &pre, NULL, is_gimple_formal_tmp_var, fb_rvalue); + gcc_assert (gs == GS_ALL_DONE); + + if (pre) + tsi_link_before (&wi->tsi, pre, TSI_SAME_STMT); +} + +static tree +expand_omp_1 (tree *tp, int *walk_subtrees, void *data) +{ + struct walk_stmt_info *wi = data; + omp_context *ctx = wi->info; + tree t = *tp; + + *walk_subtrees = 0; + switch (TREE_CODE (*tp)) + { + case OMP_PARALLEL: + ctx = maybe_lookup_ctx (t); + if (!ctx->is_nested) + expand_omp_parallel (tp, ctx); + break; + + case OMP_FOR: + ctx = maybe_lookup_ctx (t); + gcc_assert (ctx); + expand_omp_for (tp, ctx); + break; + + case OMP_SECTIONS: + ctx = maybe_lookup_ctx (t); + gcc_assert (ctx); + expand_omp_sections (tp, ctx); + break; + + case OMP_SINGLE: + ctx = maybe_lookup_ctx (t); + gcc_assert (ctx); + expand_omp_single (tp, ctx); + break; + + case OMP_MASTER: + ctx = maybe_lookup_ctx (t); + gcc_assert (ctx); + expand_omp_master (tp, ctx); + break; + + case OMP_ORDERED: + ctx = maybe_lookup_ctx (t); + gcc_assert (ctx); + expand_omp_ordered (tp, ctx); + break; + + case OMP_CRITICAL: + ctx = maybe_lookup_ctx (t); + gcc_assert (ctx); + expand_omp_critical (tp, ctx); + break; + + case VAR_DECL: + if (ctx && DECL_HAS_VALUE_EXPR_P (t)) + expand_regimplify (tp, wi); + break; + + case ADDR_EXPR: + if (ctx) + expand_regimplify (tp, wi); + break; + + case ARRAY_REF: + case ARRAY_RANGE_REF: + case REALPART_EXPR: + case IMAGPART_EXPR: + case COMPONENT_REF: + case VIEW_CONVERT_EXPR: + if (ctx) + expand_regimplify (tp, wi); + break; + + case INDIRECT_REF: + if (ctx) + { + wi->is_lhs = false; + wi->val_only = true; + expand_regimplify (&TREE_OPERAND (t, 0), wi); + } + break; + + default: + if (!TYPE_P (t) && !DECL_P (t)) + *walk_subtrees = 1; + break; + } + + return NULL_TREE; +} + +static void +expand_omp (tree *stmt_p, omp_context *ctx) +{ + struct walk_stmt_info wi; + + memset (&wi, 0, sizeof (wi)); + wi.callback = expand_omp_1; + wi.info = ctx; + wi.val_only = true; + wi.want_locations = true; + + walk_stmts (&wi, stmt_p); +} + +/* Main entry point. */ + +static void +execute_lower_omp (void) +{ + all_contexts = splay_tree_new (splay_tree_compare_pointers, 0, + delete_omp_context); + + scan_omp (&DECL_SAVED_TREE (current_function_decl), NULL); + gcc_assert (parallel_nesting_level == 0); + + if (all_contexts->root) + expand_omp (&DECL_SAVED_TREE (current_function_decl), NULL); + + splay_tree_delete (all_contexts); + all_contexts = NULL; +} + +static bool +gate_lower_omp (void) +{ + return flag_openmp != 0; +} + +struct tree_opt_pass pass_lower_omp = +{ + "omplower", /* name */ + gate_lower_omp, /* gate */ + execute_lower_omp, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + 0, /* tv_id */ + PROP_gimple_any, /* properties_required */ + PROP_gimple_lomp, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + TODO_dump_func, /* todo_flags_finish */ + 0 /* letter */ +}; + + +/* The following is a utility to diagnose OpenMP structured block violations. + It's part of the "omplower" pass, as that's invoked too late. It should + be invoked by the respective front ends after gimplification. */ + +static splay_tree all_labels; + +/* Check for mismatched contexts and generate an error if needed. Return + true if an error is detected. */ + +static bool +diagnose_sb_0 (tree *stmt_p, tree branch_ctx, tree label_ctx) +{ + bool exit_p = true; + + if ((label_ctx ? TREE_VALUE (label_ctx) : NULL) == branch_ctx) + return false; + + /* Try to avoid confusing the user by producing and error message + with correct "exit" or "enter" verbage. We prefer "exit" + unless we can show that LABEL_CTX is nested within BRANCH_CTX. */ + if (branch_ctx == NULL) + exit_p = false; + else + { + while (label_ctx) + { + if (TREE_VALUE (label_ctx) == branch_ctx) + { + exit_p = false; + break; + } + label_ctx = TREE_CHAIN (label_ctx); + } + } + + if (exit_p) + error ("invalid exit from OpenMP structured block"); + else + error ("invalid entry to OpenMP structured block"); + + *stmt_p = build_empty_stmt (); + return true; +} + +/* Pass 1: Create a minimal tree of OpenMP structured blocks, and record + where in the tree each label is found. */ + +static tree +diagnose_sb_1 (tree *tp, int *walk_subtrees, void *data) +{ + struct walk_stmt_info *wi = data; + tree context = (tree) wi->info; + tree inner_context; + tree t = *tp; + + *walk_subtrees = 0; + switch (TREE_CODE (t)) + { + case OMP_PARALLEL: + case OMP_SECTIONS: + case OMP_SINGLE: + walk_tree (&OMP_CLAUSES (t), diagnose_sb_1, wi, NULL); + /* FALLTHRU */ + case OMP_SECTION: + case OMP_MASTER: + case OMP_ORDERED: + case OMP_CRITICAL: + /* The minimal context here is just a tree of statements. */ + inner_context = tree_cons (NULL, t, context); + wi->info = inner_context; + walk_stmts (wi, &OMP_BODY (t)); + wi->info = context; + break; + + case OMP_FOR: + walk_tree (&OMP_FOR_CLAUSES (t), diagnose_sb_1, wi, NULL); + inner_context = tree_cons (NULL, t, context); + wi->info = inner_context; + walk_tree (&OMP_FOR_INIT (t), diagnose_sb_1, wi, NULL); + walk_tree (&OMP_FOR_COND (t), diagnose_sb_1, wi, NULL); + walk_tree (&OMP_FOR_INCR (t), diagnose_sb_1, wi, NULL); + walk_stmts (wi, &OMP_FOR_PRE_BODY (t)); + walk_stmts (wi, &OMP_FOR_BODY (t)); + wi->info = context; + break; + + case LABEL_EXPR: + splay_tree_insert (all_labels, (splay_tree_key) LABEL_EXPR_LABEL (t), + (splay_tree_value) context); + break; + + default: + break; + } + + return NULL_TREE; +} + +/* Pass 2: Check each branch and see if its context differs from that of + the destination label's context. */ + +static tree +diagnose_sb_2 (tree *tp, int *walk_subtrees, void *data) +{ + struct walk_stmt_info *wi = data; + tree context = (tree) wi->info; + splay_tree_node n; + tree t = *tp; + + *walk_subtrees = 0; + switch (TREE_CODE (t)) + { + case OMP_PARALLEL: + case OMP_SECTIONS: + case OMP_SINGLE: + walk_tree (&OMP_CLAUSES (t), diagnose_sb_2, wi, NULL); + /* FALLTHRU */ + case OMP_SECTION: + case OMP_MASTER: + case OMP_ORDERED: + case OMP_CRITICAL: + wi->info = t; + walk_stmts (wi, &OMP_BODY (t)); + wi->info = context; + break; + + case OMP_FOR: + walk_tree (&OMP_FOR_CLAUSES (t), diagnose_sb_2, wi, NULL); + wi->info = t; + walk_tree (&OMP_FOR_INIT (t), diagnose_sb_2, wi, NULL); + walk_tree (&OMP_FOR_COND (t), diagnose_sb_2, wi, NULL); + walk_tree (&OMP_FOR_INCR (t), diagnose_sb_2, wi, NULL); + walk_stmts (wi, &OMP_FOR_PRE_BODY (t)); + walk_stmts (wi, &OMP_FOR_BODY (t)); + wi->info = context; + break; + + case GOTO_EXPR: + { + tree lab = GOTO_DESTINATION (t); + if (TREE_CODE (lab) != LABEL_DECL) + break; + + n = splay_tree_lookup (all_labels, (splay_tree_key) lab); + diagnose_sb_0 (tp, context, n ? (tree) n->value : NULL_TREE); + } + break; + + case SWITCH_EXPR: + { + tree vec = SWITCH_LABELS (t); + int i, len = TREE_VEC_LENGTH (vec); + for (i = 0; i < len; ++i) + { + tree lab = CASE_LABEL (TREE_VEC_ELT (vec, i)); + n = splay_tree_lookup (all_labels, (splay_tree_key) lab); + if (diagnose_sb_0 (tp, context, (tree) n->value)) + break; + } + } + break; + + case RETURN_EXPR: + diagnose_sb_0 (tp, context, NULL_TREE); + break; + + default: + break; + } + + return NULL_TREE; +} + +void +diagnose_omp_structured_block_errors (tree fndecl) +{ + tree save_current = current_function_decl; + struct walk_stmt_info wi; + + current_function_decl = fndecl; + + all_labels = splay_tree_new (splay_tree_compare_pointers, 0, 0); + + memset (&wi, 0, sizeof (wi)); + wi.callback = diagnose_sb_1; + walk_stmts (&wi, &DECL_SAVED_TREE (fndecl)); + + memset (&wi, 0, sizeof (wi)); + wi.callback = diagnose_sb_2; + wi.want_locations = true; + wi.want_return_expr = true; + walk_stmts (&wi, &DECL_SAVED_TREE (fndecl)); + + splay_tree_delete (all_labels); + all_labels = NULL; + + current_function_decl = save_current; +} + +#include "gt-omp-low.h" diff --git a/gcc/passes.c b/gcc/passes.c index b79a03811ef..e2d18c9593f 100644 --- a/gcc/passes.c +++ b/gcc/passes.c @@ -462,6 +462,7 @@ init_optimization_passes (void) p = &all_lowering_passes; NEXT_PASS (pass_remove_useless_stmts); NEXT_PASS (pass_mudflap_1); + NEXT_PASS (pass_lower_omp); NEXT_PASS (pass_lower_cf); NEXT_PASS (pass_lower_eh); NEXT_PASS (pass_build_cfg); diff --git a/gcc/print-tree.c b/gcc/print-tree.c index d247835ea6d..a371f09813e 100644 --- a/gcc/print-tree.c +++ b/gcc/print-tree.c @@ -506,6 +506,10 @@ print_node (FILE *file, const char *prefix, tree node, int indent) (void *) DECL_STRUCT_FUNCTION (node)); } + if ((TREE_CODE (node) == VAR_DECL || TREE_CODE (node) == PARM_DECL) + && DECL_HAS_VALUE_EXPR_P (node)) + print_node (file, "value-expr", DECL_VALUE_EXPR (node), indent + 4); + /* Print the decl chain only if decl is at second level. */ if (indent == 4) print_node (file, "chain", TREE_CHAIN (node), indent + 4); diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 81f21e49058..15e47619f17 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,11 @@ +2006-01-18 Richard Henderson <rth@redhat.com> + Aldy Hernandez <aldyh@redhat.com> + Jakub Jelinek <jakub@redhat.com> + Diego Novillo <dnovillo@redhat.com> + Uros Bizjak <uros@kss-loka.si> + + * testsuite/gcc.dg/gomp: New directory. + 2006-01-18 Paul Thomas <pault@gcc.gnu.org> Steven G. Kargl <kargls@comcast.net> diff --git a/gcc/testsuite/gcc.dg/gomp/appendix-a/a.1.1.c b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.1.1.c new file mode 100644 index 00000000000..f7f2924ce1a --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.1.1.c @@ -0,0 +1,10 @@ +/* { dg-do compile } */ + +void +a1 (int n, float *a, float *b) +{ + int i; +#pragma omp parallel for + for (i = 1; i < n; i++) /* i is private by default */ + b[i] = (a[i] + a[i - 1]) / 2.0; +} diff --git a/gcc/testsuite/gcc.dg/gomp/appendix-a/a.10.1.c b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.10.1.c new file mode 100644 index 00000000000..750bc378c64 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.10.1.c @@ -0,0 +1,27 @@ +/* { dg-do compile } */ + +#include <stdio.h> +void +work1 () +{ +} + +void +work2 () +{ +} +void +a10 () +{ +#pragma omp parallel + { +#pragma omp single + printf ("Beginning work1.\n"); + work1 (); +#pragma omp single + printf ("Finishing work1.\n"); +#pragma omp single nowait + printf ("Finished work1 and beginning work2.\n"); + work2 (); + } +} diff --git a/gcc/testsuite/gcc.dg/gomp/appendix-a/a.12.1.c b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.12.1.c new file mode 100644 index 00000000000..fab948dd736 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.12.1.c @@ -0,0 +1,41 @@ +/* { dg-do compile } */ + +#include <stdio.h> +extern float average (float, float, float); +void +a12 (float *x, float *xold, int n, float tol) +{ + int c, i, toobig; + float error, y; + c = 0; +#pragma omp parallel + { + do + { +#pragma omp for private(i) + for (i = 1; i < n - 1; ++i) + { + xold[i] = x[i]; + } +#pragma omp single + { + toobig = 0; + } +#pragma omp for private(i,y,error) reduction(+:toobig) + for (i = 1; i < n - 1; ++i) + { + y = x[i]; + x[i] = average (xold[i - 1], x[i], xold[i + 1]); + error = y - x[i]; + if (error > tol || error < -tol) + ++toobig; + } +#pragma omp master + { + ++c; + printf ("iteration %d, toobig=%d\n", c, toobig); + } + } + while (toobig > 0); + } +} diff --git a/gcc/testsuite/gcc.dg/gomp/appendix-a/a.13.1.c b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.13.1.c new file mode 100644 index 00000000000..cc0fcc25d79 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.13.1.c @@ -0,0 +1,18 @@ +/* { dg-do compile } */ + +int dequeue (float *a); +void work (int i, float *a); +void +a13 (float *x, float *y) +{ + int ix_next, iy_next; +#pragma omp parallel shared(x, y) private(ix_next, iy_next) + { +#pragma omp critical (xaxis) + ix_next = dequeue (x); + work (ix_next, x); +#pragma omp critical (yaxis) + iy_next = dequeue (y); + work (iy_next, y); + } +} diff --git a/gcc/testsuite/gcc.dg/gomp/appendix-a/a.14.1.c b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.14.1.c new file mode 100644 index 00000000000..14a7ed0bde6 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.14.1.c @@ -0,0 +1,23 @@ +/* { dg-do compile } */ + +void +a14 () +{ + int i = 1; +#pragma omp parallel sections + { +#pragma omp section + { +#pragma omp critical (name) + { +#pragma omp parallel + { +#pragma omp single + { + i++; + } + } + } + } + } +} diff --git a/gcc/testsuite/gcc.dg/gomp/appendix-a/a.17.1.c b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.17.1.c new file mode 100644 index 00000000000..c460f7566ef --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.17.1.c @@ -0,0 +1,20 @@ +/* { dg-do compile } */ + +void +a17_1_wrong () +{ + union + { + int n; + float x; + } u; +#pragma omp parallel + { +#pragma omp atomic + u.n++; +#pragma omp atomic + u.x += 1.0; +/* Incorrect because the atomic constructs reference the same location + through incompatible types */ + } +} diff --git a/gcc/testsuite/gcc.dg/gomp/appendix-a/a.17.2.c b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.17.2.c new file mode 100644 index 00000000000..aaaec7ea351 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.17.2.c @@ -0,0 +1,20 @@ +/* { dg-do compile } */ + +void +a17_2_wrong () +{ + int x; + int *i; + float *r; + i = &x; + r = (float *) &x; +#pragma omp parallel + { +#pragma omp atomic + *i += 1; +#pragma omp atomic + *r += 1.0; +/* Incorrect because the atomic constructs reference the same location + through incompatible types */ + } +} diff --git a/gcc/testsuite/gcc.dg/gomp/appendix-a/a.20.1.c b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.20.1.c new file mode 100644 index 00000000000..d43e0cf45f5 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.20.1.c @@ -0,0 +1,18 @@ +/* { dg-do compile } */ + +void +a20_wrong () +{ + int a = 1; +#pragma omp parallel + { + if (a != 0) +#pragma omp flush(a) /* { dg-error "'#pragma omp flush' may only" } */ +/* incorrect as flush cannot be immediate substatement + of if statement */ + if (a != 0) +#pragma omp barrier /* { dg-error "'#pragma omp barrier' may only" } */ +/* incorrect as barrier cannot be immediate substatement + of if statement */ + } +} diff --git a/gcc/testsuite/gcc.dg/gomp/appendix-a/a.20.2.c b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.20.2.c new file mode 100644 index 00000000000..4f113835624 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.20.2.c @@ -0,0 +1,18 @@ +/* { dg-do compile } */ + +void +a20 () +{ + int a = 1; +#pragma omp parallel + { + if (a != 0) + { +#pragma omp flush(a) + } + if (a != 0) + { +#pragma omp barrier + } + } +} diff --git a/gcc/testsuite/gcc.dg/gomp/appendix-a/a.21.2.c b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.21.2.c new file mode 100644 index 00000000000..a9b81d0c399 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.21.2.c @@ -0,0 +1,22 @@ +/* { dg-do compile } */ + +void +work (int i) +{ +} + +void +a21_wrong (int n) +{ + int i; +#pragma omp for ordered + for (i = 0; i < n; i++) + { +/* incorrect because an iteration may not execute more than one + ordered region */ +#pragma omp ordered + work (i); +#pragma omp ordered + work (i + 1); + } +} diff --git a/gcc/testsuite/gcc.dg/gomp/appendix-a/a.21.3.c b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.21.3.c new file mode 100644 index 00000000000..4d1ed6a5286 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.21.3.c @@ -0,0 +1,23 @@ +/* { dg-do compile } */ + +void work (int); + +void +a21_good (int n) +{ + int i; +#pragma omp for ordered + for (i = 0; i < n; i++) + { + if (i <= 10) + { +#pragma omp ordered + work (i); + } + if (i > 10) + { +#pragma omp ordered + work (i + 1); + } + } +} diff --git a/gcc/testsuite/gcc.dg/gomp/appendix-a/a.22.1.c b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.22.1.c new file mode 100644 index 00000000000..e3586838a34 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.22.1.c @@ -0,0 +1,11 @@ +/* { dg-do compile } */ +/* { dg-require-effective-target tls } */ + +int counter = 0; +#pragma omp threadprivate(counter) +int +increment_counter () +{ + counter++; + return (counter); +} diff --git a/gcc/testsuite/gcc.dg/gomp/appendix-a/a.22.2.c b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.22.2.c new file mode 100644 index 00000000000..7a6e901a8c7 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.22.2.c @@ -0,0 +1,11 @@ +/* { dg-do compile } */ +/* { dg-require-effective-target tls } */ + +int +increment_counter_2 () +{ + static int counter = 0; +#pragma omp threadprivate(counter) + counter++; + return (counter); +} diff --git a/gcc/testsuite/gcc.dg/gomp/appendix-a/a.24.1.c b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.24.1.c new file mode 100644 index 00000000000..9d8baa37f23 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.24.1.c @@ -0,0 +1,35 @@ +/* { dg-do compile } */ +/* { dg-require-effective-target tls } */ + +extern int omp_get_num_threads (void); +int x, y, t, z[1000]; +#pragma omp threadprivate(x) +void +a24 (int a) +{ + const int c = 1; + int i = 0; + int l = 0; +#pragma omp parallel default(none) private(a) shared(z) + { + int j = omp_get_num_threads (); + /* O.K. - j is declared within parallel region */ + /* O.K. - a is listed in private clause */ + /* - z is listed in shared clause */ + x = c; /* O.K. - x is threadprivate */ + /* - c has const-qualified type */ + z[i] = y; + /* { dg-error "'i' not specified" "" { target *-*-* } 21 } */ + /* { dg-error "enclosing parallel" "" { target *-*-* } 13 } */ + /* { dg-error "'y' not specified" "" { target *-*-* } 21 } */ +#pragma omp for firstprivate(y) + for (i = 0; i < 10; i++) + { + z[i] = y; /* O.K. - i is the loop iteration variable */ + /* - y is listed in firstprivate clause */ + } + z[l] = t; + /* { dg-error "'l' not specified" "" { target *-*-* } 31 } */ + /* { dg-error "'t' not specified" "" { target *-*-* } 31 } */ + } +} diff --git a/gcc/testsuite/gcc.dg/gomp/appendix-a/a.26.2.c b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.26.2.c new file mode 100644 index 00000000000..b655edc107c --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.26.2.c @@ -0,0 +1,21 @@ +/* { dg-do compile } */ +/* { dg-options "-fopenmp -std=c99" } */ + +int a; +void +g (int k) +{ + a = k; /* The global "a", not the private "a" in f */ +} + +void +f (int n) +{ + int a = 0; +#pragma omp parallel for private(a) + for (int i = 1; i < n; i++) + { + a = i; + g (a * 2); /* Private copy of "a" */ + } +} diff --git a/gcc/testsuite/gcc.dg/gomp/appendix-a/a.27.1.c b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.27.1.c new file mode 100644 index 00000000000..faa6ac62128 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.27.1.c @@ -0,0 +1,15 @@ +/* { dg-do compile } */ + +void +a27 () +{ + int i, a; +#pragma omp parallel private(a) + { +#pragma omp parallel for private(a) + for (i = 0; i < 10; i++) + { + /* do work here */ + } + } +} diff --git a/gcc/testsuite/gcc.dg/gomp/appendix-a/a.30.1.c b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.30.1.c new file mode 100644 index 00000000000..e34be4c4296 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.30.1.c @@ -0,0 +1,14 @@ +/* { dg-do compile } */ + +void +a30 (int n, float *a, float *b) +{ + int i; +#pragma omp parallel + { +#pragma omp for lastprivate(i) + for (i = 0; i < n - 1; i++) + a[i] = b[i] + b[i + 1]; + } + a[i] = b[i]; /* i == n-1 here */ +} diff --git a/gcc/testsuite/gcc.dg/gomp/appendix-a/a.31.1.c b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.31.1.c new file mode 100644 index 00000000000..cbb3f60df44 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.31.1.c @@ -0,0 +1,17 @@ +/* { dg-do compile } */ + +void +a31_1 (float *x, int *y, int n) +{ + int i, b; + float a; + a = 0.0; + b = 0; +#pragma omp parallel for private(i) shared(x, y, n) \ + reduction(+:a) reduction(^:b) + for (i = 0; i < n; i++) + { + a += x[i]; + b ^= y[i]; + } +} diff --git a/gcc/testsuite/gcc.dg/gomp/appendix-a/a.31.2.c b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.31.2.c new file mode 100644 index 00000000000..f9da3f4fc20 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.31.2.c @@ -0,0 +1,27 @@ +/* { dg-do compile } */ + +void +a31_2 (float *x, int *y, int n) +{ + int i, b, b_p; + float a, a_p; + a = 0.0; + b = 0; +#pragma omp parallel shared(a, b, x, y, n) \ + private(a_p, b_p) + { + a_p = 0.0; + b_p = 0; +#pragma omp for private(i) + for (i = 0; i < n; i++) + { + a_p += x[i]; + b_p ^= y[i]; + } +#pragma omp critical + { + a += a_p; + b ^= b_p; + } + } +} diff --git a/gcc/testsuite/gcc.dg/gomp/appendix-a/a.32.1.c b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.32.1.c new file mode 100644 index 00000000000..d2cb316f3cc --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.32.1.c @@ -0,0 +1,27 @@ +/* { dg-do compile } */ +/* { dg-require-effective-target tls } */ + +#include <stdlib.h> +float *work; +int size; +float tol; +void build (void); +#pragma omp threadprivate(work,size,tol) +void +a32 (float t, int n) +{ + tol = t; + size = n; +#pragma omp parallel copyin(tol,size) + { + build (); + } +} +void +build () +{ + int i; + work = (float *) malloc (sizeof (float) * size); + for (i = 0; i < size; ++i) + work[i] = tol; +} diff --git a/gcc/testsuite/gcc.dg/gomp/appendix-a/a.33.1.c b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.33.1.c new file mode 100644 index 00000000000..99c06da6a5d --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.33.1.c @@ -0,0 +1,14 @@ +/* { dg-do compile } */ +/* { dg-require-effective-target tls } */ + +#include <stdio.h> +float x, y; +#pragma omp threadprivate(x, y) +void +init (float a, float b) +{ +#pragma omp single copyprivate(a,b,x,y) + { + scanf ("%f %f %f %f", &a, &b, &x, &y); + } +} diff --git a/gcc/testsuite/gcc.dg/gomp/appendix-a/a.33.2.c b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.33.2.c new file mode 100644 index 00000000000..a0da739dd84 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.33.2.c @@ -0,0 +1,26 @@ +/* { dg-do compile } */ + +#include <stdio.h> +#include <stdlib.h> +float +read_next () +{ + float *tmp; + float return_val; +#pragma omp single copyprivate(tmp) + { + tmp = (float *) malloc (sizeof (float)); + } /* copies the pointer only */ +#pragma omp master + { + scanf ("%f", tmp); + } +#pragma omp barrier + return_val = *tmp; +#pragma omp barrier +#pragma omp single nowait + { + free (tmp); + } + return return_val; +} diff --git a/gcc/testsuite/gcc.dg/gomp/appendix-a/a.34.1.c b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.34.1.c new file mode 100644 index 00000000000..e5ae7a61efc --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.34.1.c @@ -0,0 +1,25 @@ +/* { dg-do compile } */ + +void +work (int i, int j) +{ +} + +void +good_nesting (int n) +{ + int i, j; +#pragma omp parallel default(shared) + { +#pragma omp for + for (i = 0; i < n; i++) + { +#pragma omp parallel shared(i, n) + { +#pragma omp for + for (j = 0; j < n; j++) + work (i, j); + } + } + } +} diff --git a/gcc/testsuite/gcc.dg/gomp/appendix-a/a.34.2.c b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.34.2.c new file mode 100644 index 00000000000..7cc265fec02 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.34.2.c @@ -0,0 +1,29 @@ +/* { dg-do compile } */ + +void +work (int i, int j) +{ +} + +void +work1 (int i, int n) +{ + int j; +#pragma omp parallel default(shared) + { +#pragma omp for + for (j = 0; j < n; j++) + work (i, j); + } +} +void +good_nesting2 (int n) +{ + int i; +#pragma omp parallel default(shared) + { +#pragma omp for + for (i = 0; i < n; i++) + work1 (i, n); + } +} diff --git a/gcc/testsuite/gcc.dg/gomp/appendix-a/a.35.1.c b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.35.1.c new file mode 100644 index 00000000000..95556c948c7 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.35.1.c @@ -0,0 +1,23 @@ +/* { dg-do compile } */ + +void +work (int i, int j) +{ +} + +void +wrong1 (int n) +{ +#pragma omp parallel default(shared) + { + int i, j; +#pragma omp for + for (i = 0; i < n; i++) + { + /* incorrect nesting of loop regions */ +#pragma omp for + for (j = 0; j < n; j++) + work (i, j); + } + } +} diff --git a/gcc/testsuite/gcc.dg/gomp/appendix-a/a.35.2.c b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.35.2.c new file mode 100644 index 00000000000..165c86e278f --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.35.2.c @@ -0,0 +1,25 @@ +/* { dg-do compile } */ + +void work (int, int); + +void +work1 (int i, int n) +{ + int j; + /* incorrect nesting of loop regions */ +#pragma omp for + for (j = 0; j < n; j++) + work (i, j); +} + +void +wrong2 (int n) +{ +#pragma omp parallel default(shared) + { + int i; +#pragma omp for + for (i = 0; i < n; i++) + work1 (i, n); + } +} diff --git a/gcc/testsuite/gcc.dg/gomp/appendix-a/a.35.3.c b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.35.3.c new file mode 100644 index 00000000000..f99e09b36c5 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.35.3.c @@ -0,0 +1,19 @@ +/* { dg-do compile } */ + +void work (int, int); + +void +wrong3 (int n) +{ +#pragma omp parallel default(shared) + { + int i; +#pragma omp for + for (i = 0; i < n; i++) + { +/* incorrect nesting of regions */ +#pragma omp single + work (i, 0); + } + } +} diff --git a/gcc/testsuite/gcc.dg/gomp/appendix-a/a.35.4.c b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.35.4.c new file mode 100644 index 00000000000..88824031cc2 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.35.4.c @@ -0,0 +1,18 @@ +/* { dg-do compile } */ + +void +wrong4 (int n) +{ +#pragma omp parallel default(shared) + { + int i; +#pragma omp for + for (i = 0; i < n; i++) + { + work (i, 0); + /* incorrect nesting of barrier region in a loop region */ +#pragma omp barrier + work (i, 1); + } + } +} diff --git a/gcc/testsuite/gcc.dg/gomp/appendix-a/a.35.5.c b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.35.5.c new file mode 100644 index 00000000000..7b53015571e --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.35.5.c @@ -0,0 +1,16 @@ +/* { dg-do compile } */ + +void +wrong5 (int n) +{ +#pragma omp parallel + { +#pragma omp critical + { + work (n, 0); +/* incorrect nesting of barrier region in a critical region */ +#pragma omp barrier + work (n, 1); + } + } +} diff --git a/gcc/testsuite/gcc.dg/gomp/appendix-a/a.35.6.c b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.35.6.c new file mode 100644 index 00000000000..6385db30897 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.35.6.c @@ -0,0 +1,16 @@ +/* { dg-do compile } */ + +void +wrong6 (int n) +{ +#pragma omp parallel + { +#pragma omp single + { + work (n, 0); +/* incorrect nesting of barrier region in a single region */ +#pragma omp barrier + work (n, 1); + } + } +} diff --git a/gcc/testsuite/gcc.dg/gomp/appendix-a/a.37.1.c b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.37.1.c new file mode 100644 index 00000000000..3581ee27d7d --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.37.1.c @@ -0,0 +1,13 @@ +/* { dg-do compile } */ + +extern int omp_get_num_threads (void); +void work (int i); +void +incorrect () +{ + int np, i; + np = omp_get_num_threads (); /* misplaced */ +#pragma omp parallel for schedule(static) + for (i = 0; i < np; i++) + work (i); +} diff --git a/gcc/testsuite/gcc.dg/gomp/appendix-a/a.37.2.c b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.37.2.c new file mode 100644 index 00000000000..64256c78d88 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.37.2.c @@ -0,0 +1,14 @@ +/* { dg-do compile } */ + +extern int omp_get_thread_num (void); +void work (int i); +void +correct () +{ + int i; +#pragma omp parallel private(i) + { + i = omp_get_thread_num (); + work (i); + } +} diff --git a/gcc/testsuite/gcc.dg/gomp/appendix-a/a.8.1.c b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.8.1.c new file mode 100644 index 00000000000..eed21b367cc --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.8.1.c @@ -0,0 +1,17 @@ +/* { dg-do compile } */ + +#include <math.h> +void +a8 (int n, int m, float *a, float *b, float *y, float *z) +{ + int i; +#pragma omp parallel + { +#pragma omp for nowait + for (i = 1; i < n; i++) + b[i] = (a[i] + a[i - 1]) / 2.0; +#pragma omp for nowait + for (i = 0; i < m; i++) + y[i] = sqrt (z[i]); + } +} diff --git a/gcc/testsuite/gcc.dg/gomp/appendix-a/a.9.1.c b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.9.1.c new file mode 100644 index 00000000000..45be33f16ea --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/appendix-a/a.9.1.c @@ -0,0 +1,18 @@ +/* { dg-do compile } */ + +void XAXIS (); +void YAXIS (); +void ZAXIS (); +void +a9 () +{ +#pragma omp parallel sections + { +#pragma omp section + XAXIS (); +#pragma omp section + YAXIS (); +#pragma omp section + ZAXIS (); + } +} diff --git a/gcc/testsuite/gcc.dg/gomp/atomic-1.c b/gcc/testsuite/gcc.dg/gomp/atomic-1.c new file mode 100644 index 00000000000..3e4bc569ba7 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/atomic-1.c @@ -0,0 +1,99 @@ +/* { dg-do compile } */ + +int x; +volatile int y; +volatile unsigned char z; + +void f1(void) +{ + #pragma omp atomic + x++; + #pragma omp atomic + x--; + #pragma omp atomic + ++x; + #pragma omp atomic + --x; + #pragma omp atomic + x += 1; + #pragma omp atomic + x -= y; + #pragma omp atomic + x |= 1; + #pragma omp atomic + x &= 1; + #pragma omp atomic + x ^= 1; + #pragma omp atomic + x *= 3; + #pragma omp atomic + x /= 3; + #pragma omp atomic + x /= 3; + #pragma omp atomic + x <<= 3; + #pragma omp atomic + x >>= 3; +} + +void f2(void) +{ + #pragma omp atomic + y++; + #pragma omp atomic + y--; + #pragma omp atomic + ++y; + #pragma omp atomic + --y; + #pragma omp atomic + y += 1; + #pragma omp atomic + y -= x; + #pragma omp atomic + y |= 1; + #pragma omp atomic + y &= 1; + #pragma omp atomic + y ^= 1; + #pragma omp atomic + y *= 3; + #pragma omp atomic + y /= 3; + #pragma omp atomic + y /= 3; + #pragma omp atomic + y <<= 3; + #pragma omp atomic + y >>= 3; +} + +void f3(void) +{ + #pragma omp atomic + z++; + #pragma omp atomic + z--; + #pragma omp atomic + ++z; + #pragma omp atomic + --z; + #pragma omp atomic + z += 1; + #pragma omp atomic + z |= 1; + #pragma omp atomic + z &= 1; + #pragma omp atomic + z ^= 1; + #pragma omp atomic + z *= 3; + #pragma omp atomic + z /= 3; + #pragma omp atomic + z /= 3; + #pragma omp atomic + z <<= 3; + #pragma omp atomic + z >>= 3; +} diff --git a/gcc/testsuite/gcc.dg/gomp/atomic-2.c b/gcc/testsuite/gcc.dg/gomp/atomic-2.c new file mode 100644 index 00000000000..720ec9e8ba0 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/atomic-2.c @@ -0,0 +1,23 @@ +/* { dg-do compile } */ + +float x, y; + +void f1(void) +{ + #pragma omp atomic + x++; + #pragma omp atomic + x--; + #pragma omp atomic + ++x; + #pragma omp atomic + --x; + #pragma omp atomic + x += 1; + #pragma omp atomic + x -= y; + #pragma omp atomic + x *= 3; + #pragma omp atomic + x /= 3; +} diff --git a/gcc/testsuite/gcc.dg/gomp/atomic-3.c b/gcc/testsuite/gcc.dg/gomp/atomic-3.c new file mode 100644 index 00000000000..0c612a16061 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/atomic-3.c @@ -0,0 +1,13 @@ +/* { dg-do compile } */ +/* { dg-options "-fopenmp -fdump-tree-gimple" } */ + +int *xyzzy; + +void f1(void) +{ + #pragma omp atomic + xyzzy++; +} + +/* { dg-final { scan-tree-dump-times "xyzzy, 4" 1 "gimple" { target i?86-*-* x86_64-*-* ia64-*-* powerpc*-*-* alpha*-*-* } } } */ +/* { dg-final { cleanup-tree-dump "gimple" } } */ diff --git a/gcc/testsuite/gcc.dg/gomp/atomic-4.c b/gcc/testsuite/gcc.dg/gomp/atomic-4.c new file mode 100644 index 00000000000..7f27370d535 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/atomic-4.c @@ -0,0 +1,24 @@ +/* { dg-do compile } */ + +int a[4]; +int *p; +struct S { int x; int y[4]; } s; +int *bar(void); + +void f1(void) +{ + #pragma omp atomic + a[4] += 1; + #pragma omp atomic + *p += 1; + #pragma omp atomic + s.x += 1; + #pragma omp atomic + s.y[*p] += 1; + #pragma omp atomic + s.y[*p] *= 42; + #pragma omp atomic + *bar() += 1; + #pragma omp atomic + *bar() *= 42; +} diff --git a/gcc/testsuite/gcc.dg/gomp/atomic-5.c b/gcc/testsuite/gcc.dg/gomp/atomic-5.c new file mode 100644 index 00000000000..445f7805f70 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/atomic-5.c @@ -0,0 +1,38 @@ +/* { dg-do compile } */ + +int x; +const int y; +int bar(void); + +void f1(void) +{ + register int z; + + #pragma omp atomic + x %= 2; /* { dg-error "invalid operator" } */ + #pragma omp atomic + x = x + 1; /* { dg-error "invalid operator" } */ + #pragma omp atomic + x = 1; /* { dg-error "invalid operator" } */ + #pragma omp atomic + ++y; /* { dg-error "read-only variable" } */ + #pragma omp atomic + y--; /* { dg-error "read-only variable" } */ + #pragma omp atomic + y += 1; /* { dg-error "read-only variable" } */ + #pragma omp atomic + z += 1; /* { dg-error "register variable" } */ + #pragma omp atomic + bar(); /* { dg-error "invalid operator" } */ + #pragma omp atomic + bar() += 1; /* { dg-error "lvalue required" } */ + #pragma omp atomic a /* { dg-error "expected end of line" } */ + x++; + #pragma omp atomic + ; /* { dg-error "expected expression" } */ + #pragma omp atomic + #pragma omp atomic /* { dg-error "expected expression" } */ + ; + /* Check that we didn't get stuck on the pragma eol marker. */ + undef; /* { dg-error "" } */ +} diff --git a/gcc/testsuite/gcc.dg/gomp/atomic-6.c b/gcc/testsuite/gcc.dg/gomp/atomic-6.c new file mode 100644 index 00000000000..0d56becd85e --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/atomic-6.c @@ -0,0 +1,10 @@ +/* { dg-do compile } */ + +int x[10], z; +double y[10]; + +void f1(void) +{ + #pragma omp atomic + x[z] /= y[z]; +} diff --git a/gcc/testsuite/gcc.dg/gomp/atomic-7.c b/gcc/testsuite/gcc.dg/gomp/atomic-7.c new file mode 100644 index 00000000000..612e97f4530 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/atomic-7.c @@ -0,0 +1,23 @@ +/* { dg-do compile } */ + +double x, y; + +void f2(void) +{ + #pragma omp atomic + y++; + #pragma omp atomic + y--; + #pragma omp atomic + ++y; + #pragma omp atomic + --y; + #pragma omp atomic + y += 1; + #pragma omp atomic + y -= x; + #pragma omp atomic + y *= 3; + #pragma omp atomic + y /= 3; +} diff --git a/gcc/testsuite/gcc.dg/gomp/atomic-8.c b/gcc/testsuite/gcc.dg/gomp/atomic-8.c new file mode 100644 index 00000000000..2f04151f0ed --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/atomic-8.c @@ -0,0 +1,21 @@ +/* { dg-do compile } */ + +long double z; + +void f3(void) +{ + #pragma omp atomic + z++; + #pragma omp atomic + z--; + #pragma omp atomic + ++z; + #pragma omp atomic + --z; + #pragma omp atomic + z += 1; + #pragma omp atomic + z *= 3; + #pragma omp atomic + z /= 3; +} diff --git a/gcc/testsuite/gcc.dg/gomp/atomic-9.c b/gcc/testsuite/gcc.dg/gomp/atomic-9.c new file mode 100644 index 00000000000..128e9df5e4a --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/atomic-9.c @@ -0,0 +1,13 @@ +/* { dg-do compile } */ +/* { dg-options "-fopenmp -fdump-tree-gimple" } */ + +volatile int *bar(void); + +void f1(void) +{ + #pragma omp atomic + *bar() += 1; +} + +/* { dg-final { scan-tree-dump-times "__sync_fetch_and_add" 1 "gimple" { target i?86-*-* x86_64-*-* ia64-*-* powerpc*-*-* alpha*-*-* } } } */ +/* { dg-final { cleanup-tree-dump "gimple" } } */ diff --git a/gcc/testsuite/gcc.dg/gomp/barrier-1.c b/gcc/testsuite/gcc.dg/gomp/barrier-1.c new file mode 100644 index 00000000000..ef7c9afb18b --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/barrier-1.c @@ -0,0 +1,18 @@ +/* { dg-do compile } */ +/* { dg-options "-fopenmp -fdump-tree-gimple" } */ + +void f1(void) +{ + #pragma omp barrier +} + +void f2(_Bool p) +{ + if (p) + { + #pragma omp barrier + } +} + +/* { dg-final { scan-tree-dump-times "GOMP_barrier" 2 "gimple" } } */ +/* { dg-final { cleanup-tree-dump "gimple" } } */ diff --git a/gcc/testsuite/gcc.dg/gomp/barrier-2.c b/gcc/testsuite/gcc.dg/gomp/barrier-2.c new file mode 100644 index 00000000000..6e94d0c25c6 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/barrier-2.c @@ -0,0 +1,26 @@ +/* { dg-do compile } */ + +void f1(void) +{ + #pragma omp barrier a /* { dg-error "expected end of line" } */ +} + +/* OpenMP 2.5, section 2.7.3: + + Note that because the barrier construct does not have a C language + statement as part of its syntax, there are some restrictions on its + placement within a program. The barrier directive may only be placed + in the program at a position where ignoring or deleting the directive + would result in a program with correct syntax. */ + +void f2(void) +{ + label: + #pragma omp barrier +} /* { dg-error "label at end of compound statement" } */ + +void f3(_Bool p) +{ + if (p) + #pragma omp barrier /* { dg-error "compound statements" } */ +} diff --git a/gcc/testsuite/gcc.dg/gomp/block-1.c b/gcc/testsuite/gcc.dg/gomp/block-1.c new file mode 100644 index 00000000000..abc66e580fd --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/block-1.c @@ -0,0 +1,22 @@ +// { dg-do compile } + +void foo() +{ + bad1: + #pragma omp parallel + goto bad1; // { dg-error "invalid exit" } + + goto bad2; // { dg-error "invalid entry" } + #pragma omp parallel + { + bad2: ; + } + + #pragma omp parallel + { + int i; + goto ok1; + for (i = 0; i < 10; ++i) + { ok1: break; } + } +} diff --git a/gcc/testsuite/gcc.dg/gomp/block-10.c b/gcc/testsuite/gcc.dg/gomp/block-10.c new file mode 100644 index 00000000000..76ee3974508 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/block-10.c @@ -0,0 +1,40 @@ +// { dg-do compile } + +void foo(int i) +{ + int j; + switch (i) // { dg-error "invalid entry" } + { + #pragma omp parallel + { case 0:; } + } + switch (i) // { dg-error "invalid entry" } + { + #pragma omp for + for (j = 0; j < 10; ++ j) + { case 1:; } + } + switch (i) // { dg-error "invalid entry" } + { + #pragma omp critical + { case 2:; } + } + switch (i) // { dg-error "invalid entry" } + { + #pragma omp master + { case 3:; } + } + switch (i) // { dg-error "invalid entry" } + { + #pragma omp sections + { case 4:; + #pragma omp section + { case 5:; } + } + } + switch (i) // { dg-error "invalid entry" } + { + #pragma omp ordered + { default:; } + } +} diff --git a/gcc/testsuite/gcc.dg/gomp/block-11.c b/gcc/testsuite/gcc.dg/gomp/block-11.c new file mode 100644 index 00000000000..c2800061b6e --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/block-11.c @@ -0,0 +1,19 @@ +/* PR c++/24516 */ +/* { dg-do compile } */ + +void +bar (int *p) +{ + int m; +#pragma omp parallel for + for (m = 0; m < 1000; ++m) + switch (p[m]) + { + case 1: + p[m] = 2; + break; + default: + p[m] = 3; + break; + } +} diff --git a/gcc/testsuite/gcc.dg/gomp/block-2.c b/gcc/testsuite/gcc.dg/gomp/block-2.c new file mode 100644 index 00000000000..810b2da07b4 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/block-2.c @@ -0,0 +1,32 @@ +// { dg-do compile } + +void foo() +{ + int i, j; + + #pragma omp for + for (i = 0; i < 10; ++i) + break; // { dg-error "break" } + + bad1: + #pragma omp for + for (i = 0; i < 10; ++i) + goto bad1; // { dg-error "invalid exit" } + + goto bad2; // { dg-error "invalid entry" } + #pragma omp for + for (i = 0; i < 10; ++i) + { + bad2: ; + } + + #pragma omp for + for (i = 0; i < 10; ++i) + for (j = 0; j < 10; ++j) + if (i == j) + break; + + #pragma omp for + for (i = 0; i < 10; ++i) + continue; +} diff --git a/gcc/testsuite/gcc.dg/gomp/block-3.c b/gcc/testsuite/gcc.dg/gomp/block-3.c new file mode 100644 index 00000000000..160047c394c --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/block-3.c @@ -0,0 +1,57 @@ +// { dg-do compile } + +extern int test(int); +void foo() +{ + int i; + + for (i = 0; i < 10; ++i) + { + #pragma omp sections + { + continue; // { dg-error "invalid exit" } + } + } + + #pragma omp sections + { + #pragma omp section + { bad1: ; } + #pragma omp section + goto bad1; // { dg-error "invalid exit" } + } + + #pragma omp sections + { + goto bad2; // { dg-error "invalid exit" } + } + bad2:; + + goto bad3; // { dg-error "invalid entry" } + #pragma omp sections + { + bad3: ; + } + + #pragma omp sections + { + goto ok1; + ok1:; + + #pragma omp section + for (i = 0; i < 10; ++i) + if (test(i)) + break; + else + continue; + + #pragma omp section + switch (i) + { + case 0: + break; + default: + test(i); + } + } +} diff --git a/gcc/testsuite/gcc.dg/gomp/block-4.c b/gcc/testsuite/gcc.dg/gomp/block-4.c new file mode 100644 index 00000000000..815d36b2e39 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/block-4.c @@ -0,0 +1,9 @@ +// { dg-do compile } + +void foo() +{ + #pragma omp critical + { + return; // { dg-error "invalid exit" } + } +} diff --git a/gcc/testsuite/gcc.dg/gomp/block-5.c b/gcc/testsuite/gcc.dg/gomp/block-5.c new file mode 100644 index 00000000000..450106feeb1 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/block-5.c @@ -0,0 +1,15 @@ +// { dg-do compile } + +void foo() +{ + #pragma omp master + { + goto bad1; // { dg-error "invalid exit" } + } + + #pragma omp master + { + bad1: + return; // { dg-error "invalid exit" } + } +} diff --git a/gcc/testsuite/gcc.dg/gomp/block-6.c b/gcc/testsuite/gcc.dg/gomp/block-6.c new file mode 100644 index 00000000000..fa4c5eab5f4 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/block-6.c @@ -0,0 +1,9 @@ +// { dg-do compile } + +void foo() +{ + #pragma omp ordered + { + return; // { dg-error "invalid exit" } + } +} diff --git a/gcc/testsuite/gcc.dg/gomp/block-7.c b/gcc/testsuite/gcc.dg/gomp/block-7.c new file mode 100644 index 00000000000..802b3b3a383 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/block-7.c @@ -0,0 +1,20 @@ +// { dg-do compile } + +void foo() +{ + int i, j; + for (i = 0; i < 10; ++i) + { + #pragma omp for + for (j = ({ continue; 0; }); // { dg-error "invalid exit" } + j < ({ continue; 10; }); // { dg-error "invalid exit" } + j += ({ continue; 1; })) // { dg-error "invalid exit" } + continue; + + #pragma omp for + for (j = ({ break; 0; }); // { dg-error "invalid exit" } + j < ({ break; 10; }); // { dg-error "invalid exit" } + j += ({ break; 1; })) // { dg-error "invalid exit" } + break; // { dg-error "break" } + } +} diff --git a/gcc/testsuite/gcc.dg/gomp/block-8.c b/gcc/testsuite/gcc.dg/gomp/block-8.c new file mode 100644 index 00000000000..177acaa28c0 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/block-8.c @@ -0,0 +1,11 @@ +// { dg-do compile } +// PR 24451 + +int foo() +{ + int i; + + #pragma omp parallel for + for (i = 0; i < 10; ++i) + return 0; // { dg-error "invalid exit" } +} diff --git a/gcc/testsuite/gcc.dg/gomp/block-9.c b/gcc/testsuite/gcc.dg/gomp/block-9.c new file mode 100644 index 00000000000..9217cb74990 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/block-9.c @@ -0,0 +1,25 @@ +// { dg-do compile } + +void foo(int i) +{ + int j; + switch (i) // { dg-error "invalid entry" } + { + #pragma omp parallel + { case 0:; } + #pragma omp for + for (j = 0; j < 10; ++ j) + { case 1:; } + #pragma omp critical + { case 2:; } + #pragma omp master + { case 3:; } + #pragma omp sections + { case 4:; + #pragma omp section + { case 5:; } + } + #pragma omp ordered + { default:; } + } +} diff --git a/gcc/testsuite/gcc.dg/gomp/clause-1.c b/gcc/testsuite/gcc.dg/gomp/clause-1.c new file mode 100644 index 00000000000..ace9738043a --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/clause-1.c @@ -0,0 +1,94 @@ +/* { dg-do compile } */ +/* { dg-require-effective-target tls } */ + +#define p parallel + +extern void bar (void); +extern char q[]; +int t; +#pragma omp threadprivate (t) + +void +foo (int x) +{ + char *p; + struct S { int i; int j; } s; + char a[32]; + double d; + int i; + const int c = 8; +#pragma omp p shared (x, x) /* { dg-error "more than once" } */ + ; +#pragma omp p private (x) private (x) /* { dg-error "more than once" } */ + ; +#pragma omp p shared (x) firstprivate (x) /* { dg-error "more than once" } */ + ; +#pragma omp p firstprivate (x, x) /* { dg-error "more than once" } */ + ; +#pragma omp p for shared (x) lastprivate (x) /* { dg-error "more than" } */ + for (i = 0; i < 10; i++) + ; +#pragma omp p for private (x) lastprivate (x) /* { dg-error "more than" } */ + for (i = 0; i < 10; i++) + ; +#pragma omp p for lastprivate (x, x) /* { dg-error "more than once" } */ + for (i = 0; i < 10; i++) + ; +#pragma omp single private (x) copyprivate (x) /* { dg-error "more than" } */ + ; +#pragma omp p shared (bar) /* { dg-error "is not a variable" } */ + ; +#pragma omp p private (bar) /* { dg-error "is not a variable" } */ + ; +#pragma omp p firstprivate (bar) /* { dg-error "is not a variable" } */ + ; +#pragma omp p reduction (+:p) /* { dg-error "has invalid type for" } */ + ; +#pragma omp p reduction (*:s) /* { dg-error "has invalid type for" } */ + ; +#pragma omp p reduction (-:a) /* { dg-error "has invalid type for" } */ + ; + d = 0; +#pragma omp p reduction (*:d) + ; +#pragma omp p reduction (|:d) /* { dg-error "has invalid type for" } */ + ; +#pragma omp p reduction (&&:d) /* { dg-error "has invalid type for" } */ + ; +#pragma omp p copyin (d) /* { dg-error "must be 'threadprivate'" } */ + ; +#pragma omp p copyin (x) /* { dg-error "must be 'threadprivate'" } */ + ; +#pragma omp p for firstprivate (x) lastprivate (x) + for (i = 0; i < 10; i++) + ; +#pragma omp p private (q) /* { dg-error "incomplete type" } */ + ; +#pragma omp p firstprivate (q) /* { dg-error "incomplete type" } */ + ; +#pragma omp p for lastprivate (q) /* { dg-error "incomplete type" } */ + for (i = 0; i < 10; i++) + ; +#pragma omp p shared (t) /* { dg-error "predetermined 'threadprivate'" } */ + ; +#pragma omp p private (t) /* { dg-error "predetermined 'threadprivate'" } */ + ; +#pragma omp p firstprivate (t) /* { dg-error "predetermined 'threadpriv" } */ + ; +#pragma omp p for lastprivate (t) /* { dg-error "predetermined 'threadpr" } */ + for (i = 0; i < 10; i++) + ; +#pragma omp p reduction (*:t) /* { dg-error "predetermined 'threadprivate" } */ + ; +#pragma omp p shared (c) /* { dg-error "predetermined 'shared'" } */ + ; +#pragma omp p private (c) /* { dg-error "predetermined 'shared'" } */ + ; +#pragma omp p firstprivate (c) /* { dg-error "predetermined 'shared'" } */ + ; +#pragma omp p for lastprivate (c) /* { dg-error "predetermined 'shared'" } */ + for (i = 0; i < 10; i++) + ; +#pragma omp p reduction (*:c) /* { dg-error "predetermined 'shared'" } */ + ; +} diff --git a/gcc/testsuite/gcc.dg/gomp/copyin-1.c b/gcc/testsuite/gcc.dg/gomp/copyin-1.c new file mode 100644 index 00000000000..117f82f8134 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/copyin-1.c @@ -0,0 +1,27 @@ +// { dg-do compile } +// { dg-require-effective-target tls } + +int i, j; + +#pragma omp threadprivate (i) + +void bar(void); +void foo(void) +{ + int k; + extern int l; + extern int m; + +#pragma omp threadprivate (m) + + #pragma omp parallel copyin(i) + bar(); + #pragma omp parallel copyin(j) // { dg-error "threadprivate" } + bar(); + #pragma omp parallel copyin(k) // { dg-error "threadprivate" } + bar(); + #pragma omp parallel copyin(l) // { dg-error "threadprivate" } + bar(); + #pragma omp parallel copyin(m) + bar(); +} diff --git a/gcc/testsuite/gcc.dg/gomp/critical-1.c b/gcc/testsuite/gcc.dg/gomp/critical-1.c new file mode 100644 index 00000000000..bdc7bad7b82 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/critical-1.c @@ -0,0 +1,28 @@ +/* { dg-do compile } */ +/* { dg-options "-fopenmp -fdump-tree-omplower" } */ + +extern void bar(int); + +void foo (void) +{ + #pragma omp critical + bar(0); + + /* Note that "name" is in its own namespace, thus this foo is not + the same as the function. */ + #pragma omp critical(foo) + { + bar(1); + bar(2); + } + + #pragma omp critical + #pragma omp critical(foo) + bar(3); +} + +/* { dg-final { scan-tree-dump-times "GOMP_critical_start" 2 "omplower" } } */ +/* { dg-final { scan-tree-dump-times "GOMP_critical_end" 2 "omplower" } } */ +/* { dg-final { scan-tree-dump-times "GOMP_critical_name_start" 2 "omplower" } } */ +/* { dg-final { scan-tree-dump-times "GOMP_critical_name_end" 2 "omplower" } } */ +/* { dg-final { cleanup-tree-dump "omplower" } } */ diff --git a/gcc/testsuite/gcc.dg/gomp/critical-2.c b/gcc/testsuite/gcc.dg/gomp/critical-2.c new file mode 100644 index 00000000000..d1ba3963f8f --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/critical-2.c @@ -0,0 +1,12 @@ +/* { dg-do compile } */ + +void f1(void) +{ + #pragma omp critical a /* { dg-error "expected" } */ + ; + #pragma omp critical ( /* { dg-error "expected identifier" } */ + ; + #pragma omp critical (a /* { dg-error "expected .\\)." } */ + ; + #pragma omp critical (a b) /* { dg-error "expected .\\)." } */ +} /* { dg-error "expected expression" } */ diff --git a/gcc/testsuite/gcc.dg/gomp/critical-3.c b/gcc/testsuite/gcc.dg/gomp/critical-3.c new file mode 100644 index 00000000000..9cd73ac046c --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/critical-3.c @@ -0,0 +1,11 @@ +// { dg-do compile } +// { dg-options "-fopenmp -fdump-tree-omplower" } + +void bar(void); +void foo(void) +{ + #pragma omp critical (xyzzy) + bar(); +} + +// { dg-final { scan-tree-dump-times "\\&\\.gomp_critical_user_xyzzy" 2 "omplower" } } diff --git a/gcc/testsuite/gcc.dg/gomp/empty.c b/gcc/testsuite/gcc.dg/gomp/empty.c new file mode 100644 index 00000000000..18af1d80d11 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/empty.c @@ -0,0 +1,12 @@ +/* { dg-do compile } */ +/* { dg-options "-O -fopenmp -fdump-tree-omplower" } */ + +main() +{ +#pragma omp parallel + {;} +} + +/* There should not be a GOMP_parallel_start call. */ +/* { dg-final { scan-tree-dump-times "GOMP_parallel_start" 0 "omplower"} } */ +/* { dg-final { cleanup-tree-dump "omplower" } } */ diff --git a/gcc/testsuite/gcc.dg/gomp/flush-1.c b/gcc/testsuite/gcc.dg/gomp/flush-1.c new file mode 100644 index 00000000000..d1a4d4a5bfb --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/flush-1.c @@ -0,0 +1,24 @@ +/* { dg-do compile } */ +/* { dg-options "-fopenmp -fdump-tree-gimple" } */ + +void f1(void) +{ + #pragma omp flush +} + +int x, y, z; + +void f2(_Bool p) +{ + if (p) + { + #pragma omp flush (x) + } + else + { + #pragma omp flush (x, y, z) + } +} + +/* { dg-final { scan-tree-dump-times "__sync_synchronize" 3 "gimple" } } */ +/* { dg-final { cleanup-tree-dump "gimple" } } */ diff --git a/gcc/testsuite/gcc.dg/gomp/flush-2.c b/gcc/testsuite/gcc.dg/gomp/flush-2.c new file mode 100644 index 00000000000..c2685d837ae --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/flush-2.c @@ -0,0 +1,10 @@ +/* { dg-do compile } */ + +void f1(void) +{ + #pragma omp flush a /* { dg-error "expected" } */ + #pragma omp flush ( /* { dg-error "expected identifier" } */ + #pragma omp flush (b /* { dg-error "undeclared|expected|for each" } */ + #pragma omp flush (c d) /* { dg-error "undeclared|expected" } */ + #pragma omp flush (e) /* { dg-error "undeclared" } */ +} diff --git a/gcc/testsuite/gcc.dg/gomp/for-1.c b/gcc/testsuite/gcc.dg/gomp/for-1.c new file mode 100644 index 00000000000..59b1528e492 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/for-1.c @@ -0,0 +1,48 @@ +/* { dg-do compile } */ + +void foo (int j, int k) +{ + int i; + + /* Valid loops. */ + #pragma omp for + for (i = 0; i < 10; i++) + baz (i); + + #pragma omp for + for (i = j; i <= 10; i+=4) + baz (i); + + #pragma omp for + for (i = j; i > 0; i = i - 1) + baz (j); + + #pragma omp for + for (i = j; i >= k; i--) + baz (i); + + /* Malformed parallel loops. */ + #pragma omp for + i = 0; /* { dg-error "for statement expected" } */ + for ( ; i < 10; ) + { + baz (i); + i++; + } + + #pragma omp for + for (i = 0; ; i--) /* { dg-error "missing controlling predicate" } */ + { + if (i >= 10) + break; /* { dg-error "break" } */ + baz (i); + } + + #pragma omp for + for (i = 0; i < 10 && j > 4; i-=3) /* { dg-error "invalid controlling predicate" } */ + baz (i); + + #pragma omp for + for (i = 0; i < 10; i-=3, j+=2) /* { dg-error "invalid increment expression" } */ + baz (i); +} diff --git a/gcc/testsuite/gcc.dg/gomp/for-10.c b/gcc/testsuite/gcc.dg/gomp/for-10.c new file mode 100644 index 00000000000..9dfac165b0a --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/for-10.c @@ -0,0 +1,17 @@ +/* { dg-do compile } */ +/* { dg-options "-fopenmp -fdump-tree-lower" } */ + +extern void bar(int); + +void foo (int n) +{ + int i; + + #pragma omp for schedule(runtime) ordered + for (i = 0; i < n; ++i) + bar(i); +} + +/* { dg-final { scan-tree-dump-times "GOMP_loop_ordered_runtime_start" 1 "lower" } } */ +/* { dg-final { scan-tree-dump-times "GOMP_loop_ordered_runtime_next" 1 "lower" } } */ +/* { dg-final { cleanup-tree-dump "lower" } } */ diff --git a/gcc/testsuite/gcc.dg/gomp/for-11.c b/gcc/testsuite/gcc.dg/gomp/for-11.c new file mode 100644 index 00000000000..8c747cdb981 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/for-11.c @@ -0,0 +1,73 @@ +/* { dg-do compile } */ +/* { dg-options "-std=c99 -fopenmp" } */ + +extern void baz (int); + +void foo (int j, int k) +{ + int i; + + /* Valid loops. */ + #pragma omp for + for (i = 0; i < 10; i++) + baz (i); + + #pragma omp for + for (i = j; i <= 10; i+=4) + baz (i); + + #pragma omp for + for (i = j; i > 0; i = i - 1) + baz (j); + + #pragma omp for + for (i = j; i >= k; i--) + baz (i); + + #pragma omp for + for (int l = j; l < 10; l++) + baz (l); + + /* Malformed parallel loops. */ + #pragma omp for + i = 0; /* { dg-error "for statement expected" } */ + for ( ; i < 10; ) + { + baz (i); + i++; + } + + #pragma omp for + for (i = 0; ; i--) /* { dg-error "missing controlling predicate" } */ + { + if (i >= 10) + break; /* { dg-error "break" } */ + baz (i); + } + + #pragma omp for + for (i = 0; + i < 10 && j > 4; /* { dg-error "invalid controlling predicate" } */ + i-=3) + baz (i); + + #pragma omp for + for (i = 0; + i < 10; + i-=3, j+=2) /* { dg-error "invalid increment expression" } */ + baz (i); + + int m = 0; + #pragma omp for + for (; m < 10; m++) /* { dg-error "expected" } */ + baz (m); + + m = 0; + #pragma omp for + for (int n = 0; m < 10; m++) /* { dg-error "invalid controlling predicate|invalid increment expression" } */ + baz (m); + + #pragma omp for + for (m = 0; m < 10; i++) /* { dg-error "invalid increment expression" } */ + baz (m); +} diff --git a/gcc/testsuite/gcc.dg/gomp/for-12.c b/gcc/testsuite/gcc.dg/gomp/for-12.c new file mode 100644 index 00000000000..98318d7d5ae --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/for-12.c @@ -0,0 +1,12 @@ +int foo (void) +{ + int i, a; + + a = 30; + + #pragma omp parallel for lastprivate (a) + for (i = 0; i < 10; i++) + a = a + i; + + return a; +} diff --git a/gcc/testsuite/gcc.dg/gomp/for-13.c b/gcc/testsuite/gcc.dg/gomp/for-13.c new file mode 100644 index 00000000000..16e971f1927 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/for-13.c @@ -0,0 +1,18 @@ +// At one point in development, a typo disabled the remapping of the +// for iteration variable as private. + +// { dg-do compile } +// { dg-options "-fopenmp -fdump-tree-lower" } + +extern void bar(int); +void foo(void) +{ + int i; + +#pragma omp parallel for default(none) + for (i = 0; i < 10; i++) + bar(i); +} + +// { dg-final { scan-tree-dump-times "omp_data_o" 0 "lower" } } +// { dg-final { cleanup-tree-dump "lower" } } diff --git a/gcc/testsuite/gcc.dg/gomp/for-14.c b/gcc/testsuite/gcc.dg/gomp/for-14.c new file mode 100644 index 00000000000..fb264137025 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/for-14.c @@ -0,0 +1,19 @@ +// { dg-do compile } + +extern int printf (const char *, ...); +extern void foo (int *); + +int main (void) +{ + double d = 6; + int i = 1, j = 6, k = 8; +#pragma omp parallel shared(d) private(i) num_threads (4) + { + i = 4; +#pragma omp for lastprivate(j) + for (j = 1; j <= k; j++) + printf ("%s %d %d %d %p %g\n", "Hello, World!", i, j, k, &j, d); + printf ("%s %d %g\n", "Hello, World!", i, d); + } + return 0; +} diff --git a/gcc/testsuite/gcc.dg/gomp/for-15.c b/gcc/testsuite/gcc.dg/gomp/for-15.c new file mode 100644 index 00000000000..28c2c926cb7 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/for-15.c @@ -0,0 +1,12 @@ +/* { dg-do compile } */ +/* { dg-options "-fopenmp" } */ + +void foo() +{ + long n = 10; + int i; +#pragma omp for + for (i=0; i < n; ++i) ; +#pragma omp for + for (i=0; n > i; ++i) ; +} diff --git a/gcc/testsuite/gcc.dg/gomp/for-16.c b/gcc/testsuite/gcc.dg/gomp/for-16.c new file mode 100644 index 00000000000..2f221e4c13a --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/for-16.c @@ -0,0 +1,18 @@ +// PR 24703 +// { dg-do compile } + +void work(int); +int work_param; +int sphinx_omp_thread_count; +int schedule_loop_cap; + +int measure_omp_parallel_for_dynamic (void) +{ + int j; + +#pragma omp parallel for schedule(dynamic) + for(j=0; j < sphinx_omp_thread_count * schedule_loop_cap; j++) + work(work_param); + + return 0; +} diff --git a/gcc/testsuite/gcc.dg/gomp/for-17.c b/gcc/testsuite/gcc.dg/gomp/for-17.c new file mode 100644 index 00000000000..1c51f1a4c86 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/for-17.c @@ -0,0 +1,67 @@ +/* { dg-do compile } */ +extern int bar (int); + +void +foo (void) +{ + int j, k = 1, l = 30, m = 4; + long int o = 4; + long long int p = 0; +#pragma omp for + for (j = k; j <= l; j += m - 1) + ; +#pragma omp for + for (j = k; j <= l; j += (m - 1)) + ; +#pragma omp for + for (j = k; j <= l; j += bar (m - 1)) + ; +#pragma omp for + for (j = k; j <= l; j = j + m - 1) + ; +#pragma omp for + for (j = k; j <= l; j = j + (m - 1)) + ; +#pragma omp for + for (j = k; j <= l; j = j + bar (m - 1)) + ; +#pragma omp for + for (j = ({ int n; n = k; n; }); j <= l; j++) + ; +#pragma omp for + for (j = k; j <= ({ int n; n = l; n; }); j++) + ; +#pragma omp for + for (j = k; j <= l; j += ({ int n; n = 1; n; })) + ; +#pragma omp for + for (j = k; j <= l; j += m + 1) + ; +#pragma omp for + for (j = k; j <= l; j += o) + ; +#pragma omp for + for (j = k; j <= l; j = j + o) + ; +#pragma omp for + for (j = k; j <= l; j = o + 1 + j) + ; +#pragma omp for + for (j = k; j <= l; j = o + m + j) + ; +#pragma omp for + for (j = k; j <= l; j += o + p) + ; +#pragma omp for + for (j = k; j <= l; j = j + o + p) + ; +#pragma omp for + for (j = l; j >= k; j -= o) + ; +#pragma omp for + for (j = l; j >= k; j -= p) + ; +#pragma omp for + for (j = l; j >= k; j -= o + p) + ; +} diff --git a/gcc/testsuite/gcc.dg/gomp/for-18.c b/gcc/testsuite/gcc.dg/gomp/for-18.c new file mode 100644 index 00000000000..c875a0c5f81 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/for-18.c @@ -0,0 +1,42 @@ +/* { dg-do compile } */ +/* { dg-options "-O -fopenmp -fdump-tree-omplower" } */ + +void +foo (int *a, int i) +{ + int j, k = 1, l = 30, m = 4; +#pragma omp parallel for num_threads (3 * i) schedule (dynamic, i * 4) + for (j = 0; j <= l; j++) + a[j] = 1; +#pragma omp parallel for num_threads (3 * i) schedule (dynamic, i * 4) + for (j = k; j <= l; j += (m - 1)) + a[j] = 2; +#pragma omp parallel for num_threads (3 * i) schedule (dynamic, 4) + for (j = 0; j <= l; j++) + a[j] = 3; +#pragma omp parallel for num_threads (3 * i) schedule (dynamic, 4) + for (j = k; j <= l; j += (m - 1)) + a[j] = 4; +} + +void +bar (int *a, int i) +{ + int j, k = 1, l = 30, m = 4; +#pragma omp parallel for num_threads (3 * i) schedule (guided, i * 4) + for (j = 0; j <= l; j++) + a[j] = 1; +#pragma omp parallel for num_threads (3 * i) schedule (guided, i * 4) + for (j = k; j <= l; j += (m - 1)) + a[j] = 2; +#pragma omp parallel for num_threads (3 * i) schedule (guided, 4) + for (j = 0; j <= l; j++) + a[j] = 3; +#pragma omp parallel for num_threads (3 * i) schedule (guided, 4) + for (j = k; j <= l; j += (m - 1)) + a[j] = 4; +} + +/* { dg-final { scan-tree-dump-times "GOMP_parallel_loop_dynamic_start" 4 "omplower" { xfail *-*-* } } } */ +/* { dg-final { scan-tree-dump-times "GOMP_parallel_loop_guided_start" 4 "omplower" { xfail *-*-* } } } */ +/* { dg-final { cleanup-tree-dump "omplower" } } */ diff --git a/gcc/testsuite/gcc.dg/gomp/for-19.c b/gcc/testsuite/gcc.dg/gomp/for-19.c new file mode 100644 index 00000000000..a202ba4799f --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/for-19.c @@ -0,0 +1,21 @@ +/* Verify that if GOMP_parallel_loop_dynamic_start is used, variables + mentioned in the INIT, COND and INCR expressions aren't unnecessarily + copied to the omp_fn function. */ +/* { dg-do compile } */ +/* { dg-options "-O -fopenmp -fdump-tree-gimple" } */ + +void foo (int *a, int i, int j, int k, int l, int m) +{ +#pragma omp parallel for num_threads (3 * i) schedule (dynamic, i * 4) + for (j = 0; j <= (6 * l + 4 * k); j++) + a[j] = 1; +#pragma omp parallel for num_threads (3 * i) schedule (dynamic, i * 4) + for (j = m; j <= l; j += (k + l - m)) + a[j] = 1; +} + +/* { dg-final { scan-tree-dump-times "shared\\(a\\)" 2 "gimple" } } */ +/* { dg-final { scan-tree-dump-times "shared\\(k\\)" 0 "gimple" { xfail *-*-* } } } */ +/* { dg-final { scan-tree-dump-times "shared\\(l\\)" 0 "gimple" { xfail *-*-* } } } */ +/* { dg-final { scan-tree-dump-times "shared\\(m\\)" 0 "gimple" { xfail *-*-* } } } */ +/* { dg-final { cleanup-tree-dump "gimple" } } */ diff --git a/gcc/testsuite/gcc.dg/gomp/for-2.c b/gcc/testsuite/gcc.dg/gomp/for-2.c new file mode 100644 index 00000000000..37e5929afa1 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/for-2.c @@ -0,0 +1,18 @@ +/* { dg-do compile } */ + +void foo() +{ + int i; + + #pragma omp for nowait + for (i = 0; i < 10; ++i) ; + + #pragma omp for nowait nowait /* { dg-error "too many" } */ + for (i = 0; i < 10; ++i) ; + + #pragma omp for ordered + for (i = 0; i < 10; ++i) ; + + #pragma omp for ordered ordered /* { dg-error "too many" } */ + for (i = 0; i < 10; ++i) ; +} diff --git a/gcc/testsuite/gcc.dg/gomp/for-3.c b/gcc/testsuite/gcc.dg/gomp/for-3.c new file mode 100644 index 00000000000..f3b0dbda7c8 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/for-3.c @@ -0,0 +1,62 @@ +// { dg-do compile } + +int bar (); + +void foo() +{ + int i; + + #pragma omp for schedule // { dg-error "expected '\\('" } + for (i = 0; i < 10; ++i) ; + + #pragma omp for schedule static // { dg-error "expected '\\('" } + for (i = 0; i < 10; ++i) ; + + #pragma omp for schedule ( // { dg-error "invalid schedule kind" } + for (i = 0; i < 10; ++i) ; + + #pragma omp for schedule ( static // { dg-error "expected" } + for (i = 0; i < 10; ++i) ; + + #pragma omp for schedule ( static ) + for (i = 0; i < 10; ++i) ; + + #pragma omp for schedule ( foo ) // { dg-error "invalid schedule kind" } + for (i = 0; i < 10; ++i) ; + + #pragma omp for schedule ( static 1 // { dg-error "expected" } + for (i = 0; i < 10; ++i) ; + + #pragma omp for schedule ( static 1 ) nowait // { dg-error "expected" } + for (i = 0; i < 10; ++i) ; + + #pragma omp for schedule ( static, 1 ) nowait + for (i = 0; i < 10; ++i) ; + + #pragma omp for schedule ( static, 1, 1 ) nowait // { dg-error "expected '\\)'" } + for (i = 0; i < 10; ++i) ; + + #pragma omp for schedule ( static, 1 + 1 ) nowait + for (i = 0; i < 10; ++i) ; + + #pragma omp for schedule ( static, 1.0 ) // { dg-error "expected integer expression" } + for (i = 0; i < 10; ++i) ; + + #pragma omp for schedule (dynamic) + for (i = 0; i < 10; ++i) ; + + #pragma omp for schedule (dynamic, bar ()) + for (i = 0; i < 10; ++i) ; + + #pragma omp for schedule (guided) + for (i = 0; i < 10; ++i) ; + + #pragma omp for schedule (guided, bar ()) + for (i = 0; i < 10; ++i) ; + + #pragma omp for schedule (runtime) + for (i = 0; i < 10; ++i) ; + + #pragma omp for schedule (runtime, bar ()) // { dg-error "does not take" } + for (i = 0; i < 10; ++i) ; +} diff --git a/gcc/testsuite/gcc.dg/gomp/for-4.c b/gcc/testsuite/gcc.dg/gomp/for-4.c new file mode 100644 index 00000000000..c5f1bb8d13d --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/for-4.c @@ -0,0 +1,17 @@ +/* { dg-do compile } */ +/* { dg-options "-fopenmp -fdump-tree-lower" } */ + +extern void bar(int); + +void foo (int n) +{ + int i; + + #pragma omp for schedule(dynamic) + for (i = 0; i < n; ++i) + bar(i); +} + +/* { dg-final { scan-tree-dump-times "GOMP_loop_dynamic_start" 1 "lower" } } */ +/* { dg-final { scan-tree-dump-times "GOMP_loop_dynamic_next" 1 "lower" } } */ +/* { dg-final { cleanup-tree-dump "lower" } } */ diff --git a/gcc/testsuite/gcc.dg/gomp/for-5.c b/gcc/testsuite/gcc.dg/gomp/for-5.c new file mode 100644 index 00000000000..6d9722a97f4 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/for-5.c @@ -0,0 +1,17 @@ +/* { dg-do compile } */ +/* { dg-options "-fopenmp -fdump-tree-lower" } */ + +extern void bar(int); + +void foo (int n) +{ + int i; + + #pragma omp for schedule(guided) + for (i = 0; i < n; ++i) + bar(i); +} + +/* { dg-final { scan-tree-dump-times "GOMP_loop_guided_start" 1 "lower" } } */ +/* { dg-final { scan-tree-dump-times "GOMP_loop_guided_next" 1 "lower" } } */ +/* { dg-final { cleanup-tree-dump "lower" } } */ diff --git a/gcc/testsuite/gcc.dg/gomp/for-6.c b/gcc/testsuite/gcc.dg/gomp/for-6.c new file mode 100644 index 00000000000..9361205e757 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/for-6.c @@ -0,0 +1,17 @@ +/* { dg-do compile } */ +/* { dg-options "-fopenmp -fdump-tree-lower" } */ + +extern void bar(int); + +void foo (int n) +{ + int i; + + #pragma omp for schedule(runtime) + for (i = 0; i < n; ++i) + bar(i); +} + +/* { dg-final { scan-tree-dump-times "GOMP_loop_runtime_start" 1 "lower" } } */ +/* { dg-final { scan-tree-dump-times "GOMP_loop_runtime_next" 1 "lower" } } */ +/* { dg-final { cleanup-tree-dump "lower" } } */ diff --git a/gcc/testsuite/gcc.dg/gomp/for-7.c b/gcc/testsuite/gcc.dg/gomp/for-7.c new file mode 100644 index 00000000000..b3eb997cb38 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/for-7.c @@ -0,0 +1,17 @@ +/* { dg-do compile } */ +/* { dg-options "-fopenmp -fdump-tree-lower" } */ + +extern void bar(int); + +void foo (int n) +{ + int i; + + #pragma omp for schedule(static) ordered + for (i = 0; i < n; ++i) + bar(i); +} + +/* { dg-final { scan-tree-dump-times "GOMP_loop_ordered_static_start" 1 "lower" } } */ +/* { dg-final { scan-tree-dump-times "GOMP_loop_ordered_static_next" 1 "lower" } } */ +/* { dg-final { cleanup-tree-dump "lower" } } */ diff --git a/gcc/testsuite/gcc.dg/gomp/for-8.c b/gcc/testsuite/gcc.dg/gomp/for-8.c new file mode 100644 index 00000000000..c1386ce4a41 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/for-8.c @@ -0,0 +1,17 @@ +/* { dg-do compile } */ +/* { dg-options "-fopenmp -fdump-tree-lower" } */ + +extern void bar(int); + +void foo (int n) +{ + int i; + + #pragma omp for schedule(dynamic) ordered + for (i = 0; i < n; ++i) + bar(i); +} + +/* { dg-final { scan-tree-dump-times "GOMP_loop_ordered_dynamic_start" 1 "lower" } } */ +/* { dg-final { scan-tree-dump-times "GOMP_loop_ordered_dynamic_next" 1 "lower" } } */ +/* { dg-final { cleanup-tree-dump "lower" } } */ diff --git a/gcc/testsuite/gcc.dg/gomp/for-9.c b/gcc/testsuite/gcc.dg/gomp/for-9.c new file mode 100644 index 00000000000..2a554d51527 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/for-9.c @@ -0,0 +1,17 @@ +/* { dg-do compile } */ +/* { dg-options "-fopenmp -fdump-tree-lower" } */ + +extern void bar(int); + +void foo (int n) +{ + int i; + + #pragma omp for schedule(guided) ordered + for (i = 0; i < n; ++i) + bar(i); +} + +/* { dg-final { scan-tree-dump-times "GOMP_loop_ordered_guided_start" 1 "lower" } } */ +/* { dg-final { scan-tree-dump-times "GOMP_loop_ordered_guided_next" 1 "lower" } } */ +/* { dg-final { cleanup-tree-dump "lower" } } */ diff --git a/gcc/testsuite/gcc.dg/gomp/gomp.exp b/gcc/testsuite/gcc.dg/gomp/gomp.exp new file mode 100644 index 00000000000..cb1b3384689 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/gomp.exp @@ -0,0 +1,14 @@ +# GCC testsuite that uses the `dg.exp' driver. + +# Load support procs. +load_lib gcc-dg.exp + +# Initialize `dg'. +dg-init + +# Main loop. +dg-runtest [lsort [find $srcdir/$subdir *.c]] \ + "" "-fopenmp" + +# All done. +dg-finish diff --git a/gcc/testsuite/gcc.dg/gomp/macro-1.c b/gcc/testsuite/gcc.dg/gomp/macro-1.c new file mode 100644 index 00000000000..b04610d2ccf --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/macro-1.c @@ -0,0 +1,10 @@ +// { dg-do compile } + +#define N 10 + +extern void bar(void); +void foo(void) +{ + #pragma omp parallel num_threads(N) + bar(); +} diff --git a/gcc/testsuite/gcc.dg/gomp/macro-2.c b/gcc/testsuite/gcc.dg/gomp/macro-2.c new file mode 100644 index 00000000000..75d6490cd7c --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/macro-2.c @@ -0,0 +1,14 @@ +// { dg-do compile } + +#define p parallel +#define s(x) shared(x##1, x##2) +#define d(x) default(x) + +void bar(int, int, int, int); +void foo(void) +{ + int a1, a2, b1, b2; + + #pragma omp p s(a) s(b) d(none) + bar(a1, a2, b1, b2); +} diff --git a/gcc/testsuite/gcc.dg/gomp/master-1.c b/gcc/testsuite/gcc.dg/gomp/master-1.c new file mode 100644 index 00000000000..2681c216a3f --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/master-1.c @@ -0,0 +1,22 @@ +/* { dg-do compile } */ + +extern void bar(int); + +void foo (void) +{ + #pragma omp master + bar(0); + + #pragma omp master + { + bar(1); + bar(2); + } + + /* Yes, this is legal -- structured-block contains statement contains + openmp-construct contains master-construct. */ + #pragma omp master + #pragma omp master + #pragma omp master + ; +} diff --git a/gcc/testsuite/gcc.dg/gomp/master-2.c b/gcc/testsuite/gcc.dg/gomp/master-2.c new file mode 100644 index 00000000000..3b0bdfc90a4 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/master-2.c @@ -0,0 +1,7 @@ +/* { dg-do compile } */ + +void f1(void) +{ + #pragma omp master asdf /* { dg-error "expected" } */ + #pragma omp master +} /* { dg-error "expected expression" } */ diff --git a/gcc/testsuite/gcc.dg/gomp/master-3.c b/gcc/testsuite/gcc.dg/gomp/master-3.c new file mode 100644 index 00000000000..37966106df5 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/master-3.c @@ -0,0 +1,13 @@ +/* { dg-do compile } */ +/* { dg-options "-fopenmp -fdump-tree-omplower" } */ + +extern void bar(int); + +void foo (void) +{ + #pragma omp master + bar(0); +} + +/* { dg-final { scan-tree-dump-times "omp_get_thread_num" 1 "omplower" } } */ +/* { dg-final { cleanup-tree-dump "omplower" } } */ diff --git a/gcc/testsuite/gcc.dg/gomp/num-threads-1.c b/gcc/testsuite/gcc.dg/gomp/num-threads-1.c new file mode 100644 index 00000000000..f792ef96bcd --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/num-threads-1.c @@ -0,0 +1,10 @@ +/* { dg-do compile } */ + +extern void bar(void); +void foo(void) +{ + #pragma omp parallel num_threads (0) /* { dg-warning "must be positive" } */ + { + bar (); + } +} diff --git a/gcc/testsuite/gcc.dg/gomp/omp-parallel-if.c b/gcc/testsuite/gcc.dg/gomp/omp-parallel-if.c new file mode 100644 index 00000000000..ca06aeef9ef --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/omp-parallel-if.c @@ -0,0 +1,24 @@ +/* { dg-do compile } */ + +extern int foo(void); +extern void bar(void); + +int main () +{ + /* Malformed uses of 'if' and 'num_threads'. */ + #pragma omp parallel if (foo () > 10) if (foo () == 3) /* { dg-error "too many" } */ + { + bar (); + } + + #pragma omp parallel num_threads (3) num_threads (20) /* { dg-error "too many" } */ + { + bar (); + } + + /* Valid uses of 'if' and 'num_threads'. */ + #pragma omp parallel if (foo () == 10) num_threads (foo ()) + { + bar (); + } +} diff --git a/gcc/testsuite/gcc.dg/gomp/ordered-1.c b/gcc/testsuite/gcc.dg/gomp/ordered-1.c new file mode 100644 index 00000000000..a1cd7f48602 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/ordered-1.c @@ -0,0 +1,20 @@ +/* { dg-do compile } */ +/* { dg-options "-fopenmp -fdump-tree-omplower" } */ + +extern void bar(int); + +void foo (void) +{ + #pragma omp ordered + bar(0); + + #pragma omp ordered + { + bar(1); + bar(2); + } +} + +/* { dg-final { scan-tree-dump-times "GOMP_ordered_start" 2 "omplower" } } */ +/* { dg-final { scan-tree-dump-times "GOMP_ordered_end" 2 "omplower" } } */ +/* { dg-final { cleanup-tree-dump "omplower" } } */ diff --git a/gcc/testsuite/gcc.dg/gomp/ordered-2.c b/gcc/testsuite/gcc.dg/gomp/ordered-2.c new file mode 100644 index 00000000000..2884b10261c --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/ordered-2.c @@ -0,0 +1,7 @@ +/* { dg-do compile } */ + +void f1(void) +{ + #pragma omp ordered asdf /* { dg-error "expected" } */ + #pragma omp ordered +} /* { dg-error "expected expression" } */ diff --git a/gcc/testsuite/gcc.dg/gomp/parallel-1.c b/gcc/testsuite/gcc.dg/gomp/parallel-1.c new file mode 100644 index 00000000000..c5c233b76eb --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/parallel-1.c @@ -0,0 +1,17 @@ +// { dg-do compile } + +void foo() +{ + int i; + + #pragma omp parallel + { + #pragma omp parallel + { + #pragma omp parallel + { + i++; + } + } + } +} diff --git a/gcc/testsuite/gcc.dg/gomp/parallel-2.c b/gcc/testsuite/gcc.dg/gomp/parallel-2.c new file mode 100644 index 00000000000..68e577766b1 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/parallel-2.c @@ -0,0 +1,17 @@ +// { dg-do compile } + +void foo() +{ + int i; + + #pragma omp parallel default(none) // { dg-error "enclosing" } + { + #pragma omp parallel + { + #pragma omp parallel default(none) // { dg-error "enclosing" } + { + i++; // { dg-error "not specified" } + } + } + } +} diff --git a/gcc/testsuite/gcc.dg/gomp/parallel-3.c b/gcc/testsuite/gcc.dg/gomp/parallel-3.c new file mode 100644 index 00000000000..633d7ba5998 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/parallel-3.c @@ -0,0 +1,15 @@ +// { dg-do compile } + +extern int printf (const char *, ...); + +int main (void) +{ + double d = 6; + int i = 1; +#pragma omp parallel shared(d) private(i) num_threads (4 + i) + { + i = 4; + printf ("%s %d %g\n", "Hello, World!", i, d); + } + return 0; +} diff --git a/gcc/testsuite/gcc.dg/gomp/parallel-4.c b/gcc/testsuite/gcc.dg/gomp/parallel-4.c new file mode 100644 index 00000000000..b8cd174a31b --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/parallel-4.c @@ -0,0 +1,11 @@ +// { dg-do compile } + +extern void bar (void); + +int main (void) +{ + int i; +#pragma omp parallel for nowait /* { dg-error "'nowait'" } */ + for (i = 0; i < 10; i++) + bar (); +} diff --git a/gcc/testsuite/gcc.dg/gomp/sections-1.c b/gcc/testsuite/gcc.dg/gomp/sections-1.c new file mode 100644 index 00000000000..43704908e43 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/sections-1.c @@ -0,0 +1,39 @@ +/* { dg-do compile } */ + +extern void bar(int); + +void f1(void) +{ + #pragma omp sections nowait + { + bar (1); + #pragma omp section + bar (2); + #pragma omp section + bar (3); + #pragma omp section + bar (4); + #pragma omp section + bar (5); + } +} + +void f2(void) +{ + #pragma omp sections + { + #pragma omp section + { + bar (1); + bar (1); + } + #pragma omp section + bar (2); + #pragma omp section + bar (3); + #pragma omp section + bar (4); + #pragma omp section + bar (5); + } +} diff --git a/gcc/testsuite/gcc.dg/gomp/sections-2.c b/gcc/testsuite/gcc.dg/gomp/sections-2.c new file mode 100644 index 00000000000..aabdfaf8069 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/sections-2.c @@ -0,0 +1,29 @@ +/* { dg-do compile } */ + +extern void bar(int); +void foo(void) +{ + #pragma omp sections + bar (0); // { dg-error "expected" } + + #pragma omp sections + { + } // { dg-error "expected" } + + #pragma omp sections + { + bar (1); + } + + #pragma omp sections + { + #pragma omp section + bar(2); + bar(3); // { dg-error "expected" } + bar(4); + #pragma omp section + bar(5); + bar(6); // { dg-error "expected" } + bar(7); + } +} diff --git a/gcc/testsuite/gcc.dg/gomp/sections-3.c b/gcc/testsuite/gcc.dg/gomp/sections-3.c new file mode 100644 index 00000000000..d8fb2a09d30 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/sections-3.c @@ -0,0 +1,15 @@ + +// { dg-do compile } + +extern void bar (void); + +int main (void) +{ + #pragma omp parallel sections nowait /* { dg-error "'nowait'" } */ + { + #pragma omp section + { bar(); } + #pragma omp section + { bar(); } + } +} diff --git a/gcc/testsuite/gcc.dg/gomp/sections-4.c b/gcc/testsuite/gcc.dg/gomp/sections-4.c new file mode 100644 index 00000000000..44e7de98c20 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/sections-4.c @@ -0,0 +1,13 @@ +/* PR c++/24613 */ +/* { dg-compile } */ + +#pragma omp section /* { dg-error "may only be used in" } */ + +int i; + +void +foo (void) +{ + #pragma omp section /* { dg-error "may only be used in" } */ + i++; +} diff --git a/gcc/testsuite/gcc.dg/gomp/sharing-1.c b/gcc/testsuite/gcc.dg/gomp/sharing-1.c new file mode 100644 index 00000000000..90d389b7292 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/sharing-1.c @@ -0,0 +1,58 @@ +/* { dg-do compile } */ +/* { dg-require-effective-target tls } */ + +#include <stdlib.h> + +int thrglobalvar; +#pragma omp threadprivate (thrglobalvar) +int globalvar; +const int constvar = 8; + +int +foo (int x) +{ + return x; +} + +int +bar (int *x) +{ + return *x; +} + +int +main (void) +{ + static int thrlocvar; +#pragma omp threadprivate (thrlocvar) + static int locvar; + static int *p; + int i, j, s, l; + + p = malloc (sizeof (int)); + if (p == NULL) + return 0; + *p = 7; + s = 6; + l = 0; +#pragma omp parallel for /* { dg-error "enclosing parallel" } */ \ + default (none) private (p) shared (s) + for (i = 0; i < 64; i++) + { + int k = foo (0); /* Predetermined - private (automatic var declared */ + k++; /* in scope of construct). */ + thrglobalvar++; /* Predetermined - threadprivate. */ + thrlocvar++; /* Predetermined - threadprivate. */ + foo (i); /* Predetermined - private (omp for loop variable). */ + foo (constvar); /* Predetermined - shared (const qualified type). */ + foo (*p); /* *p predetermined - shared (heap allocated */ + (*p)++; /* storage). */ + bar (p); /* Explicitly determined - private. */ + foo (s); /* Explicitly determined - shared. */ + globalvar++; /* { dg-error "not specified in" } */ + locvar++; /* { dg-error "not specified in" } */ + l++; /* { dg-error "not specified in" } */ + for (j = 0; j < 2; j++); /* { dg-error "not specified in" } */ + } + return 0; +} diff --git a/gcc/testsuite/gcc.dg/gomp/sharing-2.c b/gcc/testsuite/gcc.dg/gomp/sharing-2.c new file mode 100644 index 00000000000..33bbb3bade8 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/sharing-2.c @@ -0,0 +1,25 @@ +/* { dg-do compile } */ + +void +foo (void) +{ + int i; + int a[10]; + #pragma omp parallel private (i) shared (a) + { + i = 1; + #pragma omp parallel shared (a, i) + { + #pragma omp master + i = 2; + #pragma omp parallel private (i) shared (a) + { + for (i = 0; i < 10; i++) + a[i] = i + 1; + } + #pragma omp master + i = 3; + } + i = 4; + } +} diff --git a/gcc/testsuite/gcc.dg/gomp/sharing-3.c b/gcc/testsuite/gcc.dg/gomp/sharing-3.c new file mode 100644 index 00000000000..36f72e3abb9 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/sharing-3.c @@ -0,0 +1,17 @@ +/* { dg-do compile } */ + +#define N 50 +#define CHUNKSIZE 5 + +main () +{ + int i, chunk; + float c[N]; + + chunk = CHUNKSIZE; +#pragma omp parallel for shared (c, chunk) schedule (dynamic, chunk) + for (i = 0; i < N; i++) + c[i] = i; + + return 0; +} diff --git a/gcc/testsuite/gcc.dg/gomp/static.c b/gcc/testsuite/gcc.dg/gomp/static.c new file mode 100644 index 00000000000..8996d818b31 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/static.c @@ -0,0 +1,14 @@ +static int bork; + +void bar(void); + +void foobar (void) +{ +#pragma omp parallel + { +#pragma omp for lastprivate(bork) + for (bork = 0; bork < 100; bork++) { + bar(); + } + } +} diff --git a/gcc/testsuite/gcc.dg/gomp/tls-1.c b/gcc/testsuite/gcc.dg/gomp/tls-1.c new file mode 100644 index 00000000000..9dc102e7e61 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/tls-1.c @@ -0,0 +1,18 @@ +// { dg-do compile } +// { dg-require-effective-target tls } + +int tp1; +static int tp2; +extern int tp3; + +int tp4 = 1; +static int tp5 = 1; + +#pragma omp threadprivate (tp1, tp2, tp3, tp4, tp5) + +#pragma omp threadprivate (undef) // { dg-error "undeclared" } + +int tp6; +int foo(void) { return tp6; } + +#pragma omp threadprivate (tp6) // { dg-error "after first use" } diff --git a/gcc/testsuite/gcc.dg/gomp/tls-2.c b/gcc/testsuite/gcc.dg/gomp/tls-2.c new file mode 100644 index 00000000000..80275f9081c --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/tls-2.c @@ -0,0 +1,13 @@ +/* { dg-do compile } */ +/* { dg-require-effective-target tls } */ + +extern char buf[]; +#pragma omp threadprivate (buf) /* { dg-error "has incomplete type" } */ + +void +foo (void) +{ + int i; +#pragma omp threadprivate (i) /* { dg-error "automatic variable" } */ + i = 0; +} diff --git a/gcc/testsuite/gcc.dg/gomp/uninit-1.c b/gcc/testsuite/gcc.dg/gomp/uninit-1.c new file mode 100644 index 00000000000..223e617b461 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/uninit-1.c @@ -0,0 +1,13 @@ +// PR 24612 +// { dg-do compile } +// { dg-options "-O -Wuninitialized -fopenmp" } + +void foo() +{ + int i; +#pragma omp parallel shared(i) + { + i = 0; + ++i; + } +} diff --git a/gcc/testsuite/gcc.dg/gomp/vla-1.c b/gcc/testsuite/gcc.dg/gomp/vla-1.c new file mode 100644 index 00000000000..bb37f33e60f --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/vla-1.c @@ -0,0 +1,11 @@ +// { dg-do compile } + +void foo(int n) +{ + int A[n]; + + #pragma omp parallel default(none) // { dg-error "enclosing" } + { + A[0] = 0; // { dg-error "'A' not specified" } + } +} diff --git a/gcc/testsuite/gcc.dg/gomp/vla-2.c b/gcc/testsuite/gcc.dg/gomp/vla-2.c new file mode 100644 index 00000000000..fc18ffc5743 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/vla-2.c @@ -0,0 +1,11 @@ +// { dg-do compile } + +void foo(int n, int i) +{ + int A[n]; + + #pragma omp parallel private(A) + { + A[i] = 0; + } +} diff --git a/gcc/testsuite/gcc.dg/gomp/vla-3.c b/gcc/testsuite/gcc.dg/gomp/vla-3.c new file mode 100644 index 00000000000..b1677b833ef --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/vla-3.c @@ -0,0 +1,11 @@ +// { dg-do compile } + +void foo(int n, int i) +{ + int A[n]; + + #pragma omp parallel shared(A) + { + A[i] = sizeof(A); + } +} diff --git a/gcc/testsuite/gcc.dg/gomp/vla-4.c b/gcc/testsuite/gcc.dg/gomp/vla-4.c new file mode 100644 index 00000000000..2c3c0aa43b7 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/vla-4.c @@ -0,0 +1,11 @@ +// { dg-do compile } + +void foo(int n, int i) +{ + int A[n]; + + #pragma omp parallel firstprivate(A) + { + A[i] = 1; + } +} diff --git a/gcc/testsuite/gcc.dg/gomp/vla-5.c b/gcc/testsuite/gcc.dg/gomp/vla-5.c new file mode 100644 index 00000000000..6c6d5517af8 --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/vla-5.c @@ -0,0 +1,11 @@ +// { dg-do compile } + +void foo(int n, int i) +{ + int A[n]; + + #pragma omp parallel sections lastprivate(A) + { + A[i] = 1; + } +} diff --git a/gcc/tree-cfg.c b/gcc/tree-cfg.c index 4b34ddac4ef..45e78ddf2e2 100644 --- a/gcc/tree-cfg.c +++ b/gcc/tree-cfg.c @@ -2746,9 +2746,12 @@ set_bb_for_stmt (tree t, basic_block bb) stmt_ann_t ann = get_stmt_ann (t); ann->bb = bb; - /* If the statement is a label, add the label to block-to-labels map - so that we can speed up edge creation for GOTO_EXPRs. */ - if (TREE_CODE (t) == LABEL_EXPR) + /* If the statement is a label, add the label to block-to-labels + map so that we can speed up edge creation for GOTO_EXPRs. + Note that LABEL_TO_BLOCK_MAP may not exist if we are + currently expanding into RTL (in which case, this mapping is + unnecessary, anyway). */ + if (TREE_CODE (t) == LABEL_EXPR && !currently_expanding_to_rtl) { int uid; @@ -3475,25 +3478,20 @@ static bool tree_node_can_be_shared (tree t) { if (IS_TYPE_OR_DECL_P (t) - /* We check for constants explicitly since they are not considered - gimple invariants if they overflowed. */ - || CONSTANT_CLASS_P (t) || is_gimple_min_invariant (t) || TREE_CODE (t) == SSA_NAME - || t == error_mark_node) + || t == error_mark_node + || TREE_CODE (t) == IDENTIFIER_NODE) return true; if (TREE_CODE (t) == CASE_LABEL_EXPR) return true; while (((TREE_CODE (t) == ARRAY_REF || TREE_CODE (t) == ARRAY_RANGE_REF) - /* We check for constants explicitly since they are not considered - gimple invariants if they overflowed. */ - && (CONSTANT_CLASS_P (TREE_OPERAND (t, 1)) - || is_gimple_min_invariant (TREE_OPERAND (t, 1)))) - || (TREE_CODE (t) == COMPONENT_REF - || TREE_CODE (t) == REALPART_EXPR - || TREE_CODE (t) == IMAGPART_EXPR)) + && is_gimple_min_invariant (TREE_OPERAND (t, 1))) + || TREE_CODE (t) == COMPONENT_REF + || TREE_CODE (t) == REALPART_EXPR + || TREE_CODE (t) == IMAGPART_EXPR) t = TREE_OPERAND (t, 0); if (DECL_P (t)) @@ -3670,27 +3668,29 @@ tree_verify_flow_info (void) if (prev_stmt && DECL_NONLOCAL (LABEL_EXPR_LABEL (stmt))) { - error ("nonlocal label %s is not first " - "in a sequence of labels in bb %d", - IDENTIFIER_POINTER (DECL_NAME (LABEL_EXPR_LABEL (stmt))), - bb->index); + error ("nonlocal label "); + print_generic_expr (stderr, LABEL_EXPR_LABEL (stmt), 0); + fprintf (stderr, " is not first in a sequence of labels in bb %d", + bb->index); err = 1; } if (label_to_block (LABEL_EXPR_LABEL (stmt)) != bb) { - error ("label %s to block does not match in bb %d", - IDENTIFIER_POINTER (DECL_NAME (LABEL_EXPR_LABEL (stmt))), - bb->index); + error ("label "); + print_generic_expr (stderr, LABEL_EXPR_LABEL (stmt), 0); + fprintf (stderr, " to block does not match in bb %d", + bb->index); err = 1; } if (decl_function_context (LABEL_EXPR_LABEL (stmt)) != current_function_decl) { - error ("label %s has incorrect context in bb %d", - IDENTIFIER_POINTER (DECL_NAME (LABEL_EXPR_LABEL (stmt))), - bb->index); + error ("label "); + print_generic_expr (stderr, LABEL_EXPR_LABEL (stmt), 0); + fprintf (stderr, " has incorrect context in bb %d", + bb->index); err = 1; } } @@ -3712,12 +3712,13 @@ tree_verify_flow_info (void) if (TREE_CODE (stmt) == LABEL_EXPR) { - error ("label %s in the middle of basic block %d", - IDENTIFIER_POINTER (DECL_NAME (LABEL_EXPR_LABEL (stmt))), - bb->index); + error ("label "); + print_generic_expr (stderr, LABEL_EXPR_LABEL (stmt), 0); + fprintf (stderr, " in the middle of basic block %d", bb->index); err = 1; } } + bsi = bsi_last (bb); if (bsi_end_p (bsi)) continue; @@ -3854,7 +3855,7 @@ tree_verify_flow_info (void) } if (! tree_int_cst_lt (CASE_LOW (prev), CASE_LOW (c))) { - error ("case labels not sorted:"); + error ("case labels not sorted: "); print_generic_expr (stderr, prev, 0); fprintf (stderr," is greater than "); print_generic_expr (stderr, c, 0); @@ -4503,7 +4504,8 @@ dump_function_to_file (tree fn, FILE *file, int flags) bool ignore_topmost_bind = false, any_var = false; basic_block bb; tree chain; - + struct function *saved_cfun; + fprintf (file, "%s (", lang_hooks.decl_printable_name (fn, 2)); arg = DECL_ARGUMENTS (fn); @@ -4524,6 +4526,10 @@ dump_function_to_file (tree fn, FILE *file, int flags) return; } + /* Switch CFUN to point to FN. */ + saved_cfun = cfun; + cfun = DECL_STRUCT_FUNCTION (fn); + /* When GIMPLE is lowered, the variables are no longer available in BIND_EXPRs, so display them separately. */ if (cfun && cfun->decl == fn && cfun->unexpanded_var_list) @@ -4565,7 +4571,7 @@ dump_function_to_file (tree fn, FILE *file, int flags) /* Make a tree based dump. */ chain = DECL_SAVED_TREE (fn); - if (TREE_CODE (chain) == BIND_EXPR) + if (chain && TREE_CODE (chain) == BIND_EXPR) { if (ignore_topmost_bind) { @@ -4591,6 +4597,18 @@ dump_function_to_file (tree fn, FILE *file, int flags) } fprintf (file, "\n\n"); + + /* Restore CFUN. */ + cfun = saved_cfun; +} + + +/* Dump FUNCTION_DECL FN to stderr using FLAGS (see TDF_* in tree.h) */ + +void +debug_function (tree fn, int flags) +{ + dump_function_to_file (fn, stderr, flags); } diff --git a/gcc/tree-dump.h b/gcc/tree-dump.h index bc226937b30..00b14404f4a 100644 --- a/gcc/tree-dump.h +++ b/gcc/tree-dump.h @@ -91,6 +91,7 @@ extern void queue_and_dump_index (dump_info_p, const char *, tree, int); extern void queue_and_dump_type (dump_info_p, tree); extern void dump_function (enum tree_dump_index, tree); extern void dump_function_to_file (tree, FILE *, int); +extern void debug_function (tree, int); extern int dump_flag (dump_info_p, int, tree); extern unsigned int dump_register (const char *, const char *, const char *, diff --git a/gcc/tree-gimple.c b/gcc/tree-gimple.c index 3b70905cf84..5edf55833f3 100644 --- a/gcc/tree-gimple.c +++ b/gcc/tree-gimple.c @@ -216,6 +216,14 @@ is_gimple_stmt (tree t) case RESX_EXPR: case PHI_NODE: case STATEMENT_LIST: + case OMP_PARALLEL: + case OMP_FOR: + case OMP_SECTIONS: + case OMP_SECTION: + case OMP_SINGLE: + case OMP_MASTER: + case OMP_ORDERED: + case OMP_CRITICAL: /* These are always void. */ return true; diff --git a/gcc/tree-gimple.h b/gcc/tree-gimple.h index 83c17fbad7a..ff1a6d20a39 100644 --- a/gcc/tree-gimple.h +++ b/gcc/tree-gimple.h @@ -109,6 +109,21 @@ enum gimplify_status { GS_ALL_DONE = 1 /* The expression is fully gimplified. */ }; +/* Type of parallel constructs. Used to decide what runtime function + to use for launching children threads and the gimplification + strategy. */ + +enum omp_parallel_type { + IS_NOT_PARALLEL = 0, + + /* Regular omp parallel */ + IS_PARALLEL, + + /* Combined parallel + workshare (parallel loop and parallel + sections). */ + IS_COMBINED_PARALLEL +}; + extern enum gimplify_status gimplify_expr (tree *, tree *, tree *, bool (*) (tree), fallback_t); extern void gimplify_type_sizes (tree, tree *); @@ -130,8 +145,77 @@ extern tree alloc_stmt_list (void); extern void free_stmt_list (tree); extern tree force_labels_r (tree *, int *, void *); extern enum gimplify_status gimplify_va_arg_expr (tree *, tree *, tree *); +struct gimplify_omp_ctx; +extern void omp_firstprivatize_variable (struct gimplify_omp_ctx *, tree); + +/* In omp-low.c. */ +extern tree find_omp_clause (tree, enum tree_code); +extern void diagnose_omp_structured_block_errors (tree); +extern tree omp_reduction_init (tree, tree); +enum omp_parallel_type determine_parallel_type (tree stmt); /* In tree-nested.c. */ extern void lower_nested_functions (tree); +extern void insert_field_into_struct (tree, tree); + +/* Convenience routines to walk all statements of a gimple function. + The difference between these walkers and the generic walk_tree is + that walk_stmt provides context information to the callback + routine to know whether it is currently on the LHS or RHS of an + assignment (IS_LHS) or contexts where only GIMPLE values are + allowed (VAL_ONLY). + + This is useful in walkers that need to re-write sub-expressions + inside statements while making sure the result is still in GIMPLE + form. + + Note that this is useful exclusively before the code is converted + into SSA form. Once the program is in SSA form, the standard + operand interface should be used to analyze/modify statements. */ + +struct walk_stmt_info +{ + /* For each statement, we invoke CALLBACK via walk_tree. The passed + data is a walk_stmt_info structure. */ + walk_tree_fn callback; + + /* Points to the current statement being walked. */ + tree_stmt_iterator tsi; + + /* Additional data that CALLBACK may want to carry through the + recursion. */ + void *info; + + /* Indicates whether the *TP being examined may be replaced + with something that matches is_gimple_val (if true) or something + slightly more complicated (if false). "Something" technically + means the common subset of is_gimple_lvalue and is_gimple_rhs, + but we never try to form anything more complicated than that, so + we don't bother checking. + + Also note that CALLBACK should update this flag while walking the + sub-expressions of a statement. For instance, when walking the + statement 'foo (&var)', the flag VAL_ONLY will initially be set + to true, however, when walking &var, the operand of that + ADDR_EXPR does not need to be a GIMPLE value. */ + bool val_only; + + /* True if we are currently walking the LHS of an assignment. */ + bool is_lhs; + + /* Optional. Set to true by CALLBACK if it made any changes. */ + bool changed; + + /* True if we're interested in seeing BIND_EXPRs. */ + bool want_bind_expr; + + /* True if we're interested in seeing RETURN_EXPRs. */ + bool want_return_expr; + + /* True if we're interested in location information. */ + bool want_locations; +}; + +void walk_stmts (struct walk_stmt_info *, tree *); #endif /* _TREE_SIMPLE_H */ diff --git a/gcc/tree-iterator.c b/gcc/tree-iterator.c index 3c2acd38ea7..c4c30104731 100644 --- a/gcc/tree-iterator.c +++ b/gcc/tree-iterator.c @@ -330,8 +330,9 @@ expr_last (tree expr) return expr; } -/* If EXPR is a single statement, naked or in a STATEMENT_LIST, then - return it. Otherwise return NULL. */ +/* If EXPR is a single statement return it. If EXPR is a + STATEMENT_LIST containing exactly one statement S, return S. + Otherwise, return NULL. */ tree expr_only (tree expr) diff --git a/gcc/tree-nested.c b/gcc/tree-nested.c index d5309ec7a6e..4c65a1dc227 100644 --- a/gcc/tree-nested.c +++ b/gcc/tree-nested.c @@ -89,9 +89,13 @@ struct nesting_info GTY ((chain_next ("%h.next"))) struct nesting_info *inner; struct nesting_info *next; + htab_t GTY ((param_is (struct var_map_elt))) field_map; htab_t GTY ((param_is (struct var_map_elt))) var_map; + bitmap suppress_expansion; + tree context; tree new_local_var_chain; + tree debug_var_chain; tree frame_type; tree frame_decl; tree chain_field; @@ -180,7 +184,7 @@ build_addr (tree exp, tree context) /* Insert FIELD into TYPE, sorted by alignment requirements. */ -static void +void insert_field_into_struct (tree type, tree field) { tree *p; @@ -264,7 +268,7 @@ lookup_field_for_decl (struct nesting_info *info, tree decl, tree field; dummy.old = decl; - slot = htab_find_slot (info->var_map, &dummy, insert); + slot = htab_find_slot (info->field_map, &dummy, insert); if (!slot) { gcc_assert (insert != INSERT); @@ -538,37 +542,21 @@ get_nl_goto_field (struct nesting_info *info) return field; } -/* Convenience routines to walk all statements of a gimple function. - - For each statement, we invoke CALLBACK via walk_tree. The passed - data is a walk_stmt_info structure. Of note here is a TSI that - points to the current statement being walked. The VAL_ONLY flag - that indicates whether the *TP being examined may be replaced - with something that matches is_gimple_val (if true) or something - slightly more complicated (if false). "Something" technically - means the common subset of is_gimple_lvalue and is_gimple_rhs, - but we never try to form anything more complicated than that, so - we don't bother checking. */ - -struct walk_stmt_info -{ - walk_tree_fn callback; - tree_stmt_iterator tsi; - struct nesting_info *info; - bool val_only; - bool is_lhs; - bool changed; -}; +/* Iterate over all sub-statements of *TP calling walk_tree with + WI->CALLBACK for every sub-expression in each statement found. */ -/* A subroutine of walk_function. Iterate over all sub-statements of *TP. */ - -static void +void walk_stmts (struct walk_stmt_info *wi, tree *tp) { tree t = *tp; + int walk_subtrees; + if (!t) return; + if (wi->want_locations && EXPR_HAS_LOCATION (t)) + input_location = EXPR_LOCATION (t); + switch (TREE_CODE (t)) { case STATEMENT_LIST: @@ -598,11 +586,26 @@ walk_stmts (struct walk_stmt_info *wi, tree *tp) walk_stmts (wi, &TREE_OPERAND (t, 0)); walk_stmts (wi, &TREE_OPERAND (t, 1)); break; + case BIND_EXPR: + if (wi->want_bind_expr) + { + walk_subtrees = 1; + wi->callback (tp, &walk_subtrees, wi); + if (!walk_subtrees) + break; + } walk_stmts (wi, &BIND_EXPR_BODY (t)); break; case RETURN_EXPR: + if (wi->want_return_expr) + { + walk_subtrees = 1; + wi->callback (tp, &walk_subtrees, wi); + if (!walk_subtrees) + break; + } walk_stmts (wi, &TREE_OPERAND (t, 0)); break; @@ -628,10 +631,10 @@ walk_stmts (struct walk_stmt_info *wi, tree *tp) } } -/* Invoke CALLBACK on all statements of INFO->CONTEXT. */ +/* Invoke CALLBACK on all statements of *STMT_P. */ static void -walk_function (walk_tree_fn callback, struct nesting_info *info) +walk_body (walk_tree_fn callback, struct nesting_info *info, tree *stmt_p) { struct walk_stmt_info wi; @@ -640,7 +643,15 @@ walk_function (walk_tree_fn callback, struct nesting_info *info) wi.info = info; wi.val_only = true; - walk_stmts (&wi, &DECL_SAVED_TREE (info->context)); + walk_stmts (&wi, stmt_p); +} + +/* Invoke CALLBACK on all statements of INFO->CONTEXT. */ + +static inline void +walk_function (walk_tree_fn callback, struct nesting_info *info) +{ + walk_body (callback, info, &DECL_SAVED_TREE (info->context)); } /* Similarly for ROOT and all functions nested underneath, depth first. */ @@ -707,7 +718,9 @@ static struct nesting_info * create_nesting_tree (struct cgraph_node *cgn) { struct nesting_info *info = GGC_CNEW (struct nesting_info); + info->field_map = htab_create_ggc (7, var_map_hash, var_map_eq, ggc_free); info->var_map = htab_create_ggc (7, var_map_hash, var_map_eq, ggc_free); + info->suppress_expansion = BITMAP_GGC_ALLOC (); info->context = cgn->decl; for (cgn = cgn->nested; cgn ; cgn = cgn->next_nested) @@ -794,6 +807,80 @@ get_frame_field (struct nesting_info *info, tree target_context, return x; } +/* A subroutine of convert_nonlocal_reference. Create a local variable + in the nested function with DECL_VALUE_EXPR set to reference the true + variable in the parent function. This is used both for debug info + and in OpenMP lowering. */ + +static tree +get_nonlocal_debug_decl (struct nesting_info *info, tree decl) +{ + struct var_map_elt *elt, dummy; + tree target_context; + struct nesting_info *i; + tree x, field, new_decl; + void **slot; + + dummy.old = decl; + slot = htab_find_slot (info->var_map, &dummy, INSERT); + elt = *slot; + + if (elt) + return elt->new; + + target_context = decl_function_context (decl); + + /* A copy of the code in get_frame_field, but without the temporaries. */ + if (info->context == target_context) + { + /* Make sure frame_decl gets created. */ + (void) get_frame_type (info); + x = info->frame_decl; + i = info; + } + else + { + x = get_chain_decl (info); + for (i = info->outer; i->context != target_context; i = i->outer) + { + field = get_chain_field (i); + x = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (x)), x); + x = build3 (COMPONENT_REF, TREE_TYPE (field), x, field, NULL_TREE); + } + x = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (x)), x); + } + + field = lookup_field_for_decl (i, decl, INSERT); + x = build3 (COMPONENT_REF, TREE_TYPE (field), x, field, NULL_TREE); + if (use_pointer_in_frame (decl)) + x = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (x)), x); + + /* ??? We should be remapping types as well, surely. */ + new_decl = build_decl (VAR_DECL, DECL_NAME (decl), TREE_TYPE (decl)); + DECL_CONTEXT (new_decl) = info->context; + DECL_SOURCE_LOCATION (new_decl) = DECL_SOURCE_LOCATION (decl); + DECL_ARTIFICIAL (new_decl) = DECL_ARTIFICIAL (decl); + DECL_IGNORED_P (new_decl) = DECL_IGNORED_P (decl); + TREE_THIS_VOLATILE (new_decl) = TREE_THIS_VOLATILE (decl); + TREE_SIDE_EFFECTS (new_decl) = TREE_SIDE_EFFECTS (decl); + TREE_READONLY (new_decl) = TREE_READONLY (decl); + TREE_ADDRESSABLE (new_decl) = TREE_ADDRESSABLE (decl); + DECL_SEEN_IN_BIND_EXPR_P (new_decl) = 1; + + SET_DECL_VALUE_EXPR (new_decl, x); + DECL_HAS_VALUE_EXPR_P (new_decl) = 1; + + elt = ggc_alloc (sizeof (*elt)); + elt->old = decl; + elt->new = new_decl; + *slot = elt; + + TREE_CHAIN (new_decl) = info->debug_var_chain; + info->debug_var_chain = new_decl; + + return new_decl; +} + /* Called via walk_function+walk_tree, rewrite all references to VAR and PARM_DECLs that belong to outer functions. @@ -802,12 +889,16 @@ get_frame_field (struct nesting_info *info, tree target_context, be CHAIN->FOO. For two levels it'll be CHAIN->__chain->FOO. Further indirections apply to decls for which use_pointer_in_frame is true. */ +static bool convert_nonlocal_omp_clauses (tree *, struct walk_stmt_info *); + static tree convert_nonlocal_reference (tree *tp, int *walk_subtrees, void *data) { struct walk_stmt_info *wi = (struct walk_stmt_info *) data; struct nesting_info *info = wi->info; tree t = *tp; + tree save_local_var_chain; + bitmap save_suppress; *walk_subtrees = 0; switch (TREE_CODE (t)) @@ -821,19 +912,23 @@ convert_nonlocal_reference (tree *tp, int *walk_subtrees, void *data) case PARM_DECL: if (decl_function_context (t) != info->context) { - tree target_context = decl_function_context (t); - struct nesting_info *i; tree x; wi->changed = true; - for (i = info->outer; i->context != target_context; i = i->outer) - continue; - x = lookup_field_for_decl (i, t, INSERT); - x = get_frame_field (info, target_context, x, &wi->tsi); - if (use_pointer_in_frame (t)) + x = get_nonlocal_debug_decl (info, t); + if (!bitmap_bit_p (info->suppress_expansion, DECL_UID (t))) { - x = init_tmp_var (info, x, &wi->tsi); - x = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (x)), x); + tree target_context = decl_function_context (t); + struct nesting_info *i; + for (i = info->outer; i->context != target_context; i = i->outer) + continue; + x = lookup_field_for_decl (i, t, INSERT); + x = get_frame_field (info, target_context, x, &wi->tsi); + if (use_pointer_in_frame (t)) + { + x = init_tmp_var (info, x, &wi->tsi); + x = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (x)), x); + } } if (wi->val_only) @@ -935,6 +1030,43 @@ convert_nonlocal_reference (tree *tp, int *walk_subtrees, void *data) walk_tree (tp, convert_nonlocal_reference, wi, NULL); break; + case OMP_PARALLEL: + save_suppress = info->suppress_expansion; + if (convert_nonlocal_omp_clauses (&OMP_PARALLEL_CLAUSES (t), wi)) + { + tree c; + c = get_chain_decl (info); + c = build1 (OMP_CLAUSE_FIRSTPRIVATE, void_type_node, c); + OMP_CLAUSE_CHAIN (c) = OMP_PARALLEL_CLAUSES (t); + OMP_PARALLEL_CLAUSES (t) = c; + } + + save_local_var_chain = info->new_local_var_chain; + info->new_local_var_chain = NULL; + + walk_body (convert_nonlocal_reference, info, &OMP_PARALLEL_BODY (t)); + + if (info->new_local_var_chain) + declare_tmp_vars (info->new_local_var_chain, OMP_PARALLEL_BODY (t)); + info->new_local_var_chain = save_local_var_chain; + info->suppress_expansion = save_suppress; + break; + + case OMP_FOR: + case OMP_SECTIONS: + case OMP_SINGLE: + save_suppress = info->suppress_expansion; + convert_nonlocal_omp_clauses (&OMP_CLAUSES (t), wi); + walk_body (convert_nonlocal_reference, info, &OMP_BODY (t)); + info->suppress_expansion = save_suppress; + break; + + case OMP_SECTION: + case OMP_MASTER: + case OMP_ORDERED: + walk_body (convert_nonlocal_reference, info, &OMP_BODY (t)); + break; + default: if (!IS_TYPE_OR_DECL_P (t)) { @@ -948,10 +1080,119 @@ convert_nonlocal_reference (tree *tp, int *walk_subtrees, void *data) return NULL_TREE; } +static bool +convert_nonlocal_omp_clauses (tree *pclauses, struct walk_stmt_info *wi) +{ + struct nesting_info *info = wi->info; + bool need_chain = false; + tree clause, decl; + int dummy; + bitmap new_suppress; + + new_suppress = BITMAP_GGC_ALLOC (); + bitmap_copy (new_suppress, info->suppress_expansion); + + for (clause = *pclauses; clause ; clause = OMP_CLAUSE_CHAIN (clause)) + { + switch (TREE_CODE (clause)) + { + case OMP_CLAUSE_PRIVATE: + case OMP_CLAUSE_FIRSTPRIVATE: + case OMP_CLAUSE_LASTPRIVATE: + case OMP_CLAUSE_REDUCTION: + case OMP_CLAUSE_COPYPRIVATE: + case OMP_CLAUSE_SHARED: + decl = OMP_CLAUSE_DECL (clause); + if (decl_function_context (decl) != info->context) + { + bitmap_set_bit (new_suppress, DECL_UID (decl)); + OMP_CLAUSE_DECL (clause) = get_nonlocal_debug_decl (info, decl); + need_chain = true; + } + break; + + case OMP_CLAUSE_SCHEDULE: + if (OMP_CLAUSE_SCHEDULE_CHUNK_EXPR (clause) == NULL) + break; + /* FALLTHRU */ + case OMP_CLAUSE_IF: + case OMP_CLAUSE_NUM_THREADS: + wi->val_only = true; + wi->is_lhs = false; + convert_nonlocal_reference (&TREE_OPERAND (clause, 0), &dummy, wi); + break; + + case OMP_CLAUSE_NOWAIT: + case OMP_CLAUSE_ORDERED: + case OMP_CLAUSE_DEFAULT: + case OMP_CLAUSE_COPYIN: + break; + + default: + gcc_unreachable (); + } + } + + info->suppress_expansion = new_suppress; + + return need_chain; +} + +/* A subroutine of convert_local_reference. Create a local variable + in the parent function with DECL_VALUE_EXPR set to reference the + field in FRAME. This is used both for debug info and in OpenMP + lowering. */ + +static tree +get_local_debug_decl (struct nesting_info *info, tree decl, tree field) +{ + struct var_map_elt *elt, dummy; + tree x, new_decl; + void **slot; + + dummy.old = decl; + slot = htab_find_slot (info->var_map, &dummy, INSERT); + elt = *slot; + + if (elt) + return elt->new; + + /* Make sure frame_decl gets created. */ + (void) get_frame_type (info); + x = info->frame_decl; + x = build3 (COMPONENT_REF, TREE_TYPE (field), x, field, NULL_TREE); + + new_decl = build_decl (VAR_DECL, DECL_NAME (decl), TREE_TYPE (decl)); + DECL_CONTEXT (new_decl) = info->context; + DECL_SOURCE_LOCATION (new_decl) = DECL_SOURCE_LOCATION (decl); + DECL_ARTIFICIAL (new_decl) = DECL_ARTIFICIAL (decl); + DECL_IGNORED_P (new_decl) = DECL_IGNORED_P (decl); + TREE_THIS_VOLATILE (new_decl) = TREE_THIS_VOLATILE (decl); + TREE_SIDE_EFFECTS (new_decl) = TREE_SIDE_EFFECTS (decl); + TREE_READONLY (new_decl) = TREE_READONLY (decl); + TREE_ADDRESSABLE (new_decl) = TREE_ADDRESSABLE (decl); + DECL_SEEN_IN_BIND_EXPR_P (new_decl) = 1; + + SET_DECL_VALUE_EXPR (new_decl, x); + DECL_HAS_VALUE_EXPR_P (new_decl) = 1; + + elt = ggc_alloc (sizeof (*elt)); + elt->old = decl; + elt->new = new_decl; + *slot = elt; + + TREE_CHAIN (new_decl) = info->debug_var_chain; + info->debug_var_chain = new_decl; + + return new_decl; +} + /* Called via walk_function+walk_tree, rewrite all references to VAR and PARM_DECLs that were referenced by inner nested functions. The rewrite will be a structure reference to the local frame variable. */ +static bool convert_local_omp_clauses (tree *, struct walk_stmt_info *); + static tree convert_local_reference (tree *tp, int *walk_subtrees, void *data) { @@ -959,6 +1200,8 @@ convert_local_reference (tree *tp, int *walk_subtrees, void *data) struct nesting_info *info = wi->info; tree t = *tp, field, x; bool save_val_only; + tree save_local_var_chain; + bitmap save_suppress; *walk_subtrees = 0; switch (TREE_CODE (t)) @@ -984,7 +1227,9 @@ convert_local_reference (tree *tp, int *walk_subtrees, void *data) break; wi->changed = true; - x = get_frame_field (info, info->context, field, &wi->tsi); + x = get_local_debug_decl (info, t, field); + if (!bitmap_bit_p (info->suppress_expansion, DECL_UID (t))) + x = get_frame_field (info, info->context, field, &wi->tsi); if (wi->val_only) { @@ -1066,6 +1311,43 @@ convert_local_reference (tree *tp, int *walk_subtrees, void *data) wi->val_only = save_val_only; break; + case OMP_PARALLEL: + save_suppress = info->suppress_expansion; + if (convert_local_omp_clauses (&OMP_PARALLEL_CLAUSES (t), wi)) + { + tree c; + (void) get_frame_type (info); + c = build1 (OMP_CLAUSE_SHARED, void_type_node, info->frame_decl); + OMP_CLAUSE_CHAIN (c) = OMP_PARALLEL_CLAUSES (t); + OMP_PARALLEL_CLAUSES (t) = c; + } + + save_local_var_chain = info->new_local_var_chain; + info->new_local_var_chain = NULL; + + walk_body (convert_local_reference, info, &OMP_PARALLEL_BODY (t)); + + if (info->new_local_var_chain) + declare_tmp_vars (info->new_local_var_chain, OMP_PARALLEL_BODY (t)); + info->new_local_var_chain = save_local_var_chain; + info->suppress_expansion = save_suppress; + break; + + case OMP_FOR: + case OMP_SECTIONS: + case OMP_SINGLE: + save_suppress = info->suppress_expansion; + convert_local_omp_clauses (&OMP_CLAUSES (t), wi); + walk_body (convert_local_reference, info, &OMP_BODY (t)); + info->suppress_expansion = save_suppress; + break; + + case OMP_SECTION: + case OMP_MASTER: + case OMP_ORDERED: + walk_body (convert_local_reference, info, &OMP_BODY (t)); + break; + default: if (!IS_TYPE_OR_DECL_P (t)) { @@ -1079,6 +1361,70 @@ convert_local_reference (tree *tp, int *walk_subtrees, void *data) return NULL_TREE; } +static bool +convert_local_omp_clauses (tree *pclauses, struct walk_stmt_info *wi) +{ + struct nesting_info *info = wi->info; + bool need_frame = false; + tree clause, decl; + int dummy; + bitmap new_suppress; + + new_suppress = BITMAP_GGC_ALLOC (); + bitmap_copy (new_suppress, info->suppress_expansion); + + for (clause = *pclauses; clause ; clause = OMP_CLAUSE_CHAIN (clause)) + { + switch (TREE_CODE (clause)) + { + case OMP_CLAUSE_PRIVATE: + case OMP_CLAUSE_FIRSTPRIVATE: + case OMP_CLAUSE_LASTPRIVATE: + case OMP_CLAUSE_REDUCTION: + case OMP_CLAUSE_COPYPRIVATE: + case OMP_CLAUSE_SHARED: + decl = OMP_CLAUSE_DECL (clause); + if (decl_function_context (decl) == info->context + && !use_pointer_in_frame (decl)) + { + tree field = lookup_field_for_decl (info, decl, NO_INSERT); + if (field) + { + bitmap_set_bit (new_suppress, DECL_UID (decl)); + OMP_CLAUSE_DECL (clause) + = get_local_debug_decl (info, decl, field); + need_frame = true; + } + } + break; + + case OMP_CLAUSE_SCHEDULE: + if (OMP_CLAUSE_SCHEDULE_CHUNK_EXPR (clause) == NULL) + break; + /* FALLTHRU */ + case OMP_CLAUSE_IF: + case OMP_CLAUSE_NUM_THREADS: + wi->val_only = true; + wi->is_lhs = false; + convert_local_reference (&TREE_OPERAND (clause, 0), &dummy, wi); + break; + + case OMP_CLAUSE_NOWAIT: + case OMP_CLAUSE_ORDERED: + case OMP_CLAUSE_DEFAULT: + case OMP_CLAUSE_COPYIN: + break; + + default: + gcc_unreachable (); + } + } + + info->suppress_expansion = new_suppress; + + return need_frame; +} + /* Called via walk_function+walk_tree, rewrite all GOTO_EXPRs that reference labels from outer functions. The rewrite will be a call to __builtin_nonlocal_goto. */ @@ -1292,6 +1638,15 @@ convert_call_expr (tree *tp, int *walk_subtrees, void *data) *walk_subtrees = 1; break; + case OMP_FOR: + case OMP_SECTIONS: + case OMP_SINGLE: + case OMP_MASTER: + case OMP_ORDERED: + case OMP_CRITICAL: + walk_body (convert_call_expr, info, &OMP_BODY (t)); + break; + default: break; } @@ -1336,7 +1691,6 @@ finalize_nesting_tree_1 (struct nesting_info *root) tree stmt_list = NULL; tree context = root->context; struct function *sf; - struct cgraph_node *node; /* If we created a non-local frame type or decl, we need to lay them out at this time. */ @@ -1449,10 +1803,33 @@ finalize_nesting_tree_1 (struct nesting_info *root) if (root->new_local_var_chain) declare_tmp_vars (root->new_local_var_chain, DECL_SAVED_TREE (root->context)); + if (root->debug_var_chain) + declare_tmp_vars (root->debug_var_chain, + DECL_SAVED_TREE (root->context)); /* Dump the translated tree function. */ dump_function (TDI_nested, root->context); - node = cgraph_node (root->context); +} + +static void +finalize_nesting_tree (struct nesting_info *root) +{ + do + { + if (root->inner) + finalize_nesting_tree (root->inner); + finalize_nesting_tree_1 (root); + root = root->next; + } + while (root); +} + +/* Unnest the nodes and pass them to cgraph. */ + +static void +unnest_nesting_tree_1 (struct nesting_info *root) +{ + struct cgraph_node *node = cgraph_node (root->context); /* For nested functions update the cgraph to reflect unnesting. We also delay finalizing of these functions up to this point. */ @@ -1464,13 +1841,13 @@ finalize_nesting_tree_1 (struct nesting_info *root) } static void -finalize_nesting_tree (struct nesting_info *root) +unnest_nesting_tree (struct nesting_info *root) { do { if (root->inner) - finalize_nesting_tree (root->inner); - finalize_nesting_tree_1 (root); + unnest_nesting_tree (root->inner); + unnest_nesting_tree_1 (root); root = root->next; } while (root); @@ -1516,6 +1893,7 @@ lower_nested_functions (tree fndecl) walk_all_functions (convert_nl_goto_receiver, root); convert_all_function_calls (root); finalize_nesting_tree (root); + unnest_nesting_tree (root); free_nesting_tree (root); root = NULL; } diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h index 3c846274449..90327ba8864 100644 --- a/gcc/tree-pass.h +++ b/gcc/tree-pass.h @@ -68,6 +68,8 @@ enum tree_dump_index #define TDF_STMTADDR (1 << 12) /* Address of stmt. */ #define TDF_GRAPH (1 << 13) /* a graph dump is being emitted */ +#define TDF_CHAIN (1 << 14) /* Follow TREE_CHAIN when + dumping *_DECLs. */ extern char *get_dump_file_name (enum tree_dump_index); extern int dump_enabled_p (enum tree_dump_index); @@ -149,6 +151,7 @@ struct dump_file_info #define PROP_no_crit_edges (1 << 7) #define PROP_rtl (1 << 8) #define PROP_alias (1 << 9) +#define PROP_gimple_lomp (1 << 10) /* lowered OpenMP directives */ #define PROP_trees \ (PROP_gimple_any | PROP_gimple_lcf | PROP_gimple_leh) @@ -259,6 +262,7 @@ extern struct tree_opt_pass pass_lower_complex_O0; extern struct tree_opt_pass pass_lower_complex; extern struct tree_opt_pass pass_lower_vector; extern struct tree_opt_pass pass_lower_vector_ssa; +extern struct tree_opt_pass pass_lower_omp; extern struct tree_opt_pass pass_object_sizes; extern struct tree_opt_pass pass_fold_builtins; extern struct tree_opt_pass pass_stdarg; diff --git a/gcc/tree-pretty-print.c b/gcc/tree-pretty-print.c index 313e461dc85..d7e3391a3e4 100644 --- a/gcc/tree-pretty-print.c +++ b/gcc/tree-pretty-print.c @@ -35,6 +35,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA /* Local functions, macros and variables. */ static int op_prio (tree); +static const char *op_symbol_1 (enum tree_code); static const char *op_symbol (tree); static void pretty_print_string (pretty_printer *, const char*); static void print_call_name (pretty_printer *, tree); @@ -58,7 +59,6 @@ static void dump_generic_bb_buff (pretty_printer *, basic_block, int, int); static pretty_printer buffer; static int initialized = 0; -static bool dumping_stmts; /* Try to print something for an unknown tree code. */ @@ -97,12 +97,18 @@ debug_generic_stmt (tree t) fprintf (stderr, "\n"); } +void +debug_tree_chain (tree t) +{ + print_generic_expr (stderr, t, TDF_VOPS|TDF_UID|TDF_CHAIN); + fprintf (stderr, "\n"); +} + /* Prints declaration DECL to the FILE with details specified by FLAGS. */ void print_generic_decl (FILE *file, tree decl, int flags) { maybe_init_pretty_print (file); - dumping_stmts = true; print_declaration (&buffer, decl, 2, flags); pp_write_text_to_stream (&buffer); } @@ -114,7 +120,6 @@ void print_generic_stmt (FILE *file, tree t, int flags) { maybe_init_pretty_print (file); - dumping_stmts = true; dump_generic_node (&buffer, t, 0, flags, true); pp_flush (&buffer); } @@ -129,7 +134,6 @@ print_generic_stmt_indented (FILE *file, tree t, int flags, int indent) int i; maybe_init_pretty_print (file); - dumping_stmts = true; for (i = 0; i < indent; i++) pp_space (&buffer); @@ -144,7 +148,6 @@ void print_generic_expr (FILE *file, tree t, int flags) { maybe_init_pretty_print (file); - dumping_stmts = false; dump_generic_node (&buffer, t, 0, flags, false); } @@ -154,21 +157,34 @@ print_generic_expr (FILE *file, tree t, int flags) static void dump_decl_name (pretty_printer *buffer, tree node, int flags) { - if (DECL_NAME (node)) - pp_tree_identifier (buffer, DECL_NAME (node)); + tree t = node; - if ((flags & TDF_UID) - || DECL_NAME (node) == NULL_TREE) + while (t) { - if (TREE_CODE (node) == LABEL_DECL - && LABEL_DECL_UID (node) != -1) - pp_printf (buffer, "L." HOST_WIDE_INT_PRINT_DEC, - LABEL_DECL_UID (node)); - else + if (DECL_NAME (t)) + pp_tree_identifier (buffer, DECL_NAME (t)); + + if ((flags & TDF_UID) + || DECL_NAME (t) == NULL_TREE) + { + if (TREE_CODE (t) == LABEL_DECL + && LABEL_DECL_UID (t) != -1) + pp_printf (buffer, "L." HOST_WIDE_INT_PRINT_DEC, + LABEL_DECL_UID (t)); + else + { + char c = TREE_CODE (t) == CONST_DECL ? 'C' : 'D'; + pp_printf (buffer, "%c.%u", c, DECL_UID (t)); + } + } + + if (flags & TDF_CHAIN) { - char c = TREE_CODE (node) == CONST_DECL ? 'C' : 'D'; - pp_printf (buffer, "%c.%u", c, DECL_UID (node)); + t = TREE_CHAIN (t); + pp_string (buffer, " "); } + else + t = NULL_TREE; } } @@ -246,6 +262,140 @@ dump_array_domain (pretty_printer *buffer, tree domain, int spc, int flags) pp_character (buffer, ']'); } +/* Dump the list of OpenMP clauses. */ + +static void +dump_omp_clauses (pretty_printer *buffer, tree clause, int spc, int flags) +{ + const char *name; + + if (clause == NULL) + return; + + pp_space (buffer); + while (1) + { + switch (TREE_CODE (clause)) + { + case OMP_CLAUSE_PRIVATE: + name = "private"; + goto print_remap; + case OMP_CLAUSE_SHARED: + name = "shared"; + goto print_remap; + case OMP_CLAUSE_FIRSTPRIVATE: + name = "firstprivate"; + goto print_remap; + case OMP_CLAUSE_LASTPRIVATE: + name = "lastprivate"; + goto print_remap; + case OMP_CLAUSE_COPYIN: + name = "copyin"; + goto print_remap; + case OMP_CLAUSE_COPYPRIVATE: + name = "copyprivate"; + goto print_remap; + print_remap: + pp_string (buffer, name); + pp_character (buffer, '('); + dump_generic_node (buffer, OMP_CLAUSE_DECL (clause), + spc, flags, false); + pp_character (buffer, ')'); + break; + + case OMP_CLAUSE_REDUCTION: + pp_string (buffer, "reduction("); + pp_string (buffer, op_symbol_1 (OMP_CLAUSE_REDUCTION_CODE (clause))); + pp_character (buffer, ':'); + dump_generic_node (buffer, OMP_CLAUSE_DECL (clause), + spc, flags, false); + pp_character (buffer, ')'); + break; + + case OMP_CLAUSE_IF: + pp_string (buffer, "if("); + dump_generic_node (buffer, OMP_CLAUSE_IF_EXPR (clause), + spc, flags, false); + pp_character (buffer, ')'); + break; + + case OMP_CLAUSE_NUM_THREADS: + pp_string (buffer, "num_threads("); + dump_generic_node (buffer, OMP_CLAUSE_NUM_THREADS_EXPR (clause), + spc, flags, false); + pp_character (buffer, ')'); + break; + + case OMP_CLAUSE_NOWAIT: + pp_string (buffer, "nowait"); + break; + case OMP_CLAUSE_ORDERED: + pp_string (buffer, "ordered"); + break; + + case OMP_CLAUSE_DEFAULT: + pp_string (buffer, "default("); + switch (OMP_CLAUSE_DEFAULT_KIND (clause)) + { + case OMP_CLAUSE_DEFAULT_UNSPECIFIED: + break; + case OMP_CLAUSE_DEFAULT_SHARED: + pp_string (buffer, "shared"); + break; + case OMP_CLAUSE_DEFAULT_NONE: + pp_string (buffer, "none"); + break; + case OMP_CLAUSE_DEFAULT_PRIVATE: + pp_string (buffer, "private"); + break; + default: + gcc_unreachable (); + } + pp_character (buffer, ')'); + break; + + case OMP_CLAUSE_SCHEDULE: + pp_string (buffer, "schedule("); + switch (OMP_CLAUSE_SCHEDULE_KIND (clause)) + { + case OMP_CLAUSE_SCHEDULE_STATIC: + pp_string (buffer, "static"); + break; + case OMP_CLAUSE_SCHEDULE_DYNAMIC: + pp_string (buffer, "dynamic"); + break; + case OMP_CLAUSE_SCHEDULE_GUIDED: + pp_string (buffer, "guided"); + break; + case OMP_CLAUSE_SCHEDULE_RUNTIME: + pp_string (buffer, "runtime"); + break; + default: + gcc_unreachable (); + } + if (OMP_CLAUSE_SCHEDULE_CHUNK_EXPR (clause)) + { + pp_character (buffer, ','); + dump_generic_node (buffer, + OMP_CLAUSE_SCHEDULE_CHUNK_EXPR (clause), + spc, flags, false); + } + pp_character (buffer, ')'); + break; + + default: + /* Should never happen. */ + dump_generic_node (buffer, clause, spc, flags, false); + break; + } + + clause = OMP_CLAUSE_CHAIN (clause); + if (clause == NULL) + return; + pp_space (buffer); + } +} + /* Dump the node NODE on the pretty_printer BUFFER, SPC spaces of indent. FLAGS specifies details to show in the dump (see TDF_* in tree.h). If IS_STMT is true, the object printed is considered to be a statement @@ -275,9 +425,7 @@ dump_generic_node (pretty_printer *buffer, tree node, int spc, int flags, if (is_stmt && (flags & TDF_STMTADDR)) pp_printf (buffer, "<&%p> ", (void *)node); - if (dumping_stmts - && (flags & TDF_LINENO) - && EXPR_HAS_LOCATION (node)) + if ((flags & TDF_LINENO) && EXPR_HAS_LOCATION (node)) { expanded_location xloc = expand_location (EXPR_LOCATION (node)); pp_character (buffer, '['); @@ -340,10 +488,6 @@ dump_generic_node (pretty_printer *buffer, tree node, int spc, int flags, } break; - case BLOCK: - NIY; - break; - case VOID_TYPE: case INTEGER_TYPE: case REAL_TYPE: @@ -823,8 +967,8 @@ dump_generic_node (pretty_printer *buffer, tree node, int spc, int flags, } dump_generic_node (buffer, TREE_OPERAND (node, 0), - spc, flags, dumping_stmts); - if (dumping_stmts) + spc, flags, !(flags & TDF_SLIM)); + if (flags & TDF_SLIM) newline_and_indent (buffer, spc); else { @@ -837,8 +981,8 @@ dump_generic_node (pretty_printer *buffer, tree node, int spc, int flags, tp = &TREE_OPERAND (*tp, 1)) { dump_generic_node (buffer, TREE_OPERAND (*tp, 0), - spc, flags, dumping_stmts); - if (dumping_stmts) + spc, flags, !(flags & TDF_SLIM)); + if (flags & TDF_SLIM) newline_and_indent (buffer, spc); else { @@ -847,7 +991,7 @@ dump_generic_node (pretty_printer *buffer, tree node, int spc, int flags, } } - dump_generic_node (buffer, *tp, spc, flags, dumping_stmts); + dump_generic_node (buffer, *tp, spc, flags, !(flags & TDF_SLIM)); } break; @@ -856,7 +1000,7 @@ dump_generic_node (pretty_printer *buffer, tree node, int spc, int flags, tree_stmt_iterator si; bool first = true; - if ((flags & TDF_SLIM) || !dumping_stmts) + if (flags & TDF_SLIM) { pp_string (buffer, "<STATEMENT_LIST>"); break; @@ -1360,7 +1504,8 @@ dump_generic_node (pretty_printer *buffer, tree node, int spc, int flags, if (SWITCH_BODY (node)) { newline_and_indent (buffer, spc+4); - dump_generic_node (buffer, SWITCH_BODY (node), spc+4, flags, true); + dump_generic_node (buffer, SWITCH_BODY (node), spc+4, flags, + true); } else { @@ -1370,10 +1515,16 @@ dump_generic_node (pretty_printer *buffer, tree node, int spc, int flags, { tree elt = TREE_VEC_ELT (vec, i); newline_and_indent (buffer, spc+4); - dump_generic_node (buffer, elt, spc+4, flags, false); - pp_string (buffer, " goto "); - dump_generic_node (buffer, CASE_LABEL (elt), spc+4, flags, true); - pp_semicolon (buffer); + if (elt) + { + dump_generic_node (buffer, elt, spc+4, flags, false); + pp_string (buffer, " goto "); + dump_generic_node (buffer, CASE_LABEL (elt), spc+4, + flags, true); + pp_semicolon (buffer); + } + else + pp_string (buffer, "case ???: goto ???;"); } } newline_and_indent (buffer, spc+2); @@ -1535,6 +1686,110 @@ dump_generic_node (pretty_printer *buffer, tree node, int spc, int flags, pp_string (buffer, " > "); break; + case OMP_PARALLEL: + pp_string (buffer, "#pragma omp parallel"); + dump_omp_clauses (buffer, OMP_PARALLEL_CLAUSES (node), spc, flags); + + dump_omp_body: + if (!(flags & TDF_SLIM) && OMP_BODY (node)) + { + newline_and_indent (buffer, spc + 2); + pp_character (buffer, '{'); + newline_and_indent (buffer, spc + 4); + dump_generic_node (buffer, OMP_BODY (node), spc + 4, flags, false); + newline_and_indent (buffer, spc + 2); + pp_character (buffer, '}'); + } + is_expr = false; + break; + + case OMP_FOR: + pp_string (buffer, "#pragma omp for"); + dump_omp_clauses (buffer, OMP_FOR_CLAUSES (node), spc, flags); + + if (!(flags & TDF_SLIM)) + { + if (OMP_FOR_PRE_BODY (node)) + { + newline_and_indent (buffer, spc + 2); + pp_character (buffer, '{'); + spc += 4; + newline_and_indent (buffer, spc); + dump_generic_node (buffer, OMP_FOR_PRE_BODY (node), + spc, flags, false); + } + newline_and_indent (buffer, spc); + pp_string (buffer, "for ("); + dump_generic_node (buffer, OMP_FOR_INIT (node), spc, flags, false); + pp_string (buffer, "; "); + dump_generic_node (buffer, OMP_FOR_COND (node), spc, flags, false); + pp_string (buffer, "; "); + dump_generic_node (buffer, OMP_FOR_INCR (node), spc, flags, false); + pp_string (buffer, ")"); + if (OMP_FOR_BODY (node)) + { + newline_and_indent (buffer, spc + 2); + pp_character (buffer, '{'); + newline_and_indent (buffer, spc + 4); + dump_generic_node (buffer, OMP_FOR_BODY (node), spc + 4, flags, + false); + newline_and_indent (buffer, spc + 2); + pp_character (buffer, '}'); + } + if (OMP_FOR_PRE_BODY (node)) + { + spc -= 4; + newline_and_indent (buffer, spc + 2); + pp_character (buffer, '}'); + } + } + is_expr = false; + break; + + case OMP_SECTIONS: + pp_string (buffer, "#pragma omp sections"); + dump_omp_clauses (buffer, OMP_SECTIONS_CLAUSES (node), spc, flags); + goto dump_omp_body; + + case OMP_SECTION: + pp_string (buffer, "#pragma omp section"); + goto dump_omp_body; + + case OMP_MASTER: + pp_string (buffer, "#pragma omp master"); + goto dump_omp_body; + + case OMP_ORDERED: + pp_string (buffer, "#pragma omp ordered"); + goto dump_omp_body; + + case OMP_CRITICAL: + pp_string (buffer, "#pragma omp critical"); + if (OMP_CRITICAL_NAME (node)) + { + pp_space (buffer); + pp_character (buffer, '('); + dump_generic_node (buffer, OMP_CRITICAL_NAME (node), spc, + flags, false); + pp_character (buffer, ')'); + } + goto dump_omp_body; + + case OMP_ATOMIC: + pp_string (buffer, "#pragma omp atomic"); + newline_and_indent (buffer, spc + 2); + dump_generic_node (buffer, TREE_OPERAND (node, 0), spc, flags, false); + pp_space (buffer); + pp_character (buffer, '='); + pp_space (buffer); + dump_generic_node (buffer, TREE_OPERAND (node, 1), spc, flags, false); + break; + + case OMP_SINGLE: + pp_string (buffer, "#pragma omp single"); + dump_omp_clauses (buffer, OMP_SINGLE_CLAUSES (node), spc, flags); + goto dump_omp_body; + case REDUC_MAX_EXPR: pp_string (buffer, " REDUC_MAX_EXPR < "); dump_generic_node (buffer, TREE_OPERAND (node, 0), spc, flags, false); @@ -1553,6 +1808,64 @@ dump_generic_node (pretty_printer *buffer, tree node, int spc, int flags, pp_string (buffer, " > "); break; + case BLOCK: + { + tree t; + pp_string (buffer, "BLOCK"); + + if (BLOCK_ABSTRACT (node)) + pp_string (buffer, " [abstract]"); + + if (TREE_ASM_WRITTEN (node)) + pp_string (buffer, " [written]"); + + newline_and_indent (buffer, spc + 2); + + if (BLOCK_SUPERCONTEXT (node)) + { + pp_string (buffer, "SUPERCONTEXT: "); + if (TREE_CODE (BLOCK_SUPERCONTEXT (node)) == BLOCK) + pp_printf (buffer, "BLOCK %p", + (void *)BLOCK_SUPERCONTEXT (node)); + else + dump_generic_node (buffer, BLOCK_SUPERCONTEXT (node), 0, flags, + false); + newline_and_indent (buffer, spc + 2); + } + + if (BLOCK_SUBBLOCKS (node)) + { + pp_string (buffer, "SUBBLOCKS: "); + for (t = BLOCK_SUBBLOCKS (node); t; t = BLOCK_CHAIN (t)) + pp_printf (buffer, "%p ", (void *)t); + newline_and_indent (buffer, spc + 2); + } + + if (BLOCK_VARS (node)) + { + pp_string (buffer, "VARS: "); + for (t = BLOCK_VARS (node); t; t = TREE_CHAIN (t)) + { + dump_generic_node (buffer, t, 0, flags, false); + pp_string (buffer, " "); + } + newline_and_indent (buffer, spc + 2); + } + + if (BLOCK_ABSTRACT_ORIGIN (node)) + { + pp_string (buffer, "ABSTRACT_ORIGIN: "); + if (TREE_CODE (BLOCK_ABSTRACT_ORIGIN (node)) == BLOCK) + pp_printf (buffer, "BLOCK %p", + (void *)BLOCK_ABSTRACT_ORIGIN (node)); + else + dump_generic_node (buffer, BLOCK_ABSTRACT_ORIGIN (node), 0, flags, + false); + newline_and_indent (buffer, spc + 2); + } + } + break; + default: NIY; } @@ -1645,6 +1958,13 @@ print_declaration (pretty_printer *buffer, tree t, int spc, int flags) } } + if (TREE_CODE (t) == VAR_DECL && DECL_HAS_VALUE_EXPR_P (t)) + { + pp_string (buffer, " [value-expr: "); + dump_generic_node (buffer, DECL_VALUE_EXPR (t), spc, flags, false); + pp_character (buffer, ']'); + } + pp_character (buffer, ';'); } @@ -1857,11 +2177,9 @@ op_prio (tree op) /* Return the symbol associated with operator OP. */ static const char * -op_symbol (tree op) +op_symbol_1 (enum tree_code code) { - gcc_assert (op); - - switch (TREE_CODE (op)) + switch (code) { case MODIFY_EXPR: return "="; @@ -2005,11 +2323,23 @@ op_symbol (tree op) case POSTINCREMENT_EXPR: return "++ "; + case MAX_EXPR: + return "max"; + + case MIN_EXPR: + return "min"; + default: return "<<< ??? >>>"; } } +static const char * +op_symbol (tree op) +{ + return op_symbol_1 (TREE_CODE (op)); +} + /* Prints the name of a CALL_EXPR. */ static void @@ -2236,7 +2566,6 @@ void dump_generic_bb (FILE *file, basic_block bb, int indent, int flags) { maybe_init_pretty_print (file); - dumping_stmts = true; dump_generic_bb_buff (&buffer, bb, indent, flags); pp_flush (&buffer); } diff --git a/gcc/tree-ssa-dce.c b/gcc/tree-ssa-dce.c index 7a463676da7..c8b98d357c8 100644 --- a/gcc/tree-ssa-dce.c +++ b/gcc/tree-ssa-dce.c @@ -182,7 +182,7 @@ find_control_dependence (struct edge_list *el, int edge_index) gcc_assert (INDEX_EDGE_PRED_BB (el, edge_index) != EXIT_BLOCK_PTR); if (INDEX_EDGE_PRED_BB (el, edge_index) == ENTRY_BLOCK_PTR) - ending_block = ENTRY_BLOCK_PTR->next_bb; + ending_block = single_succ (ENTRY_BLOCK_PTR); else ending_block = find_pdom (INDEX_EDGE_PRED_BB (el, edge_index)); diff --git a/gcc/tree.c b/gcc/tree.c index 9aaba7e4c26..140676b6bd8 100644 --- a/gcc/tree.c +++ b/gcc/tree.c @@ -2945,6 +2945,34 @@ build4_stat (enum tree_code code, tree tt, tree arg0, tree arg1, } tree +build5_stat (enum tree_code code, tree tt, tree arg0, tree arg1, + tree arg2, tree arg3, tree arg4 MEM_STAT_DECL) +{ + bool constant, read_only, side_effects, invariant; + tree t; + + gcc_assert (TREE_CODE_LENGTH (code) == 5); + + t = make_node_stat (code PASS_MEM_STAT); + TREE_TYPE (t) = tt; + + side_effects = TREE_SIDE_EFFECTS (t); + + PROCESS_ARG(0); + PROCESS_ARG(1); + PROCESS_ARG(2); + PROCESS_ARG(3); + PROCESS_ARG(4); + + TREE_SIDE_EFFECTS (t) = side_effects; + TREE_THIS_VOLATILE (t) + = (TREE_CODE_CLASS (code) == tcc_reference + && arg0 && TREE_THIS_VOLATILE (arg0)); + + return t; +} + +tree build7_stat (enum tree_code code, tree tt, tree arg0, tree arg1, tree arg2, tree arg3, tree arg4, tree arg5, tree arg6 MEM_STAT_DECL) @@ -6032,6 +6060,41 @@ tree_class_check_failed (const tree node, const enum tree_code_class cl, tree_code_name[TREE_CODE (node)], function, trim_filename (file), line); } +/* Similar to tree_check_failed, except that instead of specifying a + dozen codes, use the knowledge that they're all sequential. */ + +void +tree_range_check_failed (const tree node, const char *file, int line, + const char *function, enum tree_code c1, + enum tree_code c2) +{ + char *buffer; + unsigned length = 0; + enum tree_code c; + + for (c = c1; c <= c2; ++c) + length += 4 + strlen (tree_code_name[c]); + + length += strlen ("expected "); + buffer = alloca (length); + length = 0; + + for (c = c1; c <= c2; ++c) + { + const char *prefix = length ? " or " : "expected "; + + strcpy (buffer + length, prefix); + length += strlen (prefix); + strcpy (buffer + length, tree_code_name[c]); + length += strlen (tree_code_name[c]); + } + + internal_error ("tree check: %s, have %s in %s, at %s:%d", + buffer, tree_code_name[TREE_CODE (node)], + function, trim_filename (file), line); +} + + /* Similar to tree_check_failed, except that we check that a tree does not have the specified code, given in CL. */ @@ -7145,9 +7208,11 @@ walk_tree (tree *tp, walk_tree_fn func, void *data, struct pointer_set_t *pset) interesting below this point in the tree. */ if (!walk_subtrees) { + /* But we still need to check our siblings. */ if (code == TREE_LIST) - /* But we still need to check our siblings. */ WALK_SUBTREE_TAIL (TREE_CHAIN (*tp)); + else if (code >= OMP_CLAUSE_PRIVATE && code <= OMP_CLAUSE_DEFAULT) + WALK_SUBTREE_TAIL (OMP_CLAUSE_CHAIN (*tp)); else return NULL_TREE; } @@ -7157,190 +7222,199 @@ walk_tree (tree *tp, walk_tree_fn func, void *data, struct pointer_set_t *pset) if (result || ! walk_subtrees) return result; - /* If this is a DECL_EXPR, walk into various fields of the type that it's - defining. We only want to walk into these fields of a type in this - case. Note that decls get walked as part of the processing of a - BIND_EXPR. - - ??? Precisely which fields of types that we are supposed to walk in - this case vs. the normal case aren't well defined. */ - if (code == DECL_EXPR - && TREE_CODE (DECL_EXPR_DECL (*tp)) == TYPE_DECL - && TREE_CODE (TREE_TYPE (DECL_EXPR_DECL (*tp))) != ERROR_MARK) + switch (code) { - tree *type_p = &TREE_TYPE (DECL_EXPR_DECL (*tp)); + case ERROR_MARK: + case IDENTIFIER_NODE: + case INTEGER_CST: + case REAL_CST: + case VECTOR_CST: + case STRING_CST: + case BLOCK: + case PLACEHOLDER_EXPR: + case SSA_NAME: + case FIELD_DECL: + case RESULT_DECL: + /* None of these have subtrees other than those already walked + above. */ + break; - /* Call the function for the type. See if it returns anything or - doesn't want us to continue. If we are to continue, walk both - the normal fields and those for the declaration case. */ - result = (*func) (type_p, &walk_subtrees, data); - if (result || !walk_subtrees) - return NULL_TREE; + case TREE_LIST: + WALK_SUBTREE (TREE_VALUE (*tp)); + WALK_SUBTREE_TAIL (TREE_CHAIN (*tp)); + break; - result = walk_type_fields (*type_p, func, data, pset); - if (result) - return result; + case TREE_VEC: + { + int len = TREE_VEC_LENGTH (*tp); - WALK_SUBTREE (TYPE_SIZE (*type_p)); - WALK_SUBTREE (TYPE_SIZE_UNIT (*type_p)); + if (len == 0) + break; - /* If this is a record type, also walk the fields. */ - if (TREE_CODE (*type_p) == RECORD_TYPE - || TREE_CODE (*type_p) == UNION_TYPE - || TREE_CODE (*type_p) == QUAL_UNION_TYPE) - { - tree field; + /* Walk all elements but the first. */ + while (--len) + WALK_SUBTREE (TREE_VEC_ELT (*tp, len)); - for (field = TYPE_FIELDS (*type_p); field; - field = TREE_CHAIN (field)) - { - /* We'd like to look at the type of the field, but we can easily - get infinite recursion. So assume it's pointed to elsewhere - in the tree. Also, ignore things that aren't fields. */ - if (TREE_CODE (field) != FIELD_DECL) - continue; - - WALK_SUBTREE (DECL_FIELD_OFFSET (field)); - WALK_SUBTREE (DECL_SIZE (field)); - WALK_SUBTREE (DECL_SIZE_UNIT (field)); - if (TREE_CODE (*type_p) == QUAL_UNION_TYPE) - WALK_SUBTREE (DECL_QUALIFIER (field)); - } - } - } + /* Now walk the first one as a tail call. */ + WALK_SUBTREE_TAIL (TREE_VEC_ELT (*tp, 0)); + } - else if (code != SAVE_EXPR - && code != BIND_EXPR - && IS_EXPR_CODE_CLASS (TREE_CODE_CLASS (code))) - { - int i, len; - - /* Walk over all the sub-trees of this operand. */ - len = TREE_CODE_LENGTH (code); - /* TARGET_EXPRs are peculiar: operands 1 and 3 can be the same. - But, we only want to walk once. */ - if (code == TARGET_EXPR - && TREE_OPERAND (*tp, 3) == TREE_OPERAND (*tp, 1)) - --len; - - /* Go through the subtrees. We need to do this in forward order so - that the scope of a FOR_EXPR is handled properly. */ -#ifdef DEBUG_WALK_TREE - for (i = 0; i < len; ++i) - WALK_SUBTREE (TREE_OPERAND (*tp, i)); -#else - for (i = 0; i < len - 1; ++i) - WALK_SUBTREE (TREE_OPERAND (*tp, i)); + case COMPLEX_CST: + WALK_SUBTREE (TREE_REALPART (*tp)); + WALK_SUBTREE_TAIL (TREE_IMAGPART (*tp)); - if (len) - { - /* The common case is that we may tail recurse here. */ - if (code != BIND_EXPR - && !TREE_CHAIN (*tp)) - WALK_SUBTREE_TAIL (TREE_OPERAND (*tp, len - 1)); - else - WALK_SUBTREE (TREE_OPERAND (*tp, len - 1)); - } -#endif - } + case CONSTRUCTOR: + { + unsigned HOST_WIDE_INT idx; + constructor_elt *ce; - /* If this is a type, walk the needed fields in the type. */ - else if (TYPE_P (*tp)) - { - result = walk_type_fields (*tp, func, data, pset); - if (result) - return result; - } - else - { - /* Not one of the easy cases. We must explicitly go through the - children. */ - switch (code) - { - case ERROR_MARK: - case IDENTIFIER_NODE: - case INTEGER_CST: - case REAL_CST: - case VECTOR_CST: - case STRING_CST: - case BLOCK: - case PLACEHOLDER_EXPR: - case SSA_NAME: - case FIELD_DECL: - case RESULT_DECL: - /* None of these have subtrees other than those already walked - above. */ - break; + for (idx = 0; + VEC_iterate(constructor_elt, CONSTRUCTOR_ELTS (*tp), idx, ce); + idx++) + WALK_SUBTREE (ce->value); + } + break; - case TREE_LIST: - WALK_SUBTREE (TREE_VALUE (*tp)); - WALK_SUBTREE_TAIL (TREE_CHAIN (*tp)); - break; + case SAVE_EXPR: + WALK_SUBTREE_TAIL (TREE_OPERAND (*tp, 0)); - case TREE_VEC: + case BIND_EXPR: + { + tree decl; + for (decl = BIND_EXPR_VARS (*tp); decl; decl = TREE_CHAIN (decl)) { - int len = TREE_VEC_LENGTH (*tp); + /* Walk the DECL_INITIAL and DECL_SIZE. We don't want to walk + into declarations that are just mentioned, rather than + declared; they don't really belong to this part of the tree. + And, we can see cycles: the initializer for a declaration + can refer to the declaration itself. */ + WALK_SUBTREE (DECL_INITIAL (decl)); + WALK_SUBTREE (DECL_SIZE (decl)); + WALK_SUBTREE (DECL_SIZE_UNIT (decl)); + } + WALK_SUBTREE_TAIL (BIND_EXPR_BODY (*tp)); + } - if (len == 0) - break; + case STATEMENT_LIST: + { + tree_stmt_iterator i; + for (i = tsi_start (*tp); !tsi_end_p (i); tsi_next (&i)) + WALK_SUBTREE (*tsi_stmt_ptr (i)); + } + break; - /* Walk all elements but the first. */ - while (--len) - WALK_SUBTREE (TREE_VEC_ELT (*tp, len)); + case OMP_CLAUSE_PRIVATE: + case OMP_CLAUSE_SHARED: + case OMP_CLAUSE_FIRSTPRIVATE: + case OMP_CLAUSE_LASTPRIVATE: + case OMP_CLAUSE_COPYIN: + case OMP_CLAUSE_COPYPRIVATE: + case OMP_CLAUSE_IF: + case OMP_CLAUSE_NUM_THREADS: + case OMP_CLAUSE_SCHEDULE: + WALK_SUBTREE (TREE_OPERAND (*tp, 0)); + /* FALLTHRU */ + + case OMP_CLAUSE_NOWAIT: + case OMP_CLAUSE_ORDERED: + case OMP_CLAUSE_DEFAULT: + WALK_SUBTREE_TAIL (OMP_CLAUSE_CHAIN (*tp)); + + case OMP_CLAUSE_REDUCTION: + { + int i; + for (i = 0; i < 4; i++) + WALK_SUBTREE (TREE_OPERAND (*tp, i)); + WALK_SUBTREE_TAIL (OMP_CLAUSE_CHAIN (*tp)); + } - /* Now walk the first one as a tail call. */ - WALK_SUBTREE_TAIL (TREE_VEC_ELT (*tp, 0)); - } + case TARGET_EXPR: + { + int i, len; + + /* TARGET_EXPRs are peculiar: operands 1 and 3 can be the same. + But, we only want to walk once. */ + len = (TREE_OPERAND (*tp, 3) == TREE_OPERAND (*tp, 1)) ? 2 : 3; + for (i = 0; i < len; ++i) + WALK_SUBTREE (TREE_OPERAND (*tp, i)); + WALK_SUBTREE_TAIL (TREE_OPERAND (*tp, len)); + } - case COMPLEX_CST: - WALK_SUBTREE (TREE_REALPART (*tp)); - WALK_SUBTREE_TAIL (TREE_IMAGPART (*tp)); + case DECL_EXPR: + /* Walk into various fields of the type that it's defining. We only + want to walk into these fields of a type in this case. Note that + decls get walked as part of the processing of a BIND_EXPR. - case CONSTRUCTOR: - { - unsigned HOST_WIDE_INT idx; - constructor_elt *ce; + ??? Precisely which fields of types that we are supposed to walk in + this case vs. the normal case aren't well defined. */ + if (TREE_CODE (DECL_EXPR_DECL (*tp)) == TYPE_DECL + && TREE_CODE (TREE_TYPE (DECL_EXPR_DECL (*tp))) != ERROR_MARK) + { + tree *type_p = &TREE_TYPE (DECL_EXPR_DECL (*tp)); - for (idx = 0; - VEC_iterate(constructor_elt, CONSTRUCTOR_ELTS (*tp), idx, ce); - idx++) - WALK_SUBTREE (ce->value); - } - break; + /* Call the function for the type. See if it returns anything or + doesn't want us to continue. If we are to continue, walk both + the normal fields and those for the declaration case. */ + result = (*func) (type_p, &walk_subtrees, data); + if (result || !walk_subtrees) + return NULL_TREE; - case SAVE_EXPR: - WALK_SUBTREE_TAIL (TREE_OPERAND (*tp, 0)); + result = walk_type_fields (*type_p, func, data, pset); + if (result) + return result; - case BIND_EXPR: - { - tree decl; - for (decl = BIND_EXPR_VARS (*tp); decl; decl = TREE_CHAIN (decl)) - { - /* Walk the DECL_INITIAL and DECL_SIZE. We don't want to walk - into declarations that are just mentioned, rather than - declared; they don't really belong to this part of the tree. - And, we can see cycles: the initializer for a declaration - can refer to the declaration itself. */ - WALK_SUBTREE (DECL_INITIAL (decl)); - WALK_SUBTREE (DECL_SIZE (decl)); - WALK_SUBTREE (DECL_SIZE_UNIT (decl)); - } - WALK_SUBTREE_TAIL (BIND_EXPR_BODY (*tp)); - } + /* If this is a record type, also walk the fields. */ + if (TREE_CODE (*type_p) == RECORD_TYPE + || TREE_CODE (*type_p) == UNION_TYPE + || TREE_CODE (*type_p) == QUAL_UNION_TYPE) + { + tree field; - case STATEMENT_LIST: - { - tree_stmt_iterator i; - for (i = tsi_start (*tp); !tsi_end_p (i); tsi_next (&i)) - WALK_SUBTREE (*tsi_stmt_ptr (i)); - } - break; + for (field = TYPE_FIELDS (*type_p); field; + field = TREE_CHAIN (field)) + { + /* We'd like to look at the type of the field, but we can + easily get infinite recursion. So assume it's pointed + to elsewhere in the tree. Also, ignore things that + aren't fields. */ + if (TREE_CODE (field) != FIELD_DECL) + continue; + + WALK_SUBTREE (DECL_FIELD_OFFSET (field)); + WALK_SUBTREE (DECL_SIZE (field)); + WALK_SUBTREE (DECL_SIZE_UNIT (field)); + if (TREE_CODE (*type_p) == QUAL_UNION_TYPE) + WALK_SUBTREE (DECL_QUALIFIER (field)); + } + } - default: - /* ??? This could be a language-defined node. We really should make - a hook for it, but right now just ignore it. */ - break; + WALK_SUBTREE (TYPE_SIZE (*type_p)); + WALK_SUBTREE_TAIL (TYPE_SIZE_UNIT (*type_p)); } + /* FALLTHRU */ + + default: + if (IS_EXPR_CODE_CLASS (TREE_CODE_CLASS (code))) + { + int i, len; + + /* Walk over all the sub-trees of this operand. */ + len = TREE_CODE_LENGTH (code); + + /* Go through the subtrees. We need to do this in forward order so + that the scope of a FOR_EXPR is handled properly. */ + if (len) + { + for (i = 0; i < len - 1; ++i) + WALK_SUBTREE (TREE_OPERAND (*tp, i)); + WALK_SUBTREE_TAIL (TREE_OPERAND (*tp, len - 1)); + } + } + + /* If this is a type, walk the needed fields in the type. */ + else if (TYPE_P (*tp)) + return walk_type_fields (*tp, func, data, pset); + break; } /* We didn't find what we were looking for. */ @@ -7364,4 +7438,30 @@ walk_tree_without_duplicates (tree *tp, walk_tree_fn func, void *data) return result; } + +/* Return true if STMT is an empty statement or contains nothing but + empty statements. */ + +bool +empty_body_p (tree stmt) +{ + tree_stmt_iterator i; + tree body; + + if (IS_EMPTY_STMT (stmt)) + return true; + else if (TREE_CODE (stmt) == BIND_EXPR) + body = BIND_EXPR_BODY (stmt); + else if (TREE_CODE (stmt) == STATEMENT_LIST) + body = stmt; + else + return false; + + for (i = tsi_start (body); !tsi_end_p (i); tsi_next (&i)) + if (!empty_body_p (tsi_stmt (i))) + return false; + + return true; +} + #include "gt-tree.h" diff --git a/gcc/tree.def b/gcc/tree.def index 66692b40168..9e7e5b011d5 100644 --- a/gcc/tree.def +++ b/gcc/tree.def @@ -953,6 +953,116 @@ DEFTREECODE (REALIGN_LOAD_EXPR, "realign_load", tcc_expression, 3) DEFTREECODE (TARGET_MEM_REF, "target_mem_ref", tcc_reference, 7) +/* The ordering of the codes between OMP_PARALLEL and OMP_CRITICAL is + exposed to TREE_RANGE_CHECK. */ +/* OpenMP - #pragma omp parallel [clause1 ... clauseN] + Operand 0: OMP_PARALLEL_BODY: Code to be executed by all threads. + Operand 1: OMP_PARALLEL_CLAUSES: List of clauses. */ +DEFTREECODE (OMP_PARALLEL, "omp_parallel", tcc_statement, 2) + +/* OpenMP - #pragma omp for [clause1 ... clauseN] + Operand 0: OMP_FOR_BODY: Loop body. + Operand 1: OMP_FOR_CLAUSES: List of clauses. + Operand 2: OMP_FOR_INIT: Initialization code of the form + VAR = N1. + Operand 3: OMP_FOR_COND: Loop conditional expression of the form + VAR { <, >, <=, >= } N2. + Operand 4: OMP_FOR_INCR: Loop index increment of the form + VAR { +=, -= } INCR. + Operand 5: OMP_FOR_PRE_BODY: Filled by the gimplifier with things + from INIT, COND, and INCR that are technically part of the + OMP_FOR structured block, but are evaluated before the loop + body begins. + + VAR must be a signed integer variable, which is implicitly thread + private. N1, N2 and INCR are required to be loop invariant integer + expressions that are evaluated without any synchronization. + The evaluation order, frequency of evaluation and side-effects are + unspecified by the standard. */ +DEFTREECODE (OMP_FOR, "omp_for", tcc_statement, 6) + +/* OpenMP - #pragma omp sections [clause1 ... clauseN] + Operand 0: OMP_SECTIONS_BODY: Sections body. + Operand 1: OMP_SECTIONS_CLAUSES: List of clauses. */ +DEFTREECODE (OMP_SECTIONS, "omp_sections", tcc_statement, 2) + +/* OpenMP - #pragma omp single + Operand 0: OMP_SINGLE_BODY: Single section body. + Operand 1: OMP_SINGLE_CLAUSES: List of clauses. */ +DEFTREECODE (OMP_SINGLE, "omp_single", tcc_statement, 2) + +/* OpenMP - #pragma omp section + Operand 0: OMP_SECTION_BODY: Section body. */ +DEFTREECODE (OMP_SECTION, "omp_section", tcc_statement, 1) + +/* OpenMP - #pragma omp master + Operand 0: OMP_MASTER_BODY: Master section body. */ +DEFTREECODE (OMP_MASTER, "omp_master", tcc_statement, 1) + +/* OpenMP - #pragma omp ordered + Operand 0: OMP_ORDERED_BODY: Master section body. */ +DEFTREECODE (OMP_ORDERED, "omp_ordered", tcc_statement, 1) + +/* OpenMP - #pragma omp critical [name] + Operand 0: OMP_CRITICAL_BODY: Critical section body. + Operand 1: OMP_CRITICAL_NAME: Identifier for critical section. */ +DEFTREECODE (OMP_CRITICAL, "omp_critical", tcc_statement, 2) + +/* OpenMP - #pragma omp atomic + Operand 0: The address at which the atomic operation is to be performed. + This address should be stabilized with save_expr. + Operand 1: The expression to evaluate. When the old value of the object + at the address is used in the expression, it should appear as if + build_fold_indirect_ref of the address. */ +DEFTREECODE (OMP_ATOMIC, "omp_atomic", tcc_statement, 2) + +/* The ordering of the codes between OMP_CLAUSE_PRIVATE and + OMP_CLAUSE_DEFAULT is exposed to TREE_RANGE_CHECK. */ +/* OpenMP clause: private (variable_list). */ +DEFTREECODE (OMP_CLAUSE_PRIVATE, "private", tcc_expression, 1) + +/* OpenMP clause: shared (variable_list). */ +DEFTREECODE (OMP_CLAUSE_SHARED, "shared", tcc_expression, 1) + +/* OpenMP clause: firstprivate (variable_list). */ +DEFTREECODE (OMP_CLAUSE_FIRSTPRIVATE, "firstprivate", tcc_expression, 1) + +/* OpenMP clause: lastprivate (variable_list). */ +DEFTREECODE (OMP_CLAUSE_LASTPRIVATE, "lastprivate", tcc_expression, 1) + +/* OpenMP clause: reduction (operator:variable_list). + OMP_CLAUSE_REDUCTION_CODE: The tree_code of the operator. + Operand 1: OMP_CLAUSE_REDUCTION_INIT: Stmt-list to initialize the var. + Operand 2: OMP_CLAUSE_REDUCTION_MERGE: + Stmt-list to merge private var into the shared one. + Operand 3: OMP_CLAUSE_REDUCTION_PLACEHOLDER: + A dummy VAR_DECL placeholder used in OMP_CLAUSE_REDUCTION_MERGE. */ +DEFTREECODE (OMP_CLAUSE_REDUCTION, "reduction", tcc_expression, 4) + +/* OpenMP clause: copyin (variable_list). */ +DEFTREECODE (OMP_CLAUSE_COPYIN, "copyin", tcc_expression, 1) + +/* OpenMP clause: copyprivate (variable_list). */ +DEFTREECODE (OMP_CLAUSE_COPYPRIVATE, "copyprivate", tcc_expression, 1) + +/* OpenMP clause: if (scalar-expression). */ +DEFTREECODE (OMP_CLAUSE_IF, "if", tcc_expression, 1) + +/* OpenMP clause: num_threads (integer-expression). */ +DEFTREECODE (OMP_CLAUSE_NUM_THREADS, "num_threads", tcc_expression, 1) + +/* OpenMP clause: schedule. */ +DEFTREECODE (OMP_CLAUSE_SCHEDULE, "schedule", tcc_expression, 1) + +/* OpenMP clause: nowait. */ +DEFTREECODE (OMP_CLAUSE_NOWAIT, "nowait", tcc_expression, 0) + +/* OpenMP clause: ordered. */ +DEFTREECODE (OMP_CLAUSE_ORDERED, "ordered", tcc_expression, 0) + +/* OpenMP clause: default. */ +DEFTREECODE (OMP_CLAUSE_DEFAULT, "default", tcc_expression, 0) + /* Reduction operations. Operations that take a vector of elements and "reduce" it to a scalar result (e.g. summing the elements of the vector, finding the minimum over diff --git a/gcc/tree.h b/gcc/tree.h index afeaf1eb040..f7244fc4f74 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -351,6 +351,10 @@ struct tree_common GTY(()) ..._TYPE SAVE_EXPR_RESOLVED_P in SAVE_EXPR + OMP_CLAUSE_LASTPRIVATE_FIRSTPRIVATE in + OMP_CLAUSE_LASTPRIVATE + OMP_CLAUSE_PRIVATE_DEBUG in + OMP_CLAUSE_PRIVATE private_flag: @@ -579,6 +583,13 @@ enum tree_node_structure_enum { __FUNCTION__); \ __t; }) +#define TREE_RANGE_CHECK(T, CODE1, CODE2) __extension__ \ +({ const tree __t = (T); \ + if (TREE_CODE (__t) < (CODE1) && TREE_CODE (__t) > (CODE2)) \ + tree_range_check_failed (__t, __FILE__, __LINE__, __FUNCTION__, \ + (CODE1), (CODE2)); \ + __t; }) + /* These checks have to be special cased. */ #define EXPR_CHECK(T) __extension__ \ ({ const tree __t = (T); \ @@ -660,6 +671,9 @@ extern void tree_not_check_failed (const tree, const char *, int, const char *, extern void tree_class_check_failed (const tree, const enum tree_code_class, const char *, int, const char *) ATTRIBUTE_NORETURN; +extern void tree_range_check_failed (const tree, const char *, int, + const char *, enum tree_code, + enum tree_code); extern void tree_not_class_check_failed (const tree, const enum tree_code_class, const char *, int, const char *) @@ -688,6 +702,7 @@ extern void tree_operand_check_failed (int, enum tree_code, #define TREE_CHECK5(T, CODE1, CODE2, CODE3, CODE4, CODE5) (T) #define TREE_NOT_CHECK5(T, CODE1, CODE2, CODE3, CODE4, CODE5) (T) #define TREE_CLASS_CHECK(T, CODE) (T) +#define TREE_RANGE_CHECK(T, CODE1, CODE2) (T) #define EXPR_CHECK(T) (T) #define NON_TYPE_CHECK(T) (T) #define TREE_VEC_ELT_CHECK(T, I) ((T)->vec.a[I]) @@ -1400,6 +1415,93 @@ struct tree_constructor GTY(()) #define ASSERT_EXPR_VAR(NODE) TREE_OPERAND (ASSERT_EXPR_CHECK (NODE), 0) #define ASSERT_EXPR_COND(NODE) TREE_OPERAND (ASSERT_EXPR_CHECK (NODE), 1) +/* OpenMP directive and clause accessors. */ + +#define OMP_BODY(NODE) \ + TREE_OPERAND (TREE_RANGE_CHECK (NODE, OMP_PARALLEL, OMP_CRITICAL), 0) +#define OMP_CLAUSES(NODE) \ + TREE_OPERAND (TREE_RANGE_CHECK (NODE, OMP_PARALLEL, OMP_SINGLE), 1) + +#define OMP_PARALLEL_BODY(NODE) TREE_OPERAND (OMP_PARALLEL_CHECK (NODE), 0) +#define OMP_PARALLEL_CLAUSES(NODE) TREE_OPERAND (OMP_PARALLEL_CHECK (NODE), 1) + +#define OMP_FOR_BODY(NODE) TREE_OPERAND (OMP_FOR_CHECK (NODE), 0) +#define OMP_FOR_CLAUSES(NODE) TREE_OPERAND (OMP_FOR_CHECK (NODE), 1) +#define OMP_FOR_INIT(NODE) TREE_OPERAND (OMP_FOR_CHECK (NODE), 2) +#define OMP_FOR_COND(NODE) TREE_OPERAND (OMP_FOR_CHECK (NODE), 3) +#define OMP_FOR_INCR(NODE) TREE_OPERAND (OMP_FOR_CHECK (NODE), 4) +#define OMP_FOR_PRE_BODY(NODE) TREE_OPERAND (OMP_FOR_CHECK (NODE), 5) + +#define OMP_SECTIONS_BODY(NODE) TREE_OPERAND (OMP_SECTIONS_CHECK (NODE), 0) +#define OMP_SECTIONS_CLAUSES(NODE) TREE_OPERAND (OMP_SECTIONS_CHECK (NODE), 1) + +#define OMP_SECTION_BODY(NODE) TREE_OPERAND (OMP_SECTION_CHECK (NODE), 0) + +#define OMP_SINGLE_BODY(NODE) TREE_OPERAND (OMP_SINGLE_CHECK (NODE), 0) +#define OMP_SINGLE_CLAUSES(NODE) TREE_OPERAND (OMP_SINGLE_CHECK (NODE), 1) + +#define OMP_MASTER_BODY(NODE) TREE_OPERAND (OMP_MASTER_CHECK (NODE), 0) + +#define OMP_ORDERED_BODY(NODE) TREE_OPERAND (OMP_ORDERED_CHECK (NODE), 0) + +#define OMP_CRITICAL_BODY(NODE) TREE_OPERAND (OMP_CRITICAL_CHECK (NODE), 0) +#define OMP_CRITICAL_NAME(NODE) TREE_OPERAND (OMP_CRITICAL_CHECK (NODE), 1) + +#define OMP_CLAUSE_CHAIN(NODE) \ + TREE_CHAIN (TREE_RANGE_CHECK (NODE, OMP_CLAUSE_PRIVATE, OMP_CLAUSE_DEFAULT)) +#define OMP_CLAUSE_DECL(NODE) \ + TREE_OPERAND (TREE_RANGE_CHECK (NODE, OMP_CLAUSE_PRIVATE, \ + OMP_CLAUSE_COPYPRIVATE), 0) + +/* True on a PRIVATE clause if its decl is kept around for debugging + information only and its DECL_VALUE_EXPR is supposed to point + to what it has been remapped to. */ +#define OMP_CLAUSE_PRIVATE_DEBUG(NODE) \ + TREE_PUBLIC (OMP_CLAUSE_PRIVATE_CHECK (NODE)) + +/* True on a LASTPRIVATE clause if a FIRSTPRIVATE clause for the same + decl is present in the chain. */ +#define OMP_CLAUSE_LASTPRIVATE_FIRSTPRIVATE(NODE) \ + TREE_PUBLIC (OMP_CLAUSE_LASTPRIVATE_CHECK (NODE)) + +#define OMP_CLAUSE_IF_EXPR(NODE) \ + TREE_OPERAND (OMP_CLAUSE_IF_CHECK (NODE), 0) +#define OMP_CLAUSE_NUM_THREADS_EXPR(NODE) \ + TREE_OPERAND (OMP_CLAUSE_NUM_THREADS_CHECK (NODE), 0) +#define OMP_CLAUSE_SCHEDULE_CHUNK_EXPR(NODE) \ + TREE_OPERAND (OMP_CLAUSE_SCHEDULE_CHECK (NODE), 0) + +#define OMP_CLAUSE_REDUCTION_CODE(NODE) \ + (OMP_CLAUSE_REDUCTION_CHECK (NODE)->exp.complexity) +#define OMP_CLAUSE_REDUCTION_INIT(NODE) \ + TREE_OPERAND (OMP_CLAUSE_REDUCTION_CHECK (NODE), 1) +#define OMP_CLAUSE_REDUCTION_MERGE(NODE) \ + TREE_OPERAND (OMP_CLAUSE_REDUCTION_CHECK (NODE), 2) +#define OMP_CLAUSE_REDUCTION_PLACEHOLDER(NODE) \ + TREE_OPERAND (OMP_CLAUSE_REDUCTION_CHECK (NODE), 3) + +enum omp_clause_schedule_kind +{ + OMP_CLAUSE_SCHEDULE_STATIC, + OMP_CLAUSE_SCHEDULE_DYNAMIC, + OMP_CLAUSE_SCHEDULE_GUIDED, + OMP_CLAUSE_SCHEDULE_RUNTIME +}; + +#define OMP_CLAUSE_SCHEDULE_KIND(NODE) \ + (OMP_CLAUSE_SCHEDULE_CHECK (NODE)->exp.complexity) + +enum omp_clause_default_kind +{ + OMP_CLAUSE_DEFAULT_UNSPECIFIED, + OMP_CLAUSE_DEFAULT_SHARED, + OMP_CLAUSE_DEFAULT_NONE, + OMP_CLAUSE_DEFAULT_PRIVATE +}; + +#define OMP_CLAUSE_DEFAULT_KIND(NODE) \ + (OMP_CLAUSE_DEFAULT_CHECK (NODE)->exp.complexity) + struct tree_exp GTY(()) { struct tree_common common; @@ -3210,6 +3312,9 @@ extern tree build3_stat (enum tree_code, tree, tree, tree, tree MEM_STAT_DECL); extern tree build4_stat (enum tree_code, tree, tree, tree, tree, tree MEM_STAT_DECL); #define build4(c,t1,t2,t3,t4,t5) build4_stat (c,t1,t2,t3,t4,t5 MEM_STAT_INFO) +extern tree build5_stat (enum tree_code, tree, tree, tree, tree, tree, + tree MEM_STAT_DECL); +#define build5(c,t1,t2,t3,t4,t5,t6) build5_stat (c,t1,t2,t3,t4,t5,t6 MEM_STAT_INFO) extern tree build7_stat (enum tree_code, tree, tree, tree, tree, tree, tree, tree, tree MEM_STAT_DECL); #define build7(c,t1,t2,t3,t4,t5,t6,t7,t8) \ @@ -3823,6 +3928,7 @@ extern bool commutative_tree_code (enum tree_code); extern tree upper_bound_in_type (tree, tree); extern tree lower_bound_in_type (tree, tree); extern int operand_equal_for_phi_arg_p (tree, tree); +extern bool empty_body_p (tree); /* In stmt.c */ @@ -3947,6 +4053,7 @@ extern tree strip_float_extensions (tree); extern tree c_strlen (tree, int); extern tree std_gimplify_va_arg_expr (tree, tree, tree *, tree *); extern tree build_va_arg_indirect_ref (tree); +tree build_string_literal (int, const char *); /* In convert.c */ extern tree strip_float_extensions (tree); |