diff options
51 files changed, 3004 insertions, 8 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 2b76bf9cad6..625dc71d5d4 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,58 @@ +2013-10-29 Balaji V. Iyer <balaji.v.iyer@intel.com> + + * builtins.c (is_builtin_name): Added a check for __cilkrts_detach and + __cilkrts_pop_frame. If matched, then return true for built-in + function name. + (expand_builtin): Added BUILT_IN_CILK_DETACH and + BUILT_IN_CILK_POP_FRAME case. + * langhooks-def.h (lhd_install_body_with_frame_cleanup): New prototype. + (lhs_cilk_detect_spawn): Likewise. + (LANG_HOOKS_DECLS): Added LANG_HOOKS_CILKPLUS. + (LANG_HOOKS_CILKPLUS_DETECT_SPAWN_AND_UNWRAP): New #define. + (LANG_HOOKS_CILKPLUS_FRAME_CLEANUP): Likewise. + (LANG_HOOKS_CILKPLUS_GIMPLIFY_SPAWN): Likewise. + (LANG_HOOKS_CILKPLUS): Likewise. + * tree.h (CILK_SPAWN_FN): Likewise. + * builtin.def (DEF_CILK_BUILTIN_STUB): Likewise. + * Makefile.in (C_COMMON_OBJS): Added c-family/cilk.o. + (OBJS): Added cilk-common.o. + (BUILTINS_DEF): Added cilk-builtins.def. + * langhooks.c (lhd_install_body_with_frame_cleanup): New function. + (lhd_cilk_detect_spawn): Likewise. + * langhooks.h (lang_hooks_for_cilkplus): New struct. + (struct lang_hooks): Added new field called "cilkplus." + * cilk-common.c: New file. + * cilk.h: Likewise. + * cilk-builtins.def: Likewise. + * cppbuiltin.c (define_builtin_macros_for_compilation_flags): Added + "__cilk" macro and set it to 200. + * function.h (struct function::cilk_frame_decl): New field. + (struct function::is_cilk_function): Likewise. + (struct function::calls_cilk_spawn): Likewise. + * gimplify.c (gimplify_call_expr): Added a check if the function call + being gimplified is a spawn detach point. If so, then add pop_frame + and detach function calls. + (gimplify_expr): Added a CILK_SPAWN_STMT and CILK_SYNC_STMT case + for gimplifying _Cilk_spawn and _Cilk_sync statements. + (gimplify_return_expr): Added a check for _Cilk_spawn usage in + function. If so, added a _Cilk_sync and gimplified it. + (gimplify_modify_expr): Added a check for _Cilk_spawn in MODIFY and + INIT_EXPRs. If so, then call gimplify_cilk_spawn. + * ipa-inline-analysis (initialize_inline_failed): Prevent inlining of + spawner function. + (can_inline_edge_p): Prevent inling of spawnee function. + * ira.c (ira_setup_eliminable_regset): Force usage of frame pointer + for functions that use Cilk keywords. + * tree-inline.h (struct copy_body_data::remap_var_for_cilk): New field. + * tree-pretty-print.c (dump_generic_node): Added CILK_SPAWN_STMT and + CILK_SYNC_STMT cases. + * tree.def (DEFTREECODE): Added CILK_SPAWN_STMT and CILK_SYNC_STMT + trees. + * generic.texi (CILK_SPAWN_STMT): Added documentation for _Cilk_spawn. + (CILK_SYNC_STMT): Added documentation for _Cilk_sync. + * passes.texi (Cilk Keywords): New section that describes the compiler + code changes for handling Cilk Keywords. + 2013-10-29 David Malcolm <dmalcolm@redhat.com> Patch autogenerated by refactor_symtab.py from diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 29609fd1f1d..3e26d78eea2 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -872,7 +872,7 @@ RTL_H = $(RTL_BASE_H) $(FLAGS_H) genrtl.h READ_MD_H = $(OBSTACK_H) $(HASHTAB_H) read-md.h PARAMS_H = params.h params.def BUILTINS_DEF = builtins.def sync-builtins.def omp-builtins.def \ - gtm-builtins.def sanitizer.def cilkplus.def + gtm-builtins.def sanitizer.def cilkplus.def cilk-builtins.def INTERNAL_FN_DEF = internal-fn.def INTERNAL_FN_H = internal-fn.h $(INTERNAL_FN_DEF) TREE_CORE_H = tree-core.h coretypes.h all-tree.def tree.def \ @@ -1137,7 +1137,7 @@ C_COMMON_OBJS = c-family/c-common.o c-family/c-cppbuiltin.o c-family/c-dump.o \ c-family/c-omp.o c-family/c-opts.o c-family/c-pch.o \ c-family/c-ppoutput.o c-family/c-pragma.o c-family/c-pretty-print.o \ c-family/c-semantics.o c-family/c-ada-spec.o \ - c-family/array-notation-common.o c-family/c-ubsan.o + c-family/array-notation-common.o c-family/cilk.o c-family/c-ubsan.o # Language-independent object files. # We put the insn-*.o files first so that a parallel make will build @@ -1182,6 +1182,7 @@ OBJS = \ cgraphbuild.o \ cgraphunit.o \ cgraphclones.o \ + cilk-common.o \ combine.o \ combine-stack-adj.o \ compare-elim.o \ diff --git a/gcc/builtins.c b/gcc/builtins.c index 10758cac2c4..cfe9be533b5 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -49,6 +49,7 @@ along with GCC; see the file COPYING3. If not see #include "diagnostic-core.h" #include "builtins.h" #include "ubsan.h" +#include "cilk.h" static tree do_mpc_arg1 (tree, tree, int (*)(mpc_ptr, mpc_srcptr, mpc_rnd_t)); @@ -235,6 +236,10 @@ is_builtin_name (const char *name) return true; if (strncmp (name, "__atomic_", 9) == 0) return true; + if (flag_enable_cilkplus + && (!strcmp (name, "__cilkrts_detach") + || !strcmp (name, "__cilkrts_pop_frame"))) + return true; return false; } @@ -6685,6 +6690,14 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, expand_builtin_set_thread_pointer (exp); return const0_rtx; + case BUILT_IN_CILK_DETACH: + expand_builtin_cilk_detach (exp); + return const0_rtx; + + case BUILT_IN_CILK_POP_FRAME: + expand_builtin_cilk_pop_frame (exp); + return const0_rtx; + default: /* just do library call, if unknown builtin */ break; } diff --git a/gcc/builtins.def b/gcc/builtins.def index 8ccf3ae3578..12c56be5c5b 100644 --- a/gcc/builtins.def +++ b/gcc/builtins.def @@ -147,6 +147,13 @@ along with GCC; see the file COPYING3. If not see false, true, true, ATTRS, false, \ (flag_openmp || flag_tree_parallelize_loops)) +/* Builtin used by implementation of Cilk Plus. Most of these are decomposed + by the compiler but a few are implemented in libcilkrts. */ +#undef DEF_CILK_BUILTIN_STUB +#define DEF_CILK_BUILTIN_STUB(ENUM, NAME) \ + DEF_BUILTIN (ENUM, NAME, BUILT_IN_NORMAL, BT_LAST, BT_LAST, false, false, \ + false, ATTR_LAST, false, false) + /* Builtin used by the implementation of GNU TM. These functions are mapped to the actual implementation of the STM library. */ #undef DEF_TM_BUILTIN @@ -846,6 +853,9 @@ DEF_GCC_BUILTIN (BUILT_IN_LINE, "LINE", BT_FN_INT, ATTR_NOTHROW_LEAF_LIST) /* OpenMP builtins. */ #include "omp-builtins.def" +/* Cilk keywords builtins. */ +#include "cilk-builtins.def" + /* GTM builtins. */ #include "gtm-builtins.def" diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog index 6c6435cca23..b9fed631057 100644 --- a/gcc/c-family/ChangeLog +++ b/gcc/c-family/ChangeLog @@ -1,3 +1,20 @@ +2013-10-29 Balaji V. Iyer <balaji.v.iyer@intel.com> + + * c-common.c (c_common_reswords[]): Added _Cilk_spawn and _Cilk_sync + fields. + (c_define_builtins): Called cilk_init_builtins if Cilk Plus is + enabled. + * c-common.h (enum rid): Added RID_CILK_SPAWN and RID_CILK_SYNC. + (insert_cilk_frame): New prototype. + (cilk_init_builtins): Likewise. + (gimplify_cilk_spawn): Likewise. + (c_cilk_install_body_w_frame_cleanup): Likewise. + (cilk_detect_spawn_and_unwrap): Likewise. + (cilk_set_spawn_marker): Likewise. + (build_cilk_sync): Likewise. + (build_cilk_spawn): Likewise. + * cilk.c: New file. + 2013-10-29 David Malcolm <dmalcolm@redhat.com> Patch autogenerated by refactor_symtab.py from diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c index b20fdd6a1ea..64731683961 100644 --- a/gcc/c-family/c-common.c +++ b/gcc/c-family/c-common.c @@ -409,6 +409,8 @@ const struct c_common_resword c_common_reswords[] = { "_Alignof", RID_ALIGNOF, D_CONLY }, { "_Bool", RID_BOOL, D_CONLY }, { "_Complex", RID_COMPLEX, 0 }, + { "_Cilk_spawn", RID_CILK_SPAWN, 0 }, + { "_Cilk_sync", RID_CILK_SYNC, 0 }, { "_Imaginary", RID_IMAGINARY, D_CONLY }, { "_Decimal32", RID_DFLOAT32, D_CONLY | D_EXT }, { "_Decimal64", RID_DFLOAT64, D_CONLY | D_EXT }, @@ -5219,6 +5221,9 @@ c_define_builtins (tree va_list_ref_type_node, tree va_list_arg_type_node) targetm.init_builtins (); build_common_builtin_nodes (); + + if (flag_enable_cilkplus) + cilk_init_builtins (); } /* Like get_identifier, but avoid warnings about null arguments when diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h index 1f8333e94a0..8dd40c83e01 100644 --- a/gcc/c-family/c-common.h +++ b/gcc/c-family/c-common.h @@ -148,6 +148,9 @@ enum rid /* C++11 */ RID_CONSTEXPR, RID_DECLTYPE, RID_NOEXCEPT, RID_NULLPTR, RID_STATIC_ASSERT, + /* Cilk Plus keywords. */ + RID_CILK_SPAWN, RID_CILK_SYNC, + /* Objective-C ("AT" reserved words - they are only keywords when they follow '@') */ RID_AT_ENCODE, RID_AT_END, @@ -1356,4 +1359,18 @@ extern void cilkplus_extract_an_triplets (vec<tree, va_gc> *, size_t, size_t, vec<vec<an_parts> > *); extern vec <tree, va_gc> *fix_sec_implicit_args (location_t, vec <tree, va_gc> *, vec<an_loop_parts>, size_t, tree); + +/* In cilk.c. */ +extern tree insert_cilk_frame (tree); +extern void cilk_init_builtins (void); +extern int gimplify_cilk_spawn (tree *, gimple_seq *, gimple_seq *); +extern void c_cilk_install_body_w_frame_cleanup (tree, tree); +extern bool cilk_detect_spawn_and_unwrap (tree *); +extern bool cilk_set_spawn_marker (location_t, tree); +extern tree build_cilk_sync (void); +extern tree build_cilk_spawn (location_t, tree); +extern tree make_cilk_frame (tree); +extern tree create_cilk_function_exit (tree, bool, bool); +extern tree cilk_install_body_pedigree_operations (tree); + #endif /* ! GCC_C_COMMON_H */ diff --git a/gcc/c-family/cilk.c b/gcc/c-family/cilk.c new file mode 100644 index 00000000000..91f10d5f283 --- /dev/null +++ b/gcc/c-family/cilk.c @@ -0,0 +1,1305 @@ +/* This file is part of the Intel(R) Cilk(TM) Plus support + This file contains the CilkPlus Intrinsics + Copyright (C) 2013 Free Software Foundation, Inc. + Contributed by Balaji V. Iyer <balaji.v.iyer@intel.com>, + Intel Corporation + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tree.h" +#include "langhooks.h" +#include "gimple.h" +#include "tree-iterator.h" +#include "tree-inline.h" +#include "c-family/c-common.h" +#include "toplev.h" +#include "cgraph.h" +#include "diagnostic.h" +#include "cilk.h" + +enum add_variable_type { + /* Reference to previously-defined variable. */ + ADD_READ, + /* Definition of a new variable in inner-scope. */ + ADD_BIND, + /* Write to possibly previously-defined variable. */ + ADD_WRITE +}; + +enum cilk_block_type { + /* Indicates a _Cilk_spawn block. 30 was an arbitary number picked for + ease of debugging. */ + CILK_BLOCK_SPAWN = 30, + /* Indicates _Cilk_for statement block. */ + CILK_BLOCK_FOR +}; + +struct wrapper_data +{ + /* Kind of function to be created. */ + enum cilk_block_type type; + /* Signature of helper function. */ + tree fntype; + /* Containing function. */ + tree context; + /* Disposition of all variables in the inner statement. */ + struct pointer_map_t *decl_map; + /* True if this function needs a static chain. */ + bool nested; + /* Arguments to be passed to wrapper function, currently a list. */ + tree arglist; + /* Argument types, a list. */ + tree argtypes; + /* Incoming parameters. */ + tree parms; + /* Outer BLOCK object. */ + tree block; +}; + +static void extract_free_variables (tree, struct wrapper_data *, + enum add_variable_type); +static HOST_WIDE_INT cilk_wrapper_count; + +/* Marks the CALL_EXPR or FUNCTION_DECL, FCALL, as a spawned function call + and the current function as a spawner. Emit error if the function call + is outside a function or if a non function-call is spawned. */ + +inline bool +cilk_set_spawn_marker (location_t loc, tree fcall) +{ + if (!current_function_decl) + { + error_at (loc, "%<_Cilk_spawn%> may only be used inside a function"); + return false; + } + else if (fcall == error_mark_node) + /* Error reporting here is not necessary here since if FCALL is an + error_mark_node, the function marking it as error would have reported + it. */ + return false; + else if (TREE_CODE (fcall) != CALL_EXPR + && TREE_CODE (fcall) != FUNCTION_DECL + /* In C++, TARGET_EXPR is generated when we have an overloaded + '=' operator. */ + && TREE_CODE (fcall) != TARGET_EXPR) + { + error_at (loc, "only function calls can be spawned"); + return false; + } + else + { + cfun->calls_cilk_spawn = true; + return true; + } +} + +/* This function will output the exit conditions for a spawn call. */ + +tree +create_cilk_function_exit (tree frame, bool detaches, bool needs_sync) +{ + tree epi = alloc_stmt_list (); + + if (needs_sync) + append_to_statement_list (build_cilk_sync (), &epi); + tree func_ptr = build1 (ADDR_EXPR, cilk_frame_ptr_type_decl, frame); + tree pop_frame = build_call_expr (cilk_pop_fndecl, 1, func_ptr); + tree worker = cilk_dot (frame, CILK_TI_FRAME_WORKER, 0); + tree current = cilk_arrow (worker, CILK_TI_WORKER_CUR, 0); + tree parent = cilk_dot (frame, CILK_TI_FRAME_PARENT, 0); + tree set_current = build2 (MODIFY_EXPR, void_type_node, current, parent); + append_to_statement_list (set_current, &epi); + append_to_statement_list (pop_frame, &epi); + tree call = build_call_expr (cilk_leave_fndecl, 1, func_ptr); + if (!detaches) + { + tree flags = cilk_dot (frame, CILK_TI_FRAME_FLAGS, false); + tree flags_cmp_expr = fold_build2 (NE_EXPR, TREE_TYPE (flags), flags, + build_int_cst (TREE_TYPE (flags), + CILK_FRAME_VERSION)); + call = fold_build3 (COND_EXPR, void_type_node, flags_cmp_expr, + call, build_empty_stmt (EXPR_LOCATION (flags))); + } + append_to_statement_list (call, &epi); + return epi; +} + +/* Trying to get the correct cfun for the FUNCTION_DECL indicated by OUTER. */ + +static void +pop_cfun_to (tree outer) +{ + pop_cfun (); + current_function_decl = outer; + gcc_assert (cfun == DECL_STRUCT_FUNCTION (current_function_decl)); + gcc_assert (cfun->decl == current_function_decl); +} + +/* This function does whatever is necessary to make the compiler emit a newly + generated function, FNDECL. */ + +static void +call_graph_add_fn (tree fndecl) +{ + const tree outer = current_function_decl; + struct function *f = DECL_STRUCT_FUNCTION (fndecl); + gcc_assert (TREE_CODE (fndecl) == FUNCTION_DECL); + + f->is_cilk_function = 1; + f->curr_properties = cfun->curr_properties; + gcc_assert (cfun == DECL_STRUCT_FUNCTION (outer)); + gcc_assert (cfun->decl == outer); + + push_cfun (f); + cgraph_create_node (fndecl); + pop_cfun_to (outer); +} + +/* Return true if this is a tree which is allowed to contain a spawn as + operand 0. + A spawn call may be wrapped in a series of unary operations such + as conversions. These conversions need not be "useless" + to be disregarded because they are retained in the spawned + statement. They are bypassed only to look for a spawn + within. + A comparison to constant is simple enough to allow, and + is used to convert to bool. */ + +static bool +cilk_ignorable_spawn_rhs_op (tree exp) +{ + enum tree_code code = TREE_CODE (exp); + switch (TREE_CODE_CLASS (code)) + { + case tcc_expression: + return code == ADDR_EXPR; + case tcc_comparison: + /* We need the spawn as operand 0 for now. That's where it + appears in the only case we really care about, conversion + to bool. */ + return (TREE_CODE (TREE_OPERAND (exp, 1)) == INTEGER_CST); + case tcc_unary: + case tcc_reference: + return true; + default: + return false; + } +} + +/* Helper function for walk_tree. If *TP is a CILK_SPAWN_STMT, then unwrap + this "wrapper." The function returns NULL_TREE regardless. */ + +static tree +unwrap_cilk_spawn_stmt (tree *tp, int *walk_subtrees, void *) +{ + if (TREE_CODE (*tp) == CILK_SPAWN_STMT) + { + *tp = CILK_SPAWN_FN (*tp); + *walk_subtrees = 0; + } + return NULL_TREE; +} + +/* Returns true when EXP is a CALL_EXPR with _Cilk_spawn in front. Unwraps + CILK_SPAWN_STMT wrapper from the CALL_EXPR in *EXP0 statement. */ + +static bool +recognize_spawn (tree exp, tree *exp0) +{ + bool spawn_found = false; + if (TREE_CODE (exp) == CILK_SPAWN_STMT) + { + /* Remove the CALL_EXPR from CILK_SPAWN_STMT wrapper. */ + exp = CILK_SPAWN_FN (exp); + walk_tree (exp0, unwrap_cilk_spawn_stmt, NULL, NULL); + spawn_found = true; + } + return spawn_found; +} + +/* Returns true if *EXP0 is a recognized form of spawn. Recognized forms are, + after conversion to void, a call expression at outer level or an assignment + at outer level with the right hand side being a spawned call. + In addition to this, it also unwraps the CILK_SPAWN_STMT cover from the + CALL_EXPR that is being spawned. + Note that `=' in C++ may turn into a CALL_EXPR rather than a MODIFY_EXPR. */ + +bool +cilk_detect_spawn_and_unwrap (tree *exp0) +{ + tree exp = *exp0; + + if (!TREE_SIDE_EFFECTS (exp)) + return false; + + /* Strip off any conversion to void. It does not affect whether spawn + is supported here. */ + if (TREE_CODE (exp) == CONVERT_EXPR && VOID_TYPE_P (TREE_TYPE (exp))) + exp = TREE_OPERAND (exp, 0); + + if (TREE_CODE (exp) == MODIFY_EXPR || TREE_CODE (exp) == INIT_EXPR) + exp = TREE_OPERAND (exp, 1); + + while (cilk_ignorable_spawn_rhs_op (exp)) + exp = TREE_OPERAND (exp, 0); + + if (TREE_CODE (exp) == TARGET_EXPR) + if (TARGET_EXPR_INITIAL (exp) + && TREE_CODE (TARGET_EXPR_INITIAL (exp)) != AGGR_INIT_EXPR) + exp = TARGET_EXPR_INITIAL (exp); + + /* Happens with C++ TARGET_EXPR. */ + if (exp == NULL_TREE) + return false; + + while (TREE_CODE (exp) == CLEANUP_POINT_EXPR || TREE_CODE (exp) == EXPR_STMT) + exp = TREE_OPERAND (exp, 0); + + /* Now we should have a CALL_EXPR with a CILK_SPAWN_STMT wrapper around + it, or return false. */ + if (recognize_spawn (exp, exp0)) + return true; + return false; +} + +/* This function will build and return a FUNCTION_DECL using information + from *WD. */ + +static tree +create_cilk_helper_decl (struct wrapper_data *wd) +{ + char name[20]; + if (wd->type == CILK_BLOCK_FOR) + sprintf (name, "_cilk_for_%ld", cilk_wrapper_count++); + else if (wd->type == CILK_BLOCK_SPAWN) + sprintf (name, "_cilk_spn_%ld", cilk_wrapper_count++); + else + gcc_unreachable (); + + clean_symbol_name (name); + tree fndecl = build_decl (UNKNOWN_LOCATION, FUNCTION_DECL, + get_identifier (name), wd->fntype); + + TREE_PUBLIC (fndecl) = 0; + TREE_STATIC (fndecl) = 1; + TREE_USED (fndecl) = 1; + DECL_ARTIFICIAL (fndecl) = 0; + DECL_IGNORED_P (fndecl) = 0; + DECL_EXTERNAL (fndecl) = 0; + + DECL_CONTEXT (fndecl) = wd->context; + tree block = make_node (BLOCK); + DECL_INITIAL (fndecl) = block; + TREE_USED (block) = 1; + gcc_assert (!DECL_SAVED_TREE (fndecl)); + + /* Inlining would defeat the purpose of this wrapper. + Either it secretly switches stack frames or it allocates + a stable stack frame to hold function arguments even if + the parent stack frame is stolen. */ + DECL_UNINLINABLE (fndecl) = 1; + + tree result_decl = build_decl (UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE, + void_type_node); + DECL_ARTIFICIAL (result_decl) = 0; + DECL_IGNORED_P (result_decl) = 1; + DECL_CONTEXT (result_decl) = fndecl; + DECL_RESULT (fndecl) = result_decl; + + return fndecl; +} + +/* A function used by walk tree to find wrapper parms. */ + +static bool +wrapper_parm_cb (const void *key0, void **val0, void *data) +{ + struct wrapper_data *wd = (struct wrapper_data *) data; + tree arg = * (tree *)&key0; + tree val = (tree)*val0; + tree parm; + + if (val == error_mark_node || val == arg) + return true; + + if (TREE_CODE (val) == PAREN_EXPR) + { + /* We should not reach here with a register receiver. + We may see a register variable modified in the + argument list. Because register variables are + worker-local we don't need to work hard to support + them in code that spawns. */ + if ((TREE_CODE (arg) == VAR_DECL) && DECL_HARD_REGISTER (arg)) + { + error_at (EXPR_LOCATION (arg), + "explicit register variable %qD may not be modified in " + "spawn", arg); + arg = null_pointer_node; + } + else + arg = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (arg)), arg); + + val = TREE_OPERAND (val, 0); + *val0 = val; + gcc_assert (TREE_CODE (val) == INDIRECT_REF); + parm = TREE_OPERAND (val, 0); + STRIP_NOPS (parm); + } + else + parm = val; + TREE_CHAIN (parm) = wd->parms; + wd->parms = parm; + wd->argtypes = tree_cons (NULL_TREE, TREE_TYPE (parm), wd->argtypes); + wd->arglist = tree_cons (NULL_TREE, arg, wd->arglist); + return true; +} + +/* This function is used to build a wrapper of a certain type. */ + +static void +build_wrapper_type (struct wrapper_data *wd) +{ + wd->arglist = NULL_TREE; + wd->parms = NULL_TREE; + wd->argtypes = void_list_node; + + pointer_map_traverse (wd->decl_map, wrapper_parm_cb, wd); + gcc_assert (wd->type != CILK_BLOCK_FOR); + + /* Now build a function. + Its return type is void (all side effects are via explicit parameters). + Its parameters are WRAPPER_PARMS with type WRAPPER_TYPES. + Actual arguments in the caller are WRAPPER_ARGS. */ + wd->fntype = build_function_type (void_type_node, wd->argtypes); +} + +/* This function checks all the CALL_EXPRs in *TP found by cilk_outline. */ + +static tree +check_outlined_calls (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED, + void *data) +{ + bool *throws = (bool *) data; + tree t = *tp; + int flags; + + if (TREE_CODE (t) != CALL_EXPR) + return 0; + flags = call_expr_flags (t); + + if (!(flags & ECF_NOTHROW) && flag_exceptions) + *throws = true; + if (flags & ECF_RETURNS_TWICE) + error_at (EXPR_LOCATION (t), + "cannot spawn call to function that returns twice"); + return 0; +} + +/* Each DECL in the source code (spawned statement) is passed to this function + once. Each instance of the DECL is replaced with the result of this + function. + + The parameters of the wrapper should have been entered into the map already. + This function only deals with variables with scope limited to the + spawned expression. */ + +static tree +copy_decl_for_cilk (tree decl, copy_body_data *id) +{ + switch (TREE_CODE (decl)) + { + case VAR_DECL: + return copy_decl_no_change (decl, id); + + case LABEL_DECL: + error_at (EXPR_LOCATION (decl), "invalid use of label %q+D in " + "%<_Cilk_spawn%>", + decl); + return error_mark_node; + + case RESULT_DECL: + case PARM_DECL: + /* RESULT_DECL and PARM_DECL has already been entered into the map. */ + default: + gcc_unreachable (); + return error_mark_node; + } +} + +/* Copy all local variables. */ + +static bool +for_local_cb (const void *k_v, void **vp, void *p) +{ + tree k = *(tree *) &k_v; + tree v = (tree) *vp; + + if (v == error_mark_node) + *vp = copy_decl_no_change (k, (copy_body_data *) p); + return true; +} + +/* Copy all local declarations from a _Cilk_spawned function's body. */ + +static bool +wrapper_local_cb (const void *k_v, void **vp, void *data) +{ + copy_body_data *id = (copy_body_data *) data; + tree key = *(tree *) &k_v; + tree val = (tree) *vp; + + if (val == error_mark_node) + *vp = copy_decl_for_cilk (key, id); + + return true; +} + +/* Alter a tree STMT from OUTER_FN to form the body of INNER_FN. */ + +static void +cilk_outline (tree inner_fn, tree *stmt_p, struct wrapper_data *wd) +{ + const tree outer_fn = wd->context; + const bool nested = (wd->type == CILK_BLOCK_FOR); + copy_body_data id; + bool throws; + + DECL_STATIC_CHAIN (outer_fn) = 1; + + memset (&id, 0, sizeof (id)); + /* Copy from the function containing the spawn... */ + id.src_fn = outer_fn; + + /* ...to the wrapper. */ + id.dst_fn = inner_fn; + id.src_cfun = DECL_STRUCT_FUNCTION (outer_fn); + + /* There shall be no RETURN in spawn helper. */ + id.retvar = 0; + id.decl_map = wd->decl_map; + id.copy_decl = nested ? copy_decl_no_change : copy_decl_for_cilk; + id.block = DECL_INITIAL (inner_fn); + id.transform_lang_insert_block = NULL; + + id.transform_new_cfg = true; + id.transform_call_graph_edges = CB_CGE_MOVE; + id.remap_var_for_cilk = true; + id.regimplify = true; /* unused? */ + + insert_decl_map (&id, wd->block, DECL_INITIAL (inner_fn)); + + /* We don't want the private variables any more. */ + pointer_map_traverse (wd->decl_map, nested ? for_local_cb : wrapper_local_cb, + &id); + + walk_tree (stmt_p, copy_tree_body_r, &id, NULL); + + /* See if this function can throw or calls something that should + not be spawned. The exception part is only necessary if + flag_exceptions && !flag_non_call_exceptions. */ + throws = false ; + (void) walk_tree_without_duplicates (stmt_p, check_outlined_calls, &throws); +} + +/* Generate the body of a wrapper function that assigns the + result of the expression RHS into RECEIVER. RECEIVER must + be NULL if this is not a spawn -- the wrapper will return + a value. If this is a spawn, the wrapper will return void. */ + +static tree +create_cilk_wrapper_body (tree stmt, struct wrapper_data *wd) +{ + const tree outer = current_function_decl; + tree fndecl; + tree p; + + /* Build the type of the wrapper and its argument list from the + variables that it requires. */ + build_wrapper_type (wd); + + /* Emit a function that takes WRAPPER_PARMS incoming and applies ARGS + (modified) to the wrapped function. Return the wrapper and modified ARGS + to the caller to generate a function call. */ + fndecl = create_cilk_helper_decl (wd); + push_struct_function (fndecl); + if (wd->nested && (wd->type == CILK_BLOCK_FOR)) + { + gcc_assert (TREE_VALUE (wd->arglist) == NULL_TREE); + TREE_VALUE (wd->arglist) = build2 (FDESC_EXPR, ptr_type_node, + fndecl, integer_one_node); + } + DECL_ARGUMENTS (fndecl) = wd->parms; + + for (p = wd->parms; p; p = TREE_CHAIN (p)) + DECL_CONTEXT (p) = fndecl; + + cilk_outline (fndecl, &stmt, wd); + stmt = fold_build_cleanup_point_expr (void_type_node, stmt); + gcc_assert (!DECL_SAVED_TREE (fndecl)); + lang_hooks.cilkplus.install_body_with_frame_cleanup (fndecl, stmt); + gcc_assert (DECL_SAVED_TREE (fndecl)); + + pop_cfun_to (outer); + + /* Recognize the new function. */ + call_graph_add_fn (fndecl); + return fndecl; +} + +/* Initializes the wrapper data structure. */ + +static void +init_wd (struct wrapper_data *wd, enum cilk_block_type type) +{ + wd->type = type; + wd->fntype = NULL_TREE; + wd->context = current_function_decl; + wd->decl_map = pointer_map_create (); + /* _Cilk_for bodies are always nested. Others start off as + normal functions. */ + wd->nested = (type == CILK_BLOCK_FOR); + wd->arglist = NULL_TREE; + wd->argtypes = NULL_TREE; + wd->block = NULL_TREE; +} + +/* Clears the wrapper data structure. */ + +static void +free_wd (struct wrapper_data *wd) +{ + pointer_map_destroy (wd->decl_map); + wd->nested = false; + wd->arglist = NULL_TREE; + wd->argtypes = NULL_TREE; + wd->parms = NULL_TREE; +} + + + /* Given a variable in an expression to be extracted into + a helper function, declare the helper function parameter + to receive it. + + On entry the value of the (key, value) pair may be + + (*, error_mark_node) -- Variable is private to helper function, + do nothing. + + (var, var) -- Reference to outer scope (function or global scope). + + (var, integer 0) -- Capture by value, save newly-declared PARM_DECL + for value in value slot. + + (var, integer 1) -- Capture by reference, declare pointer to type + as new PARM_DECL and store (spawn_stmt (indirect_ref (parm)). + + (var, ???) -- Pure output argument, handled similarly to above. +*/ + +static bool +declare_one_free_variable (const void *var0, void **map0, + void *data ATTRIBUTE_UNUSED) +{ + const_tree var = (const_tree) var0; + tree map = (tree)*map0; + tree var_type = TREE_TYPE (var), arg_type; + bool by_reference; + tree parm; + + gcc_assert (DECL_P (var)); + + /* Ignore truly local variables. */ + if (map == error_mark_node) + return true; + /* Ignore references to the parent function. */ + if (map == var) + return true; + + gcc_assert (TREE_CODE (map) == INTEGER_CST); + + /* A value is passed by reference if: + + 1. It is addressable, so that a copy may not be made. + 2. It is modified in the spawned statement. + In the future this function may want to arrange + a warning if the spawned statement is a loop body + because an output argument would indicate a race. + Note: Earlier passes must have marked the variable addressable. + 3. It is expensive to copy. */ + by_reference = + (TREE_ADDRESSABLE (var_type) + /* Arrays must be passed by reference. This is required for C + semantics -- arrays are not first class objects. Other + aggregate types can and should be passed by reference if + they are not passed to the spawned function. We aren't yet + distinguishing safe uses in argument calculation from unsafe + uses as outgoing function arguments, so we make a copy to + stabilize the value. */ + || TREE_CODE (var_type) == ARRAY_TYPE + || (tree) map == integer_one_node); + + if (by_reference) + var_type = build_qualified_type (build_pointer_type (var_type), + TYPE_QUAL_RESTRICT); + gcc_assert (!TREE_ADDRESSABLE (var_type)); + + /* Maybe promote to int. */ + if (INTEGRAL_TYPE_P (var_type) && COMPLETE_TYPE_P (var_type) + && INT_CST_LT_UNSIGNED (TYPE_SIZE (var_type), + TYPE_SIZE (integer_type_node))) + arg_type = integer_type_node; + else + arg_type = var_type; + + parm = build_decl (UNKNOWN_LOCATION, PARM_DECL, NULL_TREE, var_type); + DECL_ARG_TYPE (parm) = arg_type; + DECL_ARTIFICIAL (parm) = 0; + TREE_READONLY (parm) = 1; + + if (by_reference) + { + parm = build1 (INDIRECT_REF, TREE_TYPE (var_type), parm); + parm = build1 (PAREN_EXPR, void_type_node, parm); + } + *map0 = parm; + return true; +} + +/* Returns a wrapper function for a _Cilk_spawn. */ + +static tree +create_cilk_wrapper (tree exp, tree *args_out) +{ + struct wrapper_data wd; + tree fndecl; + + init_wd (&wd, CILK_BLOCK_SPAWN); + + if (TREE_CODE (exp) == CONVERT_EXPR) + exp = TREE_OPERAND (exp, 0); + + /* Special handling for top level INIT_EXPR. Usually INIT_EXPR means the + variable is defined in the spawned expression and can be private to the + spawn helper. A top level INIT_EXPR defines a variable to be initialized + by spawn and the variable must remain in the outer function. */ + if (TREE_CODE (exp) == INIT_EXPR) + { + extract_free_variables (TREE_OPERAND (exp, 0), &wd, ADD_WRITE); + extract_free_variables (TREE_OPERAND (exp, 1), &wd, ADD_READ); + /* TREE_TYPE should be void. Be defensive. */ + if (TREE_TYPE (exp) != void_type_node) + extract_free_variables (TREE_TYPE (exp), &wd, ADD_READ); + } + else + extract_free_variables (exp, &wd, ADD_READ); + pointer_map_traverse (wd.decl_map, declare_one_free_variable, &wd); + wd.block = TREE_BLOCK (exp); + if (!wd.block) + wd.block = DECL_INITIAL (current_function_decl); + + /* Now fvars maps the old variable to incoming variable. Update + the expression and arguments to refer to the new names. */ + fndecl = create_cilk_wrapper_body (exp, &wd); + *args_out = wd.arglist; + + free_wd (&wd); + + return fndecl; +} + +/* Transform *SPAWN_P, a spawned CALL_EXPR, to gimple. *SPAWN_P can be a + CALL_EXPR, INIT_EXPR or MODIFY_EXPR. Returns GS_OK if everything is fine, + and GS_UNHANDLED, otherwise. */ + +int +gimplify_cilk_spawn (tree *spawn_p, gimple_seq *before ATTRIBUTE_UNUSED, + gimple_seq *after ATTRIBUTE_UNUSED) +{ + tree expr = *spawn_p; + tree function, call1, call2, new_args; + tree ii_args = NULL_TREE; + int total_args = 0, ii = 0; + tree *arg_array; + tree setjmp_cond_expr = NULL_TREE; + tree setjmp_expr, spawn_expr, setjmp_value = NULL_TREE; + + cfun->calls_cilk_spawn = 1; + cfun->is_cilk_function = 1; + + /* Remove CLEANUP_POINT_EXPR and EXPR_STMT from *spawn_p. */ + while (TREE_CODE (expr) == CLEANUP_POINT_EXPR + || TREE_CODE (expr) == EXPR_STMT) + expr = TREE_OPERAND (expr, 0); + + new_args = NULL; + function = create_cilk_wrapper (expr, &new_args); + + /* This should give the number of parameters. */ + total_args = list_length (new_args); + arg_array = XNEWVEC (tree, total_args); + + ii_args = new_args; + for (ii = 0; ii < total_args; ii++) + { + arg_array[ii] = TREE_VALUE (ii_args); + ii_args = TREE_CHAIN (ii_args); + } + + TREE_USED (function) = 1; + rest_of_decl_compilation (function, 0, 0); + + call1 = cilk_call_setjmp (cfun->cilk_frame_decl); + + if (*arg_array == NULL_TREE) + call2 = build_call_expr (function, 0); + else + call2 = build_call_expr_loc_array (EXPR_LOCATION (*spawn_p), function, + total_args, arg_array); + *spawn_p = alloc_stmt_list (); + tree f_ptr_type = build_pointer_type (TREE_TYPE (cfun->cilk_frame_decl)); + tree frame_ptr = build1 (ADDR_EXPR, f_ptr_type, cfun->cilk_frame_decl); + tree save_fp = build_call_expr (cilk_save_fp_fndecl, 1, frame_ptr); + append_to_statement_list (save_fp, spawn_p); + setjmp_value = create_tmp_var (TREE_TYPE (call1), NULL); + setjmp_expr = fold_build2 (MODIFY_EXPR, void_type_node, setjmp_value, call1); + + append_to_statement_list_force (setjmp_expr, spawn_p); + + setjmp_cond_expr = fold_build2 (EQ_EXPR, TREE_TYPE (call1), setjmp_value, + build_int_cst (TREE_TYPE (call1), 0)); + spawn_expr = fold_build3 (COND_EXPR, void_type_node, setjmp_cond_expr, + call2, build_empty_stmt (EXPR_LOCATION (call1))); + append_to_statement_list (spawn_expr, spawn_p); + + return GS_OK; +} + +/* Make the frames necessary for a spawn call. */ + +tree +make_cilk_frame (tree fn) +{ + struct function *f = DECL_STRUCT_FUNCTION (fn); + tree decl; + + if (f->cilk_frame_decl) + return f->cilk_frame_decl; + + decl = build_decl (EXPR_LOCATION (fn), VAR_DECL, NULL_TREE, + cilk_frame_type_decl); + DECL_CONTEXT (decl) = fn; + DECL_SEEN_IN_BIND_EXPR_P (decl) = 1; + f->cilk_frame_decl = decl; + return decl; +} + +/* Returns a STATEMENT_LIST with all the pedigree operations required for + install body with frame cleanup functions. FRAME_PTR is the pointer to + __cilkrts_stack_frame created by make_cilk_frame. */ + +tree +cilk_install_body_pedigree_operations (tree frame_ptr) +{ + tree body_list = alloc_stmt_list (); + tree enter_frame = build_call_expr (cilk_enter_fast_fndecl, 1, frame_ptr); + append_to_statement_list (enter_frame, &body_list); + + tree parent = cilk_arrow (frame_ptr, CILK_TI_FRAME_PARENT, 0); + tree worker = cilk_arrow (frame_ptr, CILK_TI_FRAME_WORKER, 0); + + tree pedigree = cilk_arrow (frame_ptr, CILK_TI_FRAME_PEDIGREE, 0); + tree pedigree_rank = cilk_dot (pedigree, CILK_TI_PEDIGREE_RANK, 0); + tree parent_pedigree = cilk_dot (pedigree, CILK_TI_PEDIGREE_PARENT, 0); + tree pedigree_parent = cilk_arrow (parent, CILK_TI_FRAME_PEDIGREE, 0); + tree pedigree_parent_rank = cilk_dot (pedigree_parent, + CILK_TI_PEDIGREE_RANK, 0); + tree pedigree_parent_parent = cilk_dot (pedigree_parent, + CILK_TI_PEDIGREE_PARENT, 0); + tree worker_pedigree = cilk_arrow (worker, CILK_TI_WORKER_PEDIGREE, 1); + tree w_pedigree_rank = cilk_dot (worker_pedigree, CILK_TI_PEDIGREE_RANK, 0); + tree w_pedigree_parent = cilk_dot (worker_pedigree, + CILK_TI_PEDIGREE_PARENT, 0); + + /* sf.pedigree.rank = worker->pedigree.rank. */ + tree exp1 = build2 (MODIFY_EXPR, void_type_node, pedigree_rank, + w_pedigree_rank); + append_to_statement_list (exp1, &body_list); + + /* sf.pedigree.parent = worker->pedigree.parent. */ + exp1 = build2 (MODIFY_EXPR, void_type_node, parent_pedigree, + w_pedigree_parent); + append_to_statement_list (exp1, &body_list); + + /* sf.call_parent->pedigree.rank = worker->pedigree.rank. */ + exp1 = build2 (MODIFY_EXPR, void_type_node, pedigree_parent_rank, + w_pedigree_rank); + append_to_statement_list (exp1, &body_list); + + /* sf.call_parent->pedigree.parent = worker->pedigree.parent. */ + exp1 = build2 (MODIFY_EXPR, void_type_node, pedigree_parent_parent, + w_pedigree_parent); + append_to_statement_list (exp1, &body_list); + + /* sf->worker.pedigree.rank = 0. */ + exp1 = build2 (MODIFY_EXPR, void_type_node, w_pedigree_rank, + build_zero_cst (uint64_type_node)); + append_to_statement_list (exp1, &body_list); + + /* sf->pedigree.parent = &sf->pedigree. */ + exp1 = build2 (MODIFY_EXPR, void_type_node, w_pedigree_parent, + build1 (ADDR_EXPR, + build_pointer_type (cilk_pedigree_type_decl), + pedigree)); + append_to_statement_list (exp1, &body_list); + return body_list; +} + +/* Inserts "cleanup" functions after the function-body of FNDECL. FNDECL is a + spawn-helper and BODY is the newly created body for FNDECL. */ + +void +c_cilk_install_body_w_frame_cleanup (tree fndecl, tree body) +{ + tree list = alloc_stmt_list (); + tree frame = make_cilk_frame (fndecl); + tree dtor = create_cilk_function_exit (frame, false, true); + add_local_decl (cfun, frame); + + DECL_SAVED_TREE (fndecl) = list; + tree frame_ptr = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (frame)), + frame); + tree body_list = cilk_install_body_pedigree_operations (frame_ptr); + gcc_assert (TREE_CODE (body_list) == STATEMENT_LIST); + + tree detach_expr = build_call_expr (cilk_detach_fndecl, 1, frame_ptr); + append_to_statement_list (detach_expr, &body_list); + append_to_statement_list (body, &body_list); + append_to_statement_list (build_stmt (EXPR_LOCATION (body), TRY_FINALLY_EXPR, + body_list, dtor), &list); +} + +/* Add a new variable, VAR to a variable list in WD->DECL_MAP. HOW indicates + whether the variable is previously defined, currently defined, or a variable + that is being written to. */ + +static void +add_variable (struct wrapper_data *wd, tree var, enum add_variable_type how) +{ + void **valp; + + valp = pointer_map_contains (wd->decl_map, (void *) var); + if (valp) + { + tree val = (tree) *valp; + /* If the variable is local, do nothing. */ + if (val == error_mark_node) + return; + /* If the variable was entered with itself as value, + meaning it belongs to an outer scope, do not alter + the value. */ + if (val == var) + return; + /* A statement expression may cause a variable to be + bound twice, once in BIND_EXPR and again in a + DECL_EXPR. That case caused a return in the + test above. Any other duplicate definition is + an error. */ + gcc_assert (how != ADD_BIND); + if (how != ADD_WRITE) + return; + /* This variable might have been entered as read but is now written. */ + *valp = (void *) var; + wd->nested = true; + return; + } + else + { + tree val = NULL_TREE; + + /* Nested function rewriting silently discards hard register + assignments for function scope variables, and they wouldn't + work anyway. Warn here. This misses one case: if the + register variable is used as the loop bound or increment it + has already been added to the map. */ + if ((how != ADD_BIND) && (TREE_CODE (var) == VAR_DECL) + && !DECL_EXTERNAL (var) && DECL_HARD_REGISTER (var)) + warning (0, "register assignment ignored for %qD used in Cilk block", + var); + + switch (how) + { + /* ADD_BIND means always make a fresh new variable. */ + case ADD_BIND: + val = error_mark_node; + break; + /* ADD_READ means + 1. For cilk_for, refer to the outer scope definition as-is + 2. For a spawned block, take a scalar in an rgument + and otherwise refer to the outer scope definition as-is. + 3. For a spawned call, take a scalar in an argument. */ + case ADD_READ: + switch (wd->type) + { + case CILK_BLOCK_FOR: + val = var; + break; + case CILK_BLOCK_SPAWN: + if (TREE_ADDRESSABLE (var)) + { + val = var; + wd->nested = true; + break; + } + val = integer_zero_node; + break; + } + break; + case ADD_WRITE: + switch (wd->type) + { + case CILK_BLOCK_FOR: + val = var; + wd->nested = true; + break; + case CILK_BLOCK_SPAWN: + if (TREE_ADDRESSABLE (var)) + val = integer_one_node; + else + { + val = var; + wd->nested = true; + } + break; + } + } + *pointer_map_insert (wd->decl_map, (void *) var) = val; + } +} + +/* Find the variables referenced in an expression T. This does not avoid + duplicates because a variable may be read in one context and written in + another. HOW describes the context in which the reference is seen. If + NESTED is true a nested function is being generated and variables in the + original context should not be remapped. */ + +static void +extract_free_variables (tree t, struct wrapper_data *wd, + enum add_variable_type how) +{ + if (t == NULL_TREE) + return; + + enum tree_code code = TREE_CODE (t); + bool is_expr = IS_EXPR_CODE_CLASS (TREE_CODE_CLASS (code)); + + if (is_expr) + extract_free_variables (TREE_TYPE (t), wd, ADD_READ); + + switch (code) + { + case ERROR_MARK: + case IDENTIFIER_NODE: + case INTEGER_CST: + case REAL_CST: + case FIXED_CST: + case STRING_CST: + case BLOCK: + case PLACEHOLDER_EXPR: + case FIELD_DECL: + case VOID_TYPE: + case REAL_TYPE: + /* These do not contain variable references. */ + return; + + case SSA_NAME: + /* Currently we don't see SSA_NAME. */ + extract_free_variables (SSA_NAME_VAR (t), wd, how); + return; + + case LABEL_DECL: + /* This might be a reference to a label outside the Cilk block, + which is an error, or a reference to a label in the Cilk block + that we haven't seen yet. We can't tell. Ignore it. An + invalid use will cause an error later in copy_decl_for_cilk. */ + return; + + case RESULT_DECL: + if (wd->type != CILK_BLOCK_SPAWN) + TREE_ADDRESSABLE (t) = 1; + case VAR_DECL: + case PARM_DECL: + if (!TREE_STATIC (t) && !DECL_EXTERNAL (t)) + add_variable (wd, t, how); + return; + + case NON_LVALUE_EXPR: + case CONVERT_EXPR: + case NOP_EXPR: + extract_free_variables (TREE_OPERAND (t, 0), wd, ADD_READ); + return; + + case INIT_EXPR: + extract_free_variables (TREE_OPERAND (t, 0), wd, ADD_BIND); + extract_free_variables (TREE_OPERAND (t, 1), wd, ADD_READ); + return; + + case MODIFY_EXPR: + case PREDECREMENT_EXPR: + case PREINCREMENT_EXPR: + case POSTDECREMENT_EXPR: + case POSTINCREMENT_EXPR: + /* These write their result. */ + extract_free_variables (TREE_OPERAND (t, 0), wd, ADD_WRITE); + extract_free_variables (TREE_OPERAND (t, 1), wd, ADD_READ); + return; + + case ADDR_EXPR: + /* This might modify its argument, and the value needs to be + passed by reference in any case to preserve identity and + type if is a promoting type. In the case of a nested loop + just notice that we touch the variable. It will already + be addressable, and marking it modified will cause a spurious + warning about writing the control variable. */ + if (wd->type != CILK_BLOCK_SPAWN) + extract_free_variables (TREE_OPERAND (t, 0), wd, ADD_READ); + else + extract_free_variables (TREE_OPERAND (t, 0), wd, ADD_WRITE); + return; + + case ARRAY_REF: + /* Treating ARRAY_REF and BIT_FIELD_REF identically may + mark the array as written but the end result is correct + because the array is passed by pointer anyway. */ + case BIT_FIELD_REF: + /* Propagate the access type to the object part of which + is being accessed here. As for ADDR_EXPR, don't do this + in a nested loop, unless the access is to a fixed index. */ + if (wd->type != CILK_BLOCK_FOR || TREE_CONSTANT (TREE_OPERAND (t, 1))) + extract_free_variables (TREE_OPERAND (t, 0), wd, how); + else + extract_free_variables (TREE_OPERAND (t, 0), wd, ADD_READ); + extract_free_variables (TREE_OPERAND (t, 1), wd, ADD_READ); + extract_free_variables (TREE_OPERAND (t, 2), wd, ADD_READ); + return; + + case TREE_LIST: + extract_free_variables (TREE_PURPOSE (t), wd, ADD_READ); + extract_free_variables (TREE_VALUE (t), wd, ADD_READ); + extract_free_variables (TREE_CHAIN (t), wd, ADD_READ); + return; + + case TREE_VEC: + { + int len = TREE_VEC_LENGTH (t); + int i; + for (i = 0; i < len; i++) + extract_free_variables (TREE_VEC_ELT (t, i), wd, ADD_READ); + return; + } + + case VECTOR_CST: + { + unsigned ii = 0; + for (ii = 0; ii < VECTOR_CST_NELTS (t); ii++) + extract_free_variables (VECTOR_CST_ELT (t, ii), wd, ADD_READ); + break; + } + + case COMPLEX_CST: + extract_free_variables (TREE_REALPART (t), wd, ADD_READ); + extract_free_variables (TREE_IMAGPART (t), wd, ADD_READ); + return; + + case BIND_EXPR: + { + tree decl; + for (decl = BIND_EXPR_VARS (t); decl; decl = TREE_CHAIN (decl)) + { + add_variable (wd, decl, ADD_BIND); + /* A self-referential initialization is no problem because + we already entered the variable into the map as local. */ + extract_free_variables (DECL_INITIAL (decl), wd, ADD_READ); + extract_free_variables (DECL_SIZE (decl), wd, ADD_READ); + extract_free_variables (DECL_SIZE_UNIT (decl), wd, ADD_READ); + } + extract_free_variables (BIND_EXPR_BODY (t), wd, ADD_READ); + return; + } + + case STATEMENT_LIST: + { + tree_stmt_iterator i; + for (i = tsi_start (t); !tsi_end_p (i); tsi_next (&i)) + extract_free_variables (*tsi_stmt_ptr (i), wd, ADD_READ); + return; + } + + case TARGET_EXPR: + { + extract_free_variables (TREE_OPERAND (t, 0), wd, ADD_BIND); + extract_free_variables (TREE_OPERAND (t, 1), wd, ADD_READ); + extract_free_variables (TREE_OPERAND (t, 2), wd, ADD_READ); + if (TREE_OPERAND (t, 3) != TREE_OPERAND (t, 1)) + extract_free_variables (TREE_OPERAND (t, 3), wd, ADD_READ); + return; + } + + case RETURN_EXPR: + if (TREE_NO_WARNING (t)) + { + gcc_assert (errorcount); + return; + } + return; + + case DECL_EXPR: + if (TREE_CODE (DECL_EXPR_DECL (t)) != TYPE_DECL) + extract_free_variables (DECL_EXPR_DECL (t), wd, ADD_BIND); + return; + + case INTEGER_TYPE: + case ENUMERAL_TYPE: + case BOOLEAN_TYPE: + extract_free_variables (TYPE_MIN_VALUE (t), wd, ADD_READ); + extract_free_variables (TYPE_MAX_VALUE (t), wd, ADD_READ); + return; + + case POINTER_TYPE: + extract_free_variables (TREE_TYPE (t), wd, ADD_READ); + break; + + case ARRAY_TYPE: + extract_free_variables (TREE_TYPE (t), wd, ADD_READ); + extract_free_variables (TYPE_DOMAIN (t), wd, ADD_READ); + return; + + case RECORD_TYPE: + extract_free_variables (TYPE_FIELDS (t), wd, ADD_READ); + return; + + case METHOD_TYPE: + extract_free_variables (TYPE_ARG_TYPES (t), wd, ADD_READ); + extract_free_variables (TYPE_METHOD_BASETYPE (t), wd, ADD_READ); + return; + + case AGGR_INIT_EXPR: + case CALL_EXPR: + { + int len = 0; + int ii = 0; + if (TREE_CODE (TREE_OPERAND (t, 0)) == INTEGER_CST) + { + len = TREE_INT_CST_LOW (TREE_OPERAND (t, 0)); + + for (ii = 0; ii < len; ii++) + extract_free_variables (TREE_OPERAND (t, ii), wd, ADD_READ); + extract_free_variables (TREE_TYPE (t), wd, ADD_READ); + } + break; + } + + default: + if (is_expr) + { + 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. */ + for (i = 0; i < len; ++i) + extract_free_variables (TREE_OPERAND (t, i), wd, ADD_READ); + } + } +} + + +/* Add appropriate frames needed for a Cilk spawned function call, FNDECL. + Returns the __cilkrts_stack_frame * variable. */ + +tree +insert_cilk_frame (tree fndecl) +{ + tree addr, body, enter, out, orig_body; + location_t loc = EXPR_LOCATION (fndecl); + + if (!cfun || cfun->decl != fndecl) + push_cfun (DECL_STRUCT_FUNCTION (fndecl)); + + tree decl = cfun->cilk_frame_decl; + if (!decl) + { + tree *saved_tree = &DECL_SAVED_TREE (fndecl); + decl = make_cilk_frame (fndecl); + add_local_decl (cfun, decl); + + addr = build1 (ADDR_EXPR, cilk_frame_ptr_type_decl, decl); + enter = build_call_expr (cilk_enter_fndecl, 1, addr); + out = create_cilk_function_exit (cfun->cilk_frame_decl, false, true); + + /* The new body will be: + __cilkrts_enter_frame_1 (&sf); + try { + orig_body; + } + finally { + __cilkrts_pop_frame (&sf); + __cilkrts_leave_frame (&sf); + } */ + + body = alloc_stmt_list (); + orig_body = *saved_tree; + + if (TREE_CODE (orig_body) == BIND_EXPR) + orig_body = BIND_EXPR_BODY (orig_body); + + append_to_statement_list (enter, &body); + append_to_statement_list (build_stmt (loc, TRY_FINALLY_EXPR, orig_body, + out), &body); + if (TREE_CODE (*saved_tree) == BIND_EXPR) + BIND_EXPR_BODY (*saved_tree) = body; + else + *saved_tree = body; + } + return decl; +} + +/* Wraps CALL, a CALL_EXPR, into a CILK_SPAWN_STMT tree and returns it. */ + +tree +build_cilk_spawn (location_t loc, tree call) +{ + if (!cilk_set_spawn_marker (loc, call)) + return error_mark_node; + tree spawn_stmt = build1 (CILK_SPAWN_STMT, TREE_TYPE (call), call); + TREE_SIDE_EFFECTS (spawn_stmt) = 1; + return spawn_stmt; +} + +/* Returns a tree of type CILK_SYNC_STMT. */ + +tree +build_cilk_sync (void) +{ + tree sync = build0 (CILK_SYNC_STMT, void_type_node); + TREE_SIDE_EFFECTS (sync) = 1; + return sync; +} diff --git a/gcc/c/ChangeLog b/gcc/c/ChangeLog index 707c5d225a3..ed60988a150 100644 --- a/gcc/c/ChangeLog +++ b/gcc/c/ChangeLog @@ -1,3 +1,21 @@ +2013-10-29 Balaji V. Iyer <balaji.v.iyer@intel.com> + + * c-decl.c (finish_function): Added a call for insert_cilk_frame when + a spawning function is found. + * c-objc-common.h (LANG_HOOKS_CILKPLUS_GIMPLIFY_SPAWN): New #define. + (LANG_HOOKS_CILKPLUS_FRAME_CLEANUP): Likewise. + (LANG_HOOKS_CILKPLUS_DETECT_SPAWN_AND_UNWRAP): Likewise. + * c-parser.c (c_parser_statement_after_labels): Added RID_CILK_SYNC + case. + (c_parser_postfix_expression): Added RID_CILK_SPAWN case. + * c-typeck.c (build_compound_expr): Reject _Cilk_spawn in a comma + expr. + (c_finish_return): Added a check to reject _Cilk_spawn in return + expression. + (build_cilk_spawn): New function. + (build_cilk_sync): Likewise. + * Makefile.in (c-decl.o): Added cilk.h in dependency list. + 2013-10-27 Tobias Burnus <burnus@net-b.de> PR other/33426 diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c index 0554e72703f..64718c55294 100644 --- a/gcc/c/c-decl.c +++ b/gcc/c/c-decl.c @@ -56,6 +56,7 @@ along with GCC; see the file COPYING3. If not see #include "pointer-set.h" #include "plugin.h" #include "c-family/c-ada-spec.h" +#include "cilk.h" /* In grokdeclarator, distinguish syntactic contexts of declarators. */ enum decl_context @@ -8447,6 +8448,12 @@ finish_function (void) /* Tie off the statement tree for this function. */ DECL_SAVED_TREE (fndecl) = pop_stmt_list (DECL_SAVED_TREE (fndecl)); + /* If the function has _Cilk_spawn in front of a function call inside it + i.e. it is a spawning function, then add the appropriate Cilk plus + functions inside. */ + if (fn_contains_cilk_spawn_p (cfun)) + cfun->cilk_frame_decl = insert_cilk_frame (fndecl); + finish_fname_decls (); /* Complain if there's just no return statement. */ diff --git a/gcc/c/c-objc-common.h b/gcc/c/c-objc-common.h index e1448248cd6..6ae7b3e0fb7 100644 --- a/gcc/c/c-objc-common.h +++ b/gcc/c/c-objc-common.h @@ -105,4 +105,13 @@ along with GCC; see the file COPYING3. If not see #undef LANG_HOOKS_TREE_INLINING_VAR_MOD_TYPE_P #define LANG_HOOKS_TREE_INLINING_VAR_MOD_TYPE_P c_vla_unspec_p +#undef LANG_HOOKS_CILKPLUS_GIMPLIFY_SPAWN +#define LANG_HOOKS_CILKPLUS_GIMPLIFY_SPAWN gimplify_cilk_spawn + +#undef LANG_HOOKS_CILKPLUS_FRAME_CLEANUP +#define LANG_HOOKS_CILKPLUS_FRAME_CLEANUP c_cilk_install_body_w_frame_cleanup + +#undef LANG_HOOKS_CILKPLUS_DETECT_SPAWN_AND_UNWRAP +#define LANG_HOOKS_CILKPLUS_DETECT_SPAWN_AND_UNWRAP \ + cilk_detect_spawn_and_unwrap #endif /* GCC_C_OBJC_COMMON */ diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c index 9ccae3bccc6..a8f4774136d 100644 --- a/gcc/c/c-parser.c +++ b/gcc/c/c-parser.c @@ -4587,6 +4587,14 @@ c_parser_statement_after_labels (c_parser *parser) case RID_FOR: c_parser_for_statement (parser, false); break; + case RID_CILK_SYNC: + c_parser_consume_token (parser); + c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>"); + if (!flag_enable_cilkplus) + error_at (loc, "-fcilkplus must be enabled to use %<_Cilk_sync%>"); + else + add_stmt (build_cilk_sync ()); + break; case RID_GOTO: c_parser_consume_token (parser); if (c_parser_next_token_is (parser, CPP_NAME)) @@ -7174,6 +7182,30 @@ c_parser_postfix_expression (c_parser *parser) case RID_GENERIC: expr = c_parser_generic_selection (parser); break; + case RID_CILK_SPAWN: + c_parser_consume_token (parser); + if (!flag_enable_cilkplus) + { + error_at (loc, "-fcilkplus must be enabled to use " + "%<_Cilk_spawn%>"); + expr = c_parser_postfix_expression (parser); + expr.value = error_mark_node; + } + if (c_parser_peek_token (parser)->keyword == RID_CILK_SPAWN) + { + error_at (loc, "consecutive %<_Cilk_spawn%> keywords " + "are not permitted"); + /* Now flush out all the _Cilk_spawns. */ + while (c_parser_peek_token (parser)->keyword == RID_CILK_SPAWN) + c_parser_consume_token (parser); + expr = c_parser_postfix_expression (parser); + } + else + { + expr = c_parser_postfix_expression (parser); + expr.value = build_cilk_spawn (loc, expr.value); + } + break; default: c_parser_error (parser, "expected expression"); expr.value = error_mark_node; diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c index 1d83137cd3f..1034cee1fcc 100644 --- a/gcc/c/c-typeck.c +++ b/gcc/c/c-typeck.c @@ -4387,6 +4387,14 @@ build_compound_expr (location_t loc, tree expr1, tree expr2) tree eptype = NULL_TREE; tree ret; + if (flag_enable_cilkplus + && (TREE_CODE (expr1) == CILK_SPAWN_STMT + || TREE_CODE (expr2) == CILK_SPAWN_STMT)) + { + error_at (loc, + "spawned function call cannot be part of a comma expression"); + return error_mark_node; + } expr1_int_operands = EXPR_INT_CONST_OPERANDS (expr1); if (expr1_int_operands) expr1 = remove_c_maybe_const_expr (expr1); @@ -8694,6 +8702,12 @@ c_finish_return (location_t loc, tree retval, tree origtype) return error_mark_node; } } + if (flag_enable_cilkplus && retval && TREE_CODE (retval) == CILK_SPAWN_STMT) + { + error_at (loc, "use of %<_Cilk_spawn%> in a return statement is not " + "allowed"); + return error_mark_node; + } if (retval) { tree semantic_type = NULL_TREE; diff --git a/gcc/cilk-builtins.def b/gcc/cilk-builtins.def new file mode 100644 index 00000000000..8634194d722 --- /dev/null +++ b/gcc/cilk-builtins.def @@ -0,0 +1,33 @@ +/* This file contains the definitions and documentation for the + Cilk Plus builtins used in the GNU compiler. + Copyright (C) 2013 Free Software Foundation, Inc. + + Contributed by Balaji V. Iyer <balaji.v.iyer@intel.com> + Intel Corporation. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +DEF_CILK_BUILTIN_STUB (BUILT_IN_CILK_ENTER_FRAME, "__cilkrts_enter_frame_1") +DEF_CILK_BUILTIN_STUB (BUILT_IN_CILK_ENTER_FRAME_FAST, + "__cilkrts_enter_frame_fast_1") +DEF_CILK_BUILTIN_STUB (BUILT_IN_CILK_DETACH, "__cilkrts_detach") +DEF_CILK_BUILTIN_STUB (BUILT_IN_CILK_RETHROW, "__cilkrts_rethrow") +DEF_CILK_BUILTIN_STUB (BUILT_IN_CILK_SYNCHED, "__cilkrts_synched") +DEF_CILK_BUILTIN_STUB (BUILT_IN_CILK_SYNC, "__cilkrts_sync") +DEF_CILK_BUILTIN_STUB (BUILT_IN_CILK_LEAVE_FRAME, "__cilkrts_leave_frame") +DEF_CILK_BUILTIN_STUB (BUILT_IN_CILK_POP_FRAME, "__cilkrts_pop_frame") +DEF_CILK_BUILTIN_STUB (BUILT_IN_CILK_SAVE_FP, "__cilkrts_save_fp_ctrl_state") diff --git a/gcc/cilk-common.c b/gcc/cilk-common.c new file mode 100644 index 00000000000..ca178c00cd3 --- /dev/null +++ b/gcc/cilk-common.c @@ -0,0 +1,484 @@ +/* This file is part of the Intel(R) Cilk(TM) Plus support + This file contains the CilkPlus Intrinsics + Copyright (C) 2013 Free Software Foundation, Inc. + Contributed by Balaji V. Iyer <balaji.v.iyer@intel.com>, + Intel Corporation + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tree.h" +#include "langhooks.h" +#include "expr.h" +#include "optabs.h" +#include "recog.h" +#include "tree-iterator.h" +#include "gimple.h" +#include "cilk.h" + +/* This structure holds all the important fields of the internal structures, + internal built-in functions, and Cilk-specific data types. Explanation of + all the these fielsd are given in cilk.h. */ +tree cilk_trees[(int) CILK_TI_MAX]; + +/* Returns the value in structure FRAME pointed by the FIELD_NUMBER + (e.g. X.y). + FIELD_NUMBER is an index to the structure FRAME_PTR. For details + about these fields, refer to cilk_trees structure in cilk.h and + cilk_init_builtins function in this file. Returns a TREE that is the type + of the field represented by FIELD_NUMBER. If VOLATIL parameter is set + to true then the returning field is set as volatile. */ + +tree +cilk_dot (tree frame, int field_number, bool volatil) +{ + tree field = cilk_trees[field_number]; + field = fold_build3 (COMPONENT_REF, TREE_TYPE (field), frame, field, + NULL_TREE); + TREE_THIS_VOLATILE (field) = volatil; + return field; +} + +/* Returns the address of a field in FRAME_PTR, pointed by FIELD_NUMBER. + (e.g. (&X)->y). Please see cilk_dot function for explanation of the + FIELD_NUMBER. Returns a tree that is the type of the field represented + by FIELD_NUMBER. If VOLATIL parameter is set to true then the returning + field is set as volatile. */ + +tree +cilk_arrow (tree frame_ptr, int field_number, bool volatil) +{ + return cilk_dot (fold_build1 (INDIRECT_REF, + TREE_TYPE (TREE_TYPE (frame_ptr)), frame_ptr), + field_number, volatil); +} + + +/* This function will add FIELD of type TYPE to a defined built-in + structure. *NAME is the name of the field to be added. */ + +static tree +add_field (const char *name, tree type, tree fields) +{ + tree t = get_identifier (name); + tree field = build_decl (BUILTINS_LOCATION, FIELD_DECL, t, type); + TREE_CHAIN (field) = fields; + return field; +} + +/* This function will define a built-in function of NAME, of type FNTYPE and + register it under the built-in function code CODE. If PUBLISH is set then + the declaration is pushed into the declaration list. CODE is the index + to the cilk_trees array. *NAME is the name of the function to be added. */ + +static tree +install_builtin (const char *name, tree fntype, enum built_in_function code, + bool publish) +{ + tree fndecl = build_fn_decl (name, fntype); + DECL_BUILT_IN_CLASS (fndecl) = BUILT_IN_NORMAL; + DECL_FUNCTION_CODE (fndecl) = code; + if (publish) + { + tree t = lang_hooks.decls.pushdecl (fndecl); + if (t) + fndecl = t; + } + set_builtin_decl (code, fndecl, true); + return fndecl; +} + +/* Creates and initializes all the built-in Cilk keywords functions and three + structures: __cilkrts_stack_frame, __cilkrts_pedigree and __cilkrts_worker. + Detailed information about __cilkrts_stack_frame and + __cilkrts_worker structures are given in libcilkrts/include/internal/abi.h. + __cilkrts_pedigree is described in libcilkrts/include/cilk/common.h. */ + +void +cilk_init_builtins (void) +{ + /* Now build the following __cilkrts_pedigree struct: + struct __cilkrts_pedigree { + uint64_t rank; + struct __cilkrts_pedigree *parent; + } */ + + tree pedigree_type = lang_hooks.types.make_type (RECORD_TYPE); + tree pedigree_ptr = build_pointer_type (pedigree_type); + tree field = add_field ("rank", uint64_type_node, NULL_TREE); + cilk_trees[CILK_TI_PEDIGREE_RANK] = field; + field = add_field ("parent", pedigree_ptr, field); + cilk_trees[CILK_TI_PEDIGREE_PARENT] = field; + finish_builtin_struct (pedigree_type, "__cilkrts_pedigree_GCC", field, + NULL_TREE); + lang_hooks.types.register_builtin_type (pedigree_type, + "__cilkrts_pedigree_t"); + cilk_pedigree_type_decl = pedigree_type; + + /* Build the Cilk Stack Frame: + struct __cilkrts_stack_frame { + uint32_t flags; + uint32_t size; + struct __cilkrts_stack_frame *call_parent; + __cilkrts_worker *worker; + void *except_data; + void *ctx[4]; + uint32_t mxcsr; + uint16_t fpcsr; + uint16_t reserved; + __cilkrts_pedigree pedigree; + }; */ + + tree frame = lang_hooks.types.make_type (RECORD_TYPE); + tree frame_ptr = build_pointer_type (frame); + tree worker_type = lang_hooks.types.make_type (RECORD_TYPE); + tree worker_ptr = build_pointer_type (worker_type); + tree s_type_node = build_int_cst (size_type_node, 4); + + tree flags = add_field ("flags", uint32_type_node, NULL_TREE); + tree size = add_field ("size", uint32_type_node, flags); + tree parent = add_field ("call_parent", frame_ptr, size); + tree worker = add_field ("worker", worker_ptr, parent); + tree except = add_field ("except_data", frame_ptr, worker); + tree context = add_field ("ctx", + build_array_type (ptr_type_node, + build_index_type (s_type_node)), + except); + tree mxcsr = add_field ("mxcsr", uint32_type_node, context); + tree fpcsr = add_field ("fpcsr", uint16_type_node, mxcsr); + tree reserved = add_field ("reserved", uint16_type_node, fpcsr); + tree pedigree = add_field ("pedigree", pedigree_type, reserved); + + /* Now add them to a common structure whose fields are #defined to something + that is used at a later stage. */ + cilk_trees[CILK_TI_FRAME_FLAGS] = flags; + cilk_trees[CILK_TI_FRAME_PARENT] = parent; + cilk_trees[CILK_TI_FRAME_WORKER] = worker; + cilk_trees[CILK_TI_FRAME_EXCEPTION] = except; + cilk_trees[CILK_TI_FRAME_CONTEXT] = context; + /* We don't care about reserved, so no need to store it in cilk_trees. */ + cilk_trees[CILK_TI_FRAME_PEDIGREE] = pedigree; + TREE_ADDRESSABLE (frame) = 1; + + finish_builtin_struct (frame, "__cilkrts_st_frame_GCC", pedigree, NULL_TREE); + cilk_frame_type_decl = frame; + lang_hooks.types.register_builtin_type (frame, "__cilkrts_frame_t"); + + cilk_frame_ptr_type_decl = build_qualified_type (frame_ptr, + TYPE_QUAL_VOLATILE); + /* Now let's do the following worker struct: + + struct __cilkrts_worker { + __cilkrts_stack_frame *volatile *volatile tail; + __cilkrts_stack_frame *volatile *volatile head; + __cilkrts_stack_frame *volatile *volatile exc; + __cilkrts_stack_frame *volatile *volatile protected_tail; + __cilkrts_stack_frame *volatile *ltq_limit; + int32_t self; + global_state_t *g; + local_state *l; + cilkred_map *reducer_map; + __cilkrts_stack_frame *current_stack_frame; + void *reserved; + __cilkrts_worker_sysdep_state *sysdep; + __cilkrts_pedigree pedigree; + } */ + + tree fptr_volatil_type = build_qualified_type (frame_ptr, TYPE_QUAL_VOLATILE); + tree fptr_volatile_ptr = build_pointer_type (fptr_volatil_type); + tree fptr_vol_ptr_vol = build_qualified_type (fptr_volatile_ptr, + TYPE_QUAL_VOLATILE); + tree g = lang_hooks.types.make_type (RECORD_TYPE); + finish_builtin_struct (g, "__cilkrts_global_state", NULL_TREE, NULL_TREE); + tree l = lang_hooks.types.make_type (RECORD_TYPE); + finish_builtin_struct (l, "__cilkrts_local_state", NULL_TREE, NULL_TREE); + tree sysdep_t = lang_hooks.types.make_type (RECORD_TYPE); + finish_builtin_struct (sysdep_t, "__cilkrts_worker_sysdep_state", NULL_TREE, + NULL_TREE); + + field = add_field ("tail", fptr_vol_ptr_vol, NULL_TREE); + cilk_trees[CILK_TI_WORKER_TAIL] = field; + field = add_field ("head", fptr_vol_ptr_vol, field); + field = add_field ("exc", fptr_vol_ptr_vol, field); + field = add_field ("protected_tail", fptr_vol_ptr_vol, field); + field = add_field ("ltq_limit", fptr_volatile_ptr, field); + field = add_field ("self", integer_type_node, field); + field = add_field ("g", build_pointer_type (g), field); + field = add_field ("l", build_pointer_type (g), field); + field = add_field ("reducer_map", ptr_type_node, field); + field = add_field ("current_stack_frame", frame_ptr, field); + cilk_trees[CILK_TI_WORKER_CUR] = field; + field = add_field ("saved_protected_tail", fptr_volatile_ptr, field); + field = add_field ("sysdep", build_pointer_type (sysdep_t), field); + field = add_field ("pedigree", pedigree_type, field); + cilk_trees[CILK_TI_WORKER_PEDIGREE] = field; + finish_builtin_struct (worker_type, "__cilkrts_worker_GCC", field, + NULL_TREE); + + tree fptr_arglist = tree_cons (NULL_TREE, frame_ptr, void_list_node); + tree fptr_fun = build_function_type (void_type_node, fptr_arglist); + + /* void __cilkrts_enter_frame_1 (__cilkrts_stack_frame *); */ + cilk_enter_fndecl = install_builtin ("__cilkrts_enter_frame_1", fptr_fun, + BUILT_IN_CILK_ENTER_FRAME, false); + + /* void __cilkrts_enter_frame_fast_1 (__cilkrts_stack_frame *); */ + cilk_enter_fast_fndecl = + install_builtin ("__cilkrts_enter_frame_fast_1", fptr_fun, + BUILT_IN_CILK_ENTER_FRAME_FAST, false); + + /* void __cilkrts_pop_frame (__cilkrts_stack_frame *); */ + cilk_pop_fndecl = install_builtin ("__cilkrts_pop_frame", fptr_fun, + BUILT_IN_CILK_POP_FRAME, false); + + /* void __cilkrts_leave_frame (__cilkrts_stack_frame *); */ + cilk_leave_fndecl = install_builtin ("__cilkrts_leave_frame", fptr_fun, + BUILT_IN_CILK_LEAVE_FRAME, false); + + /* void __cilkrts_sync (__cilkrts_stack_frame *); */ + cilk_sync_fndecl = install_builtin ("__cilkrts_sync", fptr_fun, + BUILT_IN_CILK_SYNC, false); + + /* void __cilkrts_detach (__cilkrts_stack_frame *); */ + cilk_detach_fndecl = install_builtin ("__cilkrts_detach", fptr_fun, + BUILT_IN_CILK_DETACH, false); + + /* __cilkrts_rethrow (struct stack_frame *); */ + cilk_rethrow_fndecl = install_builtin ("__cilkrts_rethrow", fptr_fun, + BUILT_IN_CILK_RETHROW, false); + + /* __cilkrts_save_fp_ctrl_state (__cilkrts_stack_frame *); */ + cilk_save_fp_fndecl = install_builtin ("__cilkrts_save_fp_ctrl_state", + fptr_fun, BUILT_IN_CILK_SAVE_FP, + false); +} + +/* Get the appropriate frame arguments for CALL that is of type CALL_EXPR. */ + +static tree +get_frame_arg (tree call) +{ + tree arg, argtype; + + gcc_assert (call_expr_nargs (call) >= 1); + + arg = CALL_EXPR_ARG (call, 0); + argtype = TREE_TYPE (arg); + gcc_assert (TREE_CODE (argtype) == POINTER_TYPE); + + argtype = TREE_TYPE (argtype); + + gcc_assert (!lang_hooks.types_compatible_p + || lang_hooks.types_compatible_p (argtype, cilk_frame_type_decl)); + + /* If it is passed in as an address, then just use the value directly + since the function is inlined. */ + if (TREE_CODE (arg) == INDIRECT_REF || TREE_CODE (arg) == ADDR_EXPR) + return TREE_OPERAND (arg, 0); + return arg; +} + +/* Expands the __cilkrts_pop_frame function call stored in EXP. */ + +void +expand_builtin_cilk_pop_frame (tree exp) +{ + tree frame = get_frame_arg (exp); + tree parent = cilk_dot (frame, CILK_TI_FRAME_PARENT, 0); + + tree clear_parent = build2 (MODIFY_EXPR, void_type_node, parent, + build_int_cst (TREE_TYPE (parent), 0)); + expand_expr (clear_parent, const0_rtx, VOIDmode, EXPAND_NORMAL); + + /* During LTO, the is_cilk_function flag gets cleared. + If __cilkrts_pop_frame is called, then this definitely must be a + cilk function. */ + if (cfun) + cfun->is_cilk_function = 1; +} + +/* Expands the cilk_detach function call stored in EXP. */ + +void +expand_builtin_cilk_detach (tree exp) +{ + rtx insn; + tree fptr = get_frame_arg (exp); + + if (fptr == NULL_TREE) + return; + + tree parent = cilk_dot (fptr, CILK_TI_FRAME_PARENT, 0); + tree worker = cilk_dot (fptr, CILK_TI_FRAME_WORKER, 0); + tree tail = cilk_dot (worker, CILK_TI_WORKER_TAIL, 1); + + rtx wreg = expand_expr (worker, NULL_RTX, Pmode, EXPAND_NORMAL); + if (GET_CODE (wreg) != REG) + wreg = copy_to_reg (wreg); + rtx preg = expand_expr (parent, NULL_RTX, Pmode, EXPAND_NORMAL); + + /* TMP <- WORKER.TAIL + *TMP <- PARENT + TMP <- TMP + 1 + WORKER.TAIL <- TMP */ + + HOST_WIDE_INT worker_tail_offset = + tree_low_cst (DECL_FIELD_OFFSET (cilk_trees[CILK_TI_WORKER_TAIL]), 0) + + tree_low_cst (DECL_FIELD_BIT_OFFSET (cilk_trees[CILK_TI_WORKER_TAIL]), 0) / + BITS_PER_UNIT; + rtx tmem0 = gen_rtx_MEM (Pmode, + plus_constant (Pmode, wreg, worker_tail_offset)); + set_mem_attributes (tmem0, tail, 0); + MEM_NOTRAP_P (tmem0) = 1; + gcc_assert (MEM_VOLATILE_P (tmem0)); + rtx treg = copy_to_mode_reg (Pmode, tmem0); + rtx tmem1 = gen_rtx_MEM (Pmode, treg); + set_mem_attributes (tmem1, TREE_TYPE (TREE_TYPE (tail)), 0); + MEM_NOTRAP_P (tmem1) = 1; + emit_move_insn (tmem1, preg); + emit_move_insn (treg, plus_constant (Pmode, treg, GET_MODE_SIZE (Pmode))); + + /* There is a release barrier (st8.rel, membar #StoreStore, + sfence, lwsync, etc.) between the two stores. On x86 + normal volatile stores have proper semantics; the sfence + would only be needed for nontemporal stores (which we + could generate using the storent optab, for no benefit + in this case). + + The predicate may return false even for a REG if this is + the limited release operation that only stores 0. */ + enum insn_code icode = direct_optab_handler (sync_lock_release_optab, Pmode); + if (icode != CODE_FOR_nothing + && insn_data[icode].operand[1].predicate (treg, Pmode) + && (insn = GEN_FCN (icode) (tmem0, treg)) != NULL_RTX) + emit_insn (insn); + else + emit_move_insn (tmem0, treg); + + /* The memory barrier inserted above should not prevent + the load of flags from being moved before the stores, + but in practice it does because it is implemented with + unspec_volatile. In-order RISC machines should + explicitly load flags earlier. */ + + tree flags = cilk_dot (fptr, CILK_TI_FRAME_FLAGS, 0); + expand_expr (build2 (MODIFY_EXPR, void_type_node, flags, + build2 (BIT_IOR_EXPR, TREE_TYPE (flags), flags, + build_int_cst (TREE_TYPE (flags), + CILK_FRAME_DETACHED))), + const0_rtx, VOIDmode, EXPAND_NORMAL); +} + +/* Returns a setjmp CALL_EXPR with FRAME->context as its parameter. */ + +tree +cilk_call_setjmp (tree frame) +{ + tree c = cilk_dot (frame, CILK_TI_FRAME_CONTEXT, false); + c = build1 (ADDR_EXPR, build_pointer_type (ptr_type_node), c); + return build_call_expr (builtin_decl_implicit (BUILT_IN_SETJMP), 1, c); +} + +/* This function will expand the _Cilk_sync keyword. */ + +static tree +expand_cilk_sync (void) +{ + tree frame = cfun->cilk_frame_decl; + + /* Cilk_sync is converted to the following code: + + sf.pedigree = sf.worker->pedigree; + if (frame.flags & CILK_FRAME_UNSYNCHED) + { + __cilkrts_save_fp_state (&sf); + if (!builtin_setjmp (sf.ctx) + __cilkrts_sync (&sf); + else + if (sf.flags & CILK_FRAME_EXCEPTING) + __cilkrts_rethrow (&sf); + } + sf.worker->pedigree.rank = sf.worker->pedigree.rank + 1; */ + + tree flags = cilk_dot (frame, CILK_TI_FRAME_FLAGS, false); + + tree unsynched = fold_build2 (BIT_AND_EXPR, TREE_TYPE (flags), flags, + build_int_cst (TREE_TYPE (flags), + CILK_FRAME_UNSYNCHED)); + + unsynched = fold_build2 (NE_EXPR, TREE_TYPE (unsynched), unsynched, + build_int_cst (TREE_TYPE (unsynched), 0)); + + tree frame_addr = build1 (ADDR_EXPR, cilk_frame_ptr_type_decl, frame); + + /* Check if exception (0x10) bit is set in the sf->flags. */ + tree except_flag = fold_build2 (BIT_AND_EXPR, TREE_TYPE (flags), flags, + build_int_cst (TREE_TYPE (flags), + CILK_FRAME_EXCEPTING)); + except_flag = fold_build2 (NE_EXPR, TREE_TYPE (except_flag), except_flag, + build_int_cst (TREE_TYPE (except_flag), 0)); + + /* If the exception flag is set then call the __cilkrts_rethrow (&sf). */ + tree except_cond = fold_build3 (COND_EXPR, void_type_node, except_flag, + build_call_expr (cilk_rethrow_fndecl, 1, + frame_addr), + build_empty_stmt (EXPR_LOCATION (unsynched))); + + tree sync_expr = build_call_expr (cilk_sync_fndecl, 1, frame_addr); + tree setjmp_expr = cilk_call_setjmp (frame); + setjmp_expr = fold_build2 (EQ_EXPR, TREE_TYPE (setjmp_expr), setjmp_expr, + build_int_cst (TREE_TYPE (setjmp_expr), 0)); + + setjmp_expr = fold_build3 (COND_EXPR, void_type_node, setjmp_expr, + sync_expr, except_cond); + tree sync_list = alloc_stmt_list (); + append_to_statement_list (build_call_expr (cilk_save_fp_fndecl, 1, + frame_addr), &sync_list); + append_to_statement_list (setjmp_expr, &sync_list); + tree sync = fold_build3 (COND_EXPR, void_type_node, unsynched, sync_list, + build_empty_stmt (EXPR_LOCATION (unsynched))); + tree parent_pedigree = cilk_dot (frame, CILK_TI_FRAME_PEDIGREE, false); + tree worker = cilk_dot (frame, CILK_TI_FRAME_WORKER, false); + tree worker_pedigree = cilk_arrow (worker, CILK_TI_WORKER_PEDIGREE, false); + tree assign_pedigree = fold_build2 (MODIFY_EXPR, void_type_node, + parent_pedigree, worker_pedigree); + tree w_ped_rank = cilk_dot (unshare_expr (worker_pedigree), + CILK_TI_PEDIGREE_RANK, false); + tree incr_ped_rank = fold_build2 (PLUS_EXPR, TREE_TYPE (w_ped_rank), + w_ped_rank, + build_one_cst (TREE_TYPE (w_ped_rank))); + incr_ped_rank = fold_build2 (MODIFY_EXPR, void_type_node, w_ped_rank, + incr_ped_rank); + tree ret_sync_exp = alloc_stmt_list (); + append_to_statement_list (assign_pedigree, &ret_sync_exp); + append_to_statement_list (sync, &ret_sync_exp); + append_to_statement_list (incr_ped_rank, &ret_sync_exp); + return ret_sync_exp; +} + +/* Gimplifies the cilk_sync expression passed in *EXPR_P. Returns GS_ALL_DONE + when finished. */ + +void +gimplify_cilk_sync (tree *expr_p, gimple_seq *pre_p) +{ + tree sync_expr = expand_cilk_sync (); + *expr_p = NULL_TREE; + gimplify_and_add (sync_expr, pre_p); +} diff --git a/gcc/cilk.h b/gcc/cilk.h new file mode 100644 index 00000000000..99b4d782af4 --- /dev/null +++ b/gcc/cilk.h @@ -0,0 +1,102 @@ +/* This file is part of the Intel(R) Cilk(TM) Plus support + This file contains Cilk Support files. + Copyright (C) 2013 Free Software Foundation, Inc. + Contributed by Balaji V. Iyer <balaji.v.iyer@intel.com>, + Intel Corporation + + This file is part of GCC. + + GCC is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GCC is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GCC; see the file COPYING3. If not see + <http://www.gnu.org/licenses/>. */ + +#ifndef GCC_CILK_H +#define GCC_CILK_H + +/* Frame status bits known to compiler. */ +#define CILK_FRAME_UNSYNCHED 0x02 +#define CILK_FRAME_DETACHED 0x04 +#define CILK_FRAME_EXCEPTING 0x10 +#define CILK_FRAME_VERSION (1 << 24) + +enum cilk_tree_index { +/* All the built-in functions for Cilk keywords. */ + CILK_TI_F_WORKER = 0, /* __cilkrts_get_worker (). */ + CILK_TI_F_SYNC, /* __cilkrts_sync (). */ + CILK_TI_F_DETACH, /* __cilkrts_detach (...). */ + CILK_TI_F_ENTER, /* __cilkrts_enter_frame (...). */ + CILK_TI_F_ENTER_FAST, /* __cilkrts_enter_frame_fast (.). */ + CILK_TI_F_LEAVE, /* __cilkrts_leave_frame (...). */ + CILK_TI_F_POP, /* __cilkrts_pop_frame (...). */ + CILK_TI_F_RETHROW, /* __cilkrts_rethrow (...). */ + CILK_TI_F_SAVE_FP, /* __cilkrts_save_fp_ctrl_state (...). */ + /* __cilkrts_stack_frame struct fields. */ + CILK_TI_FRAME_FLAGS, /* stack_frame->flags. */ + CILK_TI_FRAME_PARENT, /* stack_frame->parent. */ + CILK_TI_FRAME_WORKER, /* stack_frame->worker. */ + CILK_TI_FRAME_EXCEPTION, /* stack_frame->except_data. */ + CILK_TI_FRAME_CONTEXT, /* stack_frame->context[4]. */ + CILK_TI_FRAME_PEDIGREE, /* stack_frame->pedigree. */ + + /* __cilkrts_worker struct fields. */ + CILK_TI_WORKER_CUR, /* worker->current_stack_frame. */ + CILK_TI_WORKER_TAIL, /* worker->tail. */ + CILK_TI_WORKER_PEDIGREE, /* worker->pedigree. */ + + /* __cilkrts_pedigree struct fields. */ + CILK_TI_PEDIGREE_RANK, /* pedigree->rank. */ + CILK_TI_PEDIGREE_PARENT, /* pedigree->parent. */ + + /* Types. */ + CILK_TI_FRAME_TYPE, /* struct __cilkrts_stack_frame. */ + CILK_TI_FRAME_PTR, /* __cilkrts_stack_frame *. */ + CILK_TI_WORKER_TYPE, /* struct __cilkrts_worker. */ + CILK_TI_PEDIGREE_TYPE, /* struct __cilkrts_pedigree. */ + CILK_TI_MAX +}; + +extern GTY (()) tree cilk_trees[CILK_TI_MAX]; + +#define cilk_worker_fndecl cilk_trees[CILK_TI_F_WORKER] +#define cilk_sync_fndecl cilk_trees[CILK_TI_F_SYNC] +#define cilk_synched_fndecl cilk_trees[CILK_TI_F_SYNCED] +#define cilk_detach_fndecl cilk_trees[CILK_TI_F_DETACH] +#define cilk_enter_fndecl cilk_trees[CILK_TI_F_ENTER] +#define cilk_enter_fast_fndecl cilk_trees[CILK_TI_F_ENTER_FAST] +#define cilk_leave_fndecl cilk_trees[CILK_TI_F_LEAVE] +#define cilk_rethrow_fndecl cilk_trees[CILK_TI_F_RETHROW] +#define cilk_pop_fndecl cilk_trees[CILK_TI_F_POP] +#define cilk_save_fp_fndecl cilk_trees[CILK_TI_F_SAVE_FP] + +#define cilk_worker_type_fndecl cilk_trees[CILK_TI_WORKER_TYPE] +#define cilk_frame_type_decl cilk_trees[CILK_TI_FRAME_TYPE] +#define cilk_frame_ptr_type_decl cilk_trees[CILK_TI_FRAME_PTR] +#define cilk_pedigree_type_decl cilk_trees[CILK_TI_PEDIGREE_TYPE] + +extern void expand_builtin_cilk_detach (tree); +extern void expand_builtin_cilk_pop_frame (tree); +extern tree cilk_arrow (tree, int, bool); +extern tree cilk_dot (tree, int, bool); +extern void cilk_init_builtins (void); +extern void gimplify_cilk_sync (tree *, gimple_seq *); +extern tree cilk_call_setjmp (tree); +/* Returns true if Cilk Plus is enabled and if F->cilk_frame_decl is not + NULL_TREE. */ + +inline bool +fn_contains_cilk_spawn_p (function *f) +{ + return (flag_enable_cilkplus + && (f->calls_cilk_spawn || f->cilk_frame_decl != NULL_TREE)); +} +#endif diff --git a/gcc/cppbuiltin.c b/gcc/cppbuiltin.c index 2ceccdcce2b..cd74e2349c4 100644 --- a/gcc/cppbuiltin.c +++ b/gcc/cppbuiltin.c @@ -105,6 +105,8 @@ define_builtin_macros_for_compilation_flags (cpp_reader *pfile) cpp_define_formatted (pfile, "__FINITE_MATH_ONLY__=%d", flag_finite_math_only); + if (flag_enable_cilkplus) + cpp_define (pfile, "__cilk=200"); } diff --git a/gcc/doc/generic.texi b/gcc/doc/generic.texi index 75c47456fb9..73dd1236412 100644 --- a/gcc/doc/generic.texi +++ b/gcc/doc/generic.texi @@ -3166,6 +3166,30 @@ several statements chained together. Used to represent a @code{break} statement. There are no additional fields. +@item CILK_SPAWN_STMT + +Used to represent a spawning function in the Cilk Plus language extension. +This tree has one field that holds the name of the spawning function. +@code{_Cilk_spawn} can be written in C in the following way: + +@smallexample +@code{_Cilk_spawn} <function_name> (<parameters>); +@end smallexample + +Detailed description for usage and functionality of @code{_Cilk_spawn} can be +found at http://www.cilkplus.org + +@item CILK_SYNC_STMT + +This statement is part of the Cilk Plus language extension. It indicates that +the current function cannot continue in parallel with its spawned children. +There are no additional fields. @code{_Cilk_sync} can be written in C in the +following way: + +@smallexample +@code{_Cilk_sync}; +@end smallexample + @item CLEANUP_STMT Used to represent an action that should take place upon exit from the diff --git a/gcc/doc/passes.texi b/gcc/doc/passes.texi index d8e4315d74c..073f8996088 100644 --- a/gcc/doc/passes.texi +++ b/gcc/doc/passes.texi @@ -124,13 +124,45 @@ true, then we expand them using either @code{expand_array_notation_exprs} or inside conditions, they are transformed using the function @code{fix_conditional_array_notations}. The C language-specific routines are located in @file{c/c-array-notation.c} and the equivalent C++ routines are in -file @file{cp/cp-array-notation.c}. Common routines such as functions to -initialize builtin functions are stored in @file{array-notation-common.c}. +the file @file{cp/cp-array-notation.c}. Common routines such as functions to +initialize built-in functions are stored in @file{array-notation-common.c}. + +@item Cilk keywords: +@itemize @bullet +@item @code{_Cilk_spawn}: +The @code{_Cilk_spawn} keyword is parsed and the function it contains is marked +as a spawning function. The spawning function is called the spawner. At +the end of the parsing phase, appropriate built-in functions are +added to the spawner that are defined in the Cilk runtime. The appropriate +locations of these functions, and the internal structures are detailed in +@code{cilk_init_builtins} in the file @file{cilk-common.c}. The pointers to +Cilk functions and fields of internal structures are described +in @file{cilk.h}. The built-in functions are described in +@file{cilk-builtins.def}. + +During gimplification, a new "spawn-helper" function is created. +The spawned function is replaced with a spawn helper function in the spawner. +The spawned function-call is moved into the spawn helper. The main function +that does these transformations is @code{gimplify_cilk_spawn} in +@file{c-family/cilk.c}. In the spawn-helper, the gimplification function +@code{gimplify_call_expr}, inserts a function call @code{__cilkrts_detach}. +This function is expanded by @code{builtin_expand_cilk_detach} located in +@file{c-family/cilk.c}. + +@item @code{_Cilk_sync}: +@code{_Cilk_sync} is parsed like a keyword. During gimplification, +the function @code{gimplify_cilk_sync} in @file{c-family/cilk.c}, will replace +this keyword with a set of functions that are stored in the Cilk runtime. +One of the internal functions inserted during gimplification, +@code{__cilkrts_pop_frame} must be expanded by the compiler and is +done by @code{builtin_expand_cilk_pop_frame} in @file{cilk-common.c}. + +@end itemize @end itemize -Detailed information about Cilk Plus and language specification is provided in -@w{@uref{http://www.cilkplus.org/}}. It is worth mentioning that the current -implementation follows ABI 0.9. +Documentation about Cilk Plus and language specification is provided under the +"Learn" section in @w{@uref{http://www.cilkplus.org/}}. It is worth mentioning +that the current implementation follows ABI 1.1. @node Gimplification pass @section Gimplification pass diff --git a/gcc/function.h b/gcc/function.h index d1f4ffc1fd4..9bb6ff04c50 100644 --- a/gcc/function.h +++ b/gcc/function.h @@ -552,6 +552,9 @@ struct GTY(()) function { /* Vector of function local variables, functions, types and constants. */ vec<tree, va_gc> *local_decls; + /* In a Cilk function, the VAR_DECL for the frame descriptor. */ + tree cilk_frame_decl; + /* For md files. */ /* tm.h can use this to store whatever it likes. */ @@ -607,6 +610,12 @@ struct GTY(()) function { either as a subroutine or builtin. */ unsigned int calls_alloca : 1; + /* This will indicate whether a function is a cilk function */ + unsigned int is_cilk_function : 1; + + /* Nonzero if this is a Cilk function that spawns. */ + unsigned int calls_cilk_spawn : 1; + /* Nonzero if function being compiled receives nonlocal gotos from nested functions. */ unsigned int has_nonlocal_label : 1; diff --git a/gcc/gimplify.c b/gcc/gimplify.c index 04f08b3461e..5edc6e8c33d 100644 --- a/gcc/gimplify.c +++ b/gcc/gimplify.c @@ -48,6 +48,7 @@ along with GCC; see the file COPYING3. If not see #include "vec.h" #include "omp-low.h" #include "gimple-low.h" +#include "cilk.h" #include "langhooks-def.h" /* FIXME: for lhd_set_decl_assembler_name */ #include "tree-pass.h" /* FIXME: only for PROP_gimple_any */ @@ -1309,6 +1310,15 @@ gimplify_return_expr (tree stmt, gimple_seq *pre_p) if (ret_expr == error_mark_node) return GS_ERROR; + /* Implicit _Cilk_sync must be inserted right before any return statement + if there is a _Cilk_spawn in the function. If the user has provided a + _Cilk_sync, the optimizer should remove this duplicate one. */ + if (fn_contains_cilk_spawn_p (cfun)) + { + tree impl_sync = build0 (CILK_SYNC_STMT, void_type_node); + gimplify_and_add (impl_sync, pre_p); + } + if (!ret_expr || TREE_CODE (ret_expr) == RESULT_DECL || ret_expr == error_mark_node) @@ -2498,6 +2508,12 @@ gimplify_call_expr (tree *expr_p, gimple_seq *pre_p, bool want_value) if (! EXPR_HAS_LOCATION (*expr_p)) SET_EXPR_LOCATION (*expr_p, input_location); + if (fn_contains_cilk_spawn_p (cfun) + && lang_hooks.cilkplus.cilk_detect_spawn_and_unwrap (expr_p) + && !seen_error ()) + return (enum gimplify_status) + lang_hooks.cilkplus.gimplify_cilk_spawn (expr_p, pre_p, NULL); + /* This may be a call to a builtin function. Builtin function calls may be transformed into different @@ -4714,6 +4730,12 @@ gimplify_modify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p, gcc_assert (TREE_CODE (*expr_p) == MODIFY_EXPR || TREE_CODE (*expr_p) == INIT_EXPR); + + if (fn_contains_cilk_spawn_p (cfun) + && lang_hooks.cilkplus.cilk_detect_spawn_and_unwrap (expr_p) + && !seen_error ()) + return (enum gimplify_status) + lang_hooks.cilkplus.gimplify_cilk_spawn (expr_p, pre_p, post_p); /* Trying to simplify a clobber using normal logic doesn't work, so handle it here. */ @@ -7660,6 +7682,19 @@ gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p, } break; + case CILK_SPAWN_STMT: + gcc_assert + (fn_contains_cilk_spawn_p (cfun) + && lang_hooks.cilkplus.cilk_detect_spawn_and_unwrap (expr_p)); + if (!seen_error ()) + { + ret = (enum gimplify_status) + lang_hooks.cilkplus.gimplify_cilk_spawn (expr_p, pre_p, + post_p); + break; + } + /* If errors are seen, then just process it as a CALL_EXPR. */ + case CALL_EXPR: ret = gimplify_call_expr (expr_p, pre_p, fallback != fb_none); @@ -8295,6 +8330,22 @@ gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p, break; } + case CILK_SYNC_STMT: + { + if (!fn_contains_cilk_spawn_p (cfun)) + { + error_at (EXPR_LOCATION (*expr_p), + "expected %<_Cilk_spawn%> before %<_Cilk_sync%>"); + ret = GS_ERROR; + } + else + { + gimplify_cilk_sync (expr_p, pre_p); + ret = GS_ALL_DONE; + } + break; + } + default: switch (TREE_CODE_CLASS (TREE_CODE (*expr_p))) { diff --git a/gcc/ipa-inline-analysis.c b/gcc/ipa-inline-analysis.c index bc0e8c3170f..83bd47933b9 100644 --- a/gcc/ipa-inline-analysis.c +++ b/gcc/ipa-inline-analysis.c @@ -95,6 +95,7 @@ along with GCC; see the file COPYING3. If not see #include "cfgloop.h" #include "tree-scalar-evolution.h" #include "ipa-utils.h" +#include "cilk.h" /* Estimate runtime of function can easilly run into huge numbers with many nested loops. Be sure we can compute time * INLINE_SIZE_SCALE * 2 in an @@ -1440,6 +1441,9 @@ initialize_inline_failed (struct cgraph_edge *e) e->inline_failed = CIF_REDEFINED_EXTERN_INLINE; else if (e->call_stmt_cannot_inline_p) e->inline_failed = CIF_MISMATCHED_ARGUMENTS; + else if (cfun && fn_contains_cilk_spawn_p (cfun)) + /* We can't inline if the function is spawing a function. */ + e->inline_failed = CIF_FUNCTION_NOT_INLINABLE; else e->inline_failed = CIF_FUNCTION_NOT_CONSIDERED; } diff --git a/gcc/ipa-inline.c b/gcc/ipa-inline.c index 784094b4948..f4cb72a9c2b 100644 --- a/gcc/ipa-inline.c +++ b/gcc/ipa-inline.c @@ -115,6 +115,7 @@ along with GCC; see the file COPYING3. If not see #include "ipa-inline.h" #include "ipa-utils.h" #include "sreal.h" +#include "cilk.h" /* Statistics we collect about inlining algorithm. */ static int overall_size; @@ -264,7 +265,8 @@ can_inline_edge_p (struct cgraph_edge *e, bool report, e->inline_failed = CIF_BODY_NOT_AVAILABLE; inlinable = false; } - else if (!inline_summary (callee)->inlinable) + else if (!inline_summary (callee)->inlinable + || (caller_cfun && fn_contains_cilk_spawn_p (caller_cfun))) { e->inline_failed = CIF_FUNCTION_NOT_INLINABLE; inlinable = false; diff --git a/gcc/ira.c b/gcc/ira.c index d95910900b3..628fd031208 100644 --- a/gcc/ira.c +++ b/gcc/ira.c @@ -1874,6 +1874,9 @@ ira_setup_eliminable_regset (bool from_ira_p) || (flag_stack_check && STACK_CHECK_MOVING_SP) || crtl->accesses_prior_frames || crtl->stack_realign_needed + /* We need a frame pointer for all Cilk Plus functions that use + Cilk keywords. */ + || (flag_enable_cilkplus && cfun->is_cilk_function) || targetm.frame_pointer_required ()); if (from_ira_p && ira_use_lra_p) diff --git a/gcc/langhooks-def.h b/gcc/langhooks-def.h index b7be47200a8..411cf74b666 100644 --- a/gcc/langhooks-def.h +++ b/gcc/langhooks-def.h @@ -214,6 +214,18 @@ extern tree lhd_make_node (enum tree_code); #define LANG_HOOKS_OMP_CLAUSE_DTOR hook_tree_tree_tree_null #define LANG_HOOKS_OMP_FINISH_CLAUSE hook_void_tree +extern void lhd_install_body_with_frame_cleanup (tree, tree); +extern bool lhd_cilk_detect_spawn (tree *); +#define LANG_HOOKS_CILKPLUS_DETECT_SPAWN_AND_UNWRAP lhd_cilk_detect_spawn +#define LANG_HOOKS_CILKPLUS_FRAME_CLEANUP lhd_install_body_with_frame_cleanup +#define LANG_HOOKS_CILKPLUS_GIMPLIFY_SPAWN lhd_gimplify_expr + +#define LANG_HOOKS_CILKPLUS { \ + LANG_HOOKS_CILKPLUS_DETECT_SPAWN_AND_UNWRAP, \ + LANG_HOOKS_CILKPLUS_FRAME_CLEANUP, \ + LANG_HOOKS_CILKPLUS_GIMPLIFY_SPAWN \ +} + #define LANG_HOOKS_DECLS { \ LANG_HOOKS_GLOBAL_BINDINGS_P, \ LANG_HOOKS_PUSHDECL, \ @@ -291,6 +303,7 @@ extern void lhd_end_section (void); LANG_HOOKS_TREE_DUMP_INITIALIZER, \ LANG_HOOKS_DECLS, \ LANG_HOOKS_FOR_TYPES_INITIALIZER, \ + LANG_HOOKS_CILKPLUS, \ LANG_HOOKS_LTO, \ LANG_HOOKS_GET_INNERMOST_GENERIC_PARMS, \ LANG_HOOKS_GET_INNERMOST_GENERIC_ARGS, \ diff --git a/gcc/langhooks.c b/gcc/langhooks.c index 559d5c1beac..5d1457b1b22 100644 --- a/gcc/langhooks.c +++ b/gcc/langhooks.c @@ -675,3 +675,18 @@ lhd_end_section (void) saved_section = NULL; } } + +/* Empty function that is replaced with appropriate language dependent + frame cleanup function for _Cilk_spawn. */ + +void +lhd_install_body_with_frame_cleanup (tree, tree) +{ +} + +/* Empty function to handle cilk_valid_spawn. */ +bool +lhd_cilk_detect_spawn (tree *) +{ + return false; +} diff --git a/gcc/langhooks.h b/gcc/langhooks.h index a83bf7b71ed..9539e7d5b7a 100644 --- a/gcc/langhooks.h +++ b/gcc/langhooks.h @@ -139,6 +139,23 @@ struct lang_hooks_for_types tree (*reconstruct_complex_type) (tree, tree); }; +/* Language hooks related to Cilk Plus. */ + +struct lang_hooks_for_cilkplus +{ + /* Returns true if the expression passed in has a spawned function call. */ + bool (*cilk_detect_spawn_and_unwrap) (tree *); + + /* Function to add the clean up functions after spawn. The reason why it is + language dependent is because in C++, it must handle exceptions. */ + void (*install_body_with_frame_cleanup) (tree, tree); + + /* Function to gimplify a spawned function call. Returns enum gimplify + status, but as mentioned in a previous comment, we can't see that type + here, so just return an int. */ + int (*gimplify_cilk_spawn) (tree *, gimple_seq *, gimple_seq *); +}; + /* Language hooks related to decls and the symbol table. */ struct lang_hooks_for_decls @@ -408,6 +425,8 @@ struct lang_hooks struct lang_hooks_for_types types; + struct lang_hooks_for_cilkplus cilkplus; + struct lang_hooks_for_lto lto; /* Returns a TREE_VEC of the generic parameters of an instantiation of diff --git a/gcc/lto/ChangeLog b/gcc/lto/ChangeLog index d1f5d496755..6606e590fb6 100644 --- a/gcc/lto/ChangeLog +++ b/gcc/lto/ChangeLog @@ -1,3 +1,9 @@ +2013-10-29 Balaji V. Iyer <balaji.v.iyer@intel.com> + + * Make-lang.in (lto/lto-lang.o): Added cilk.h in dependency list. + * lto-lang.c (lto_init): Added a call to cilk_init_builtins if Cilk + Plus is enabled. + 2013-10-29 David Malcolm <dmalcolm@redhat.com> Patch autogenerated by refactor_symtab.py from diff --git a/gcc/lto/lto-lang.c b/gcc/lto/lto-lang.c index 0fa0fc9a438..b56c22b8a87 100644 --- a/gcc/lto/lto-lang.c +++ b/gcc/lto/lto-lang.c @@ -35,6 +35,7 @@ along with GCC; see the file COPYING3. If not see #include "diagnostic-core.h" #include "toplev.h" #include "lto-streamer.h" +#include "cilk.h" static tree lto_type_for_size (unsigned, int); @@ -1174,6 +1175,9 @@ lto_init (void) lto_define_builtins (va_list_type_node, build_reference_type (va_list_type_node)); } + + if (flag_enable_cilkplus) + cilk_init_builtins (); targetm.init_builtins (); build_common_builtin_nodes (); diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 357939e9dd4..621b951bfa3 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,25 @@ +2013-10-29 Balaji V. Iyer <balaji.v.iyer@intel.com> + + * c-c++-common/cilk-plus/CK/compound_cilk_spawn.c: New test. + * c-c++-common/cilk-plus/CK/concec_cilk_spawn.c: Likewise. + * c-c++-common/cilk-plus/CK/fib.c: Likewise. + * c-c++-common/cilk-plus/CK/no_args_error.c: Likewise. + * c-c++-common/cilk-plus/CK/spawnee_inline.c: Likewise. + * c-c++-common/cilk-plus/CK/spawner_inline.c: Likewise. + * c-c++-common/cilk-plus/CK/spawning_arg.c: Likewise. + * c-c++-common/cilk-plus/CK/steal_check.c: Likewise. + * c-c++-common/cilk-plus/CK/test__cilk.c: Likewise. + * c-c++-common/cilk-plus/CK/varargs_test.c: Likewise. + * c-c++-common/cilk-plus/CK/sync_wo_spawn.c: Likewise. + * c-c++-common/cilk-plus/CK/invalid_spawn.c: Likewise. + * c-c++-common/cilk-plus/CK/spawn_in_return.c: Likewise. + * c-c++-common/cilk-plus/CK/fib_init_expr_xy.c: Likewise. + * c-c++-common/cilk-plus/CK/fib_no_sync.c: Likewise. + * c-c++-common/cilk-plus/CK/fib_no_return.c: Likewise. + * gcc.dg/cilk-plus/cilk-plus.exp: Added support to run Cilk Keywords + test stored in c-c++-common. Also, added the Cilk runtime's library + to the ld_library_path. + 2013-10-29 Paolo Carlini <paolo.carlini@oracle.com> PR c++/58888 diff --git a/gcc/testsuite/c-c++-common/cilk-plus/CK/compound_cilk_spawn.c b/gcc/testsuite/c-c++-common/cilk-plus/CK/compound_cilk_spawn.c new file mode 100644 index 00000000000..5e687bd0a0c --- /dev/null +++ b/gcc/testsuite/c-c++-common/cilk-plus/CK/compound_cilk_spawn.c @@ -0,0 +1,26 @@ +/* { dg-do compile } */ +/* { dg-options "-fcilkplus" } */ + +/* <feature> + A program is considered ill formed if the _Cilk_spawn form of this + expression appears other than in one of the following contexts: + as the entire body of an expression statement, + as the entire right hand side of an assignment expression that is the entire + body of an expression statement, or as the entire initializer-clause in a + simple declaration. + </feature> +*/ + +int spawn_func (int arg) +{ + return arg + 1; +} + +int check() +{ + int z; + z = 23, _Cilk_spawn spawn_func (3), 3424; /* { dg-error "spawned function call cannot be part of a comma expression" } */ + 23, spawn_func (5), _Cilk_spawn spawn_func (3); /* { dg-error "spawned function call cannot be part of a comma expression" } */ + _Cilk_spawn spawn_func (0), _Cilk_spawn spawn_func (3), 3, spawn_func (0); /* { dg-error "spawned function call cannot be part of a comma expression" } */ + return 23; +} diff --git a/gcc/testsuite/c-c++-common/cilk-plus/CK/concec_cilk_spawn.c b/gcc/testsuite/c-c++-common/cilk-plus/CK/concec_cilk_spawn.c new file mode 100644 index 00000000000..b93c9626d18 --- /dev/null +++ b/gcc/testsuite/c-c++-common/cilk-plus/CK/concec_cilk_spawn.c @@ -0,0 +1,20 @@ +/* { dg-do compile } */ +/* { dg-options "-fcilkplus" } */ + +/* <feature> Consecutive _Cilk_spawn tokens are not permitted + </feature> +*/ + +int spawn_func (int arg) +{ + return arg + 1; +} + +void func () +{ + int a; + a = _Cilk_spawn _Cilk_spawn spawn_func (4); /* { dg-error "consecutive" } */ + a = _Cilk_spawn _Cilk_spawn _Cilk_spawn spawn_func (4); /* { dg-error "consecutive" } */ + a = _Cilk_spawn _Cilk_spawn _Cilk_spawn _Cilk_spawn spawn_func (4); /* { dg-error "consecutive" } */ + return; +} diff --git a/gcc/testsuite/c-c++-common/cilk-plus/CK/fib.c b/gcc/testsuite/c-c++-common/cilk-plus/CK/fib.c new file mode 100644 index 00000000000..6612936a05c --- /dev/null +++ b/gcc/testsuite/c-c++-common/cilk-plus/CK/fib.c @@ -0,0 +1,61 @@ +/* { dg-options "-fcilkplus" } */ +/* { dg-do run { target i?86-*-* x86_64-*-* } } */ +/* { dg-options "-fcilkplus -lcilkrts" { target { i?86-*-* x86_64-*-* } } } */ + +#if HAVE_IO +#include <stdio.h> +#endif + +int fib (int); +int fib_serial (int); + +int main(void) +{ + int ii = 0, error = 0; + int fib_result[41], fib_serial_result[41]; +#if HAVE_IO + + for (ii = 0; ii <= 40; ii++) + printf("fib (%2d) = %10d\n", ii, fib (ii)); +#else + for (ii = 0; ii <= 40; ii++) + { + fib_result[ii] = fib (ii); + fib_serial_result[ii] = fib_serial (ii); + } + + for (ii = 0; ii <= 40; ii++) + { + if (fib_result[ii] != fib_serial_result[ii]) + error = 1; + } +#endif + return error; +} + +int fib_serial (int n) +{ + int x = 0, y = 0; + if (n < 2) + return n; + else + { + x = fib (n-1); + y = fib (n-2); + return (x+y); + } +} + +int fib(int n) +{ + int x = 0, y = 0; + if (n < 2) + return n; + else + { + x = _Cilk_spawn fib(n-1); + y = fib(n-2); + _Cilk_sync; + return (x+y); + } +} diff --git a/gcc/testsuite/c-c++-common/cilk-plus/CK/fib_init_expr_xy.c b/gcc/testsuite/c-c++-common/cilk-plus/CK/fib_init_expr_xy.c new file mode 100644 index 00000000000..6b09918a420 --- /dev/null +++ b/gcc/testsuite/c-c++-common/cilk-plus/CK/fib_init_expr_xy.c @@ -0,0 +1,60 @@ +/* { dg-options "-fcilkplus" } */ +/* { dg-do run { target i?86-*-* x86_64-*-* } } */ +/* { dg-options "-fcilkplus -lcilkrts" { target { i?86-*-* x86_64-*-* } } } */ + +#if HAVE_IO +#include <stdio.h> +#endif + +int fib (int); +int fib_serial (int); + +int main(void) +{ + int ii = 0, error = 0; + int fib_result[41], fib_serial_result[41]; +#if HAVE_IO + + for (ii = 0; ii <= 40; ii++) + printf("fib (%2d) = %10d\n", ii, fib (ii)); +#else + for (ii = 0; ii <= 40; ii++) + { + fib_result[ii] = fib (ii); + fib_serial_result[ii] = fib_serial (ii); + } + + for (ii = 0; ii <= 40; ii++) + { + if (fib_result[ii] != fib_serial_result[ii]) + error = 1; + } +#endif + return error; +} + +int fib_serial (int n) +{ + int x = 0, y = 0; + if (n < 2) + return n; + else + { + x = fib (n-1); + y = fib (n-2); + return (x+y); + } +} + +int fib(int n) +{ + if (n < 2) + return n; + else + { + int x = _Cilk_spawn fib(n-1); + int y = fib(n-2); + _Cilk_sync; + return (x+y); + } +} diff --git a/gcc/testsuite/c-c++-common/cilk-plus/CK/fib_no_return.c b/gcc/testsuite/c-c++-common/cilk-plus/CK/fib_no_return.c new file mode 100644 index 00000000000..2adf3a2f6e1 --- /dev/null +++ b/gcc/testsuite/c-c++-common/cilk-plus/CK/fib_no_return.c @@ -0,0 +1,65 @@ +/* { dg-options "-fcilkplus" } */ +/* { dg-do run { target i?86-*-* x86_64-*-* } } */ +/* { dg-options "-fcilkplus -lcilkrts" { target { i?86-*-* x86_64-*-* } } } */ + +#if HAVE_IO +#include <stdio.h> +#endif + +void fib (int *, int); +int fib_serial (int); + +int main(void) +{ + int ii = 0, error = 0; + int fib_result[41], fib_serial_result[41]; + +#if HAVE_IO + for (ii = 0; ii <= 40; ii++) + { + int result = 0; + fib (&result, ii); + printf("fib (%2d) = %10d\n", ii, result); + } +#else + for (ii = 0; ii <= 40; ii++) + { + fib (&fib_result[ii], ii); + fib_serial_result[ii] = fib_serial (ii); + } + + for (ii = 0; ii <= 40; ii++) + { + if (fib_result[ii] != fib_serial_result[ii]) + error = 1; + } +#endif + return error; +} + +int fib_serial (int n) +{ + int x = 0, y = 0; + if (n < 2) + return n; + else + { + fib (&x, n-1); + fib (&y, n-2); + return (x+y); + } +} + +void fib(int *result, int n) +{ + int x = 0, y = 0; + if (n < 2) + x = n; + else + { + _Cilk_spawn fib(&x, n-1); + fib(&y, n-2); + _Cilk_sync; + } + *result = (x+y); +} diff --git a/gcc/testsuite/c-c++-common/cilk-plus/CK/fib_no_sync.c b/gcc/testsuite/c-c++-common/cilk-plus/CK/fib_no_sync.c new file mode 100644 index 00000000000..9de70be7584 --- /dev/null +++ b/gcc/testsuite/c-c++-common/cilk-plus/CK/fib_no_sync.c @@ -0,0 +1,59 @@ +/* { dg-options "-fcilkplus" } */ +/* { dg-do run { target i?86-*-* x86_64-*-* } } */ +/* { dg-options "-fcilkplus -lcilkrts" { target { i?86-*-* x86_64-*-* } } } */ + +#if HAVE_IO +#include <stdio.h> +#endif + +int fib (int); +int fib_serial (int); + +int main(void) +{ + int ii = 0, error = 0; + int fib_result[41], fib_serial_result[41]; +#if HAVE_IO + + for (ii = 0; ii <= 40; ii++) + printf("fib (%2d) = %10d\n", ii, fib (ii)); +#else + for (ii = 0; ii <= 40; ii++) + { + fib_result[ii] = fib (ii); + fib_serial_result[ii] = fib_serial (ii); + } + + for (ii = 0; ii <= 40; ii++) + { + if (fib_result[ii] != fib_serial_result[ii]) + error = 1; + } +#endif + return error; +} + +int fib_serial (int n) +{ + int x = 0, y = 0; + if (n < 2) + return n; + else + { + x = fib (n-1); + y = fib (n-2); + return (x+y); + } +} + +int fib(int n) +{ + if (n < 2) + return n; + else + { + int x = _Cilk_spawn fib(n-1); + int y = fib(n-2); + return (x+y); + } +} diff --git a/gcc/testsuite/c-c++-common/cilk-plus/CK/invalid_spawns.c b/gcc/testsuite/c-c++-common/cilk-plus/CK/invalid_spawns.c new file mode 100644 index 00000000000..90dd5c130b4 --- /dev/null +++ b/gcc/testsuite/c-c++-common/cilk-plus/CK/invalid_spawns.c @@ -0,0 +1,11 @@ +extern int foo (); +int bar = _Cilk_spawn foo (); /* { dg-error "may only be used inside a function" } */ + + +int main (void) +{ + int x; + + _Cilk_spawn x; /* { dg-error "only function calls can be spawned" } */ + return x; +} diff --git a/gcc/testsuite/c-c++-common/cilk-plus/CK/no_args_error.c b/gcc/testsuite/c-c++-common/cilk-plus/CK/no_args_error.c new file mode 100644 index 00000000000..593732ef5ab --- /dev/null +++ b/gcc/testsuite/c-c++-common/cilk-plus/CK/no_args_error.c @@ -0,0 +1,11 @@ +/* { dg-do compile } */ +/* { dg-options "-fcilkplus" } */ + +int spawn_1 (); +typedef int(*func) (int); + +void check () { + func var = spawn_1; /* { dg-error "invalid conversion from" "" { target c++ } 8 } */ + _Cilk_spawn var (); /* { dg-error "too few arguments to function" } */ +} + diff --git a/gcc/testsuite/c-c++-common/cilk-plus/CK/spawn_in_return.c b/gcc/testsuite/c-c++-common/cilk-plus/CK/spawn_in_return.c new file mode 100644 index 00000000000..14b7eef1276 --- /dev/null +++ b/gcc/testsuite/c-c++-common/cilk-plus/CK/spawn_in_return.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-options "-fcilkplus" } */ + +int main (void) +{ + extern int foo (); + return _Cilk_spawn foo (); /* { dg-error "return statement is not allowed" } */ +} diff --git a/gcc/testsuite/c-c++-common/cilk-plus/CK/spawnee_inline.c b/gcc/testsuite/c-c++-common/cilk-plus/CK/spawnee_inline.c new file mode 100644 index 00000000000..8060c6ceb15 --- /dev/null +++ b/gcc/testsuite/c-c++-common/cilk-plus/CK/spawnee_inline.c @@ -0,0 +1,80 @@ +/* { dg-do run { target { i?86-*-* x86_64-*-* } } } */ +/* { dg-options "-fcilkplus -w" } */ +/* { dg-options "-lcilkrts" { target { i?86-*-* x86_64-*-* } } } */ + +#include <stdio.h> +#include <stdlib.h> +#define DEFAULT_VALUE "30" + +int fib (char *n_char) +{ + int n; + char n_char_minus_one[20], n_char_minus_two[20]; + if (n_char) + n = atoi (n_char); + else + n = atoi(DEFAULT_VALUE); + + if (n < 2) + return n; + else + { + int x, y; + sprintf (n_char_minus_one,"%d", n-1); + sprintf (n_char_minus_two,"%d", n-2); + x = _Cilk_spawn fib (n_char_minus_one); + y = _Cilk_spawn fib (n_char_minus_two); + _Cilk_sync; + return (x+y); + } +} + +int fib_serial (int n) +{ + int x, y; + if (n < 2) + return n; + else + { + x = fib_serial (n-1); + y = fib_serial (n-2); + return (x+y); + } + return 0; +} + +int main2_parallel (int argc, char *argv[]) +{ + int n, result_parallel = 0; + + if (argc == 2) + { + result_parallel = _Cilk_spawn fib (argv[1]); + _Cilk_sync; + } + else + { + result_parallel = _Cilk_spawn fib((char *)"30"); + _Cilk_sync; + } + return result_parallel; +} + +int main2_serial (int argc, char *argv[]) +{ + int n, result_serial = 0; + if (argc == 2) + result_serial = fib_serial (atoi (argv[1])); + else + result_serial = fib_serial (atoi (DEFAULT_VALUE)); + + return result_serial; +} + +int main (void) +{ + if (main2_serial (1, 0) != main2_parallel (1,0)) + return 1; + return 0; +} + diff --git a/gcc/testsuite/c-c++-common/cilk-plus/CK/spawner_inline.c b/gcc/testsuite/c-c++-common/cilk-plus/CK/spawner_inline.c new file mode 100644 index 00000000000..eab9e4206a0 --- /dev/null +++ b/gcc/testsuite/c-c++-common/cilk-plus/CK/spawner_inline.c @@ -0,0 +1,67 @@ +/* { dg-do run { target { i?86-*-* x86_64-*-* } } } */ +/* { dg-options "-fcilkplus" } */ +/* { dg-options "-lcilkrts" { target { i?86-*-* x86_64-*-* } } } */ + +#include <stdlib.h> +#define DEFAULT_VALUE 30 +int fib (int n) +{ + if (n<2) + return n; + else + { + int x, y; + x = _Cilk_spawn fib (n-1); + y = _Cilk_spawn fib (n-2); + _Cilk_sync; + return (x+y); + return 5; + } +} + +int main_parallel (int argc, char *argv[]) +{ + int n, result; + if (argc == 2) + n = atoi(argv[1]); + else + n = DEFAULT_VALUE; + result = _Cilk_spawn fib(n); + _Cilk_sync; + return result; +} + +int fib_serial (int n) +{ + int x, y; + if (n < 2) + return n; + else + { + x = fib (n-1); + y = fib (n-2); + return (x+y); + } +} + +int main_serial (int argc, char *argv[]) +{ + int n, result; + + if (argc == 2) + n = atoi (argv[1]); + else + n = DEFAULT_VALUE; + result = fib_serial (n); + + return result; +} + +int main (void) +{ + if (main_serial (1, 0) != main_parallel (1,0)) + return 1; + else + return 0; +} + diff --git a/gcc/testsuite/c-c++-common/cilk-plus/CK/spawning_arg.c b/gcc/testsuite/c-c++-common/cilk-plus/CK/spawning_arg.c new file mode 100644 index 00000000000..ac379528379 --- /dev/null +++ b/gcc/testsuite/c-c++-common/cilk-plus/CK/spawning_arg.c @@ -0,0 +1,37 @@ +/* { dg-do run { target { i?86-*-* x86_64-*-* } } } */ +/* { dg-options "-fcilkplus" } */ +/* { dg-options "-lcilkrts" { target { i?86-*-* x86_64-*-* } } } */ + +void f0(volatile int *steal_flag) +{ + int i = 0; + /* Wait for steal_flag to be set */ + while (!*steal_flag) + ; +} + +int f1() +{ + + volatile int steal_flag = 0; + _Cilk_spawn f0(&steal_flag); + steal_flag = 1; // Indicate stolen + _Cilk_sync; + return 0; +} + +void f2(int q) +{ + q = 5; +} + +void f3() +{ + _Cilk_spawn f2(f1()); +} + +int main() +{ + f3(); + return 0; +} diff --git a/gcc/testsuite/c-c++-common/cilk-plus/CK/steal_check.c b/gcc/testsuite/c-c++-common/cilk-plus/CK/steal_check.c new file mode 100644 index 00000000000..21d6797857d --- /dev/null +++ b/gcc/testsuite/c-c++-common/cilk-plus/CK/steal_check.c @@ -0,0 +1,43 @@ +/* { dg-do run { target { i?86-*-* x86_64-*-* } } } */ +/* { dg-options "-fcilkplus" } */ +/* { dg-options "-lcilkrts" { target { i?86-*-* x86_64-*-* } } } */ + +// #include <cilk/cilk_api.h> +extern void __cilkrts_set_param (char *, char *); + +void foo(volatile int *); + +void main2(void); + +int main(void) +{ + // __cilkrts_set_param ((char *)"nworkers", (char *)"2"); + main2(); + return 0; +} + + +void main2(void) +{ + int some_var = 0; + + _Cilk_spawn foo(&some_var); + + some_var=1; + some_var=5; + some_var=3; + some_var=4; + + _Cilk_sync; + return; +} + +void foo(volatile int *some_other_var) +{ + while (*some_other_var == 0) + { + ; + } +} + + diff --git a/gcc/testsuite/c-c++-common/cilk-plus/CK/sync_wo_spawn.c b/gcc/testsuite/c-c++-common/cilk-plus/CK/sync_wo_spawn.c new file mode 100644 index 00000000000..51be796e562 --- /dev/null +++ b/gcc/testsuite/c-c++-common/cilk-plus/CK/sync_wo_spawn.c @@ -0,0 +1,9 @@ +/* { dg-do compile } */ +/* { dg-options "-fcilkplus" } */ + +int main (void) +{ + _Cilk_sync; /* { dg-error "expected '_Cilk_spawn' before '_Cilk_sync'" } */ + return 0; +} + diff --git a/gcc/testsuite/c-c++-common/cilk-plus/CK/test__cilk.c b/gcc/testsuite/c-c++-common/cilk-plus/CK/test__cilk.c new file mode 100644 index 00000000000..2b37cd664c1 --- /dev/null +++ b/gcc/testsuite/c-c++-common/cilk-plus/CK/test__cilk.c @@ -0,0 +1,10 @@ +/* { dg-do compile } */ +/* { dg-do run { target { i?86-*-* x86_64-*-* } } } */ +/* { dg-options "-fcilkplus" } */ + +int main (void) +{ + if (__cilk == 200) + return 0; + return 1; +} diff --git a/gcc/testsuite/c-c++-common/cilk-plus/CK/varargs_test.c b/gcc/testsuite/c-c++-common/cilk-plus/CK/varargs_test.c new file mode 100644 index 00000000000..ab5d63a3f4c --- /dev/null +++ b/gcc/testsuite/c-c++-common/cilk-plus/CK/varargs_test.c @@ -0,0 +1,47 @@ +/* { dg-do run { target { i?86-*-* x86_64-*-* } } } */ +/* { dg-options "-fcilkplus" } */ +/* { dg-options "-lcilkrts" { target { i?86-*-* x86_64-*-* } } } */ + +#include <stdarg.h> +#include <stdlib.h> + + +double compute_total (int no_elements, ...); + +int main(void) +{ + double array[5] = {5.0, 4.0, 9.0, 3.0, 4.0}; + double array2[5] = {5.0, 6.0, 8.0, 6.0}; + double yy=0, xx=0, xx_serial, yy_serial; + + yy = _Cilk_spawn compute_total(5,array[0],array[1],array[2], + array[3], array[4]); + xx= compute_total(4,array2[0],array2[1],array2[2], array2[3]); + + _Cilk_sync; + + yy_serial = compute_total(5,array[0],array[1],array[2], array[3], array[4]); + xx_serial = compute_total(4,array2[0],array2[1],array2[2], array2[3]); + + if ((xx + yy) != (xx_serial + yy_serial)) + return 1; + return 0; + +} + + +double compute_total (int no_elements, ...) +{ + double total = 0; + va_list args; + va_start(args, no_elements); + int ii = 0; + for (ii = 0; ii < no_elements; ii++) + { + total += va_arg(args,double); + } + va_end(args); + + return total; +} + diff --git a/gcc/testsuite/gcc.dg/cilk-plus/cilk-plus.exp b/gcc/testsuite/gcc.dg/cilk-plus/cilk-plus.exp index 2533febc8ab..a8f00d493ee 100644 --- a/gcc/testsuite/gcc.dg/cilk-plus/cilk-plus.exp +++ b/gcc/testsuite/gcc.dg/cilk-plus/cilk-plus.exp @@ -23,6 +23,11 @@ if { ![check_effective_target_cilkplus] } { return; } +verbose "$tool $libdir" 1 +set library_var [get_multilibs] +# Pointing the ld_library_path to the Cilk Runtime library binaries. +set ld_library_path "${library_var}/libcilkrts/.libs" + dg-init dg-runtest [lsort [glob -nocomplain $srcdir/c-c++-common/cilk-plus/AN/*.c]] " -fcilkplus" " " dg-runtest [lsort [glob -nocomplain $srcdir/c-c++-common/cilk-plus/AN/*.c]] " -O0 -fcilkplus" " " @@ -46,4 +51,31 @@ dg-runtest [lsort [glob -nocomplain $srcdir/c-c++-common/cilk-plus/AN/*.c]] " -f dg-runtest [lsort [glob -nocomplain $srcdir/c-c++-common/cilk-plus/AN/*.c]] " -fcilkplus -g -O2 -ftree-vectorize -std=c99" " " dg-runtest [lsort [glob -nocomplain $srcdir/c-c++-common/cilk-plus/AN/*.c]] " -fcilkplus -g -O3 -std=c99" " " dg-runtest [lsort [glob -nocomplain $srcdir/c-c++-common/cilk-plus/AN/*.c]] " -O3 -ftree-vectorize -std=c99 -g -fcilkplus" " " + + +dg-runtest [lsort [glob -nocomplain $srcdir/c-c++-common/cilk-plus/CK/*.c]] " -fcilkplus" " " +dg-runtest [lsort [glob -nocomplain $srcdir/c-c++-common/cilk-plus/CK/*.c]] " -O0 -fcilkplus" " " +dg-runtest [lsort [glob -nocomplain $srcdir/c-c++-common/cilk-plus/CK/*.c]] " -O1 -fcilkplus" " " +dg-runtest [lsort [glob -nocomplain $srcdir/c-c++-common/cilk-plus/CK/*.c]] " -O2 -ftree-vectorize -fcilkplus" " " +dg-runtest [lsort [glob -nocomplain $srcdir/c-c++-common/cilk-plus/CK/*.c]] " -O3 -fcilkplus" " " +dg-runtest [lsort [glob -nocomplain $srcdir/c-c++-common/cilk-plus/CK/*.c]] " -g -fcilkplus" " " +dg-runtest [lsort [glob -nocomplain $srcdir/c-c++-common/cilk-plus/CK/*.c]] " -g -O0 -fcilkplus" " " +dg-runtest [lsort [glob -nocomplain $srcdir/c-c++-common/cilk-plus/CK/*.c]] " -g -O1 -fcilkplus" " " +dg-runtest [lsort [glob -nocomplain $srcdir/c-c++-common/cilk-plus/CK/*.c]] " -g -O2 -ftree-vectorize -fcilkplus" " " +dg-runtest [lsort [glob -nocomplain $srcdir/c-c++-common/cilk-plus/CK/*.c]] " -g -O3 -fcilkplus" " " +dg-runtest [lsort [glob -nocomplain $srcdir/c-c++-common/cilk-plus/CK/*.c]] " -O3 -ftree-vectorize -fcilkplus -g" " " +dg-runtest [lsort [glob -nocomplain $srcdir/c-c++-common/cilk-plus/CK/*.c]] " -fcilkplus -std=c99" " " +dg-runtest [lsort [glob -nocomplain $srcdir/c-c++-common/cilk-plus/CK/*.c]] " -fcilkplus -O0 -std=c99" " " +dg-runtest [lsort [glob -nocomplain $srcdir/c-c++-common/cilk-plus/CK/*.c]] " -fcilkplus -O1 -std=c99" " " +dg-runtest [lsort [glob -nocomplain $srcdir/c-c++-common/cilk-plus/CK/*.c]] " -fcilkplus -O2 -ftree-vectorize -std=c99" " " +dg-runtest [lsort [glob -nocomplain $srcdir/c-c++-common/cilk-plus/CK/*.c]] " -fcilkplus -O3 -std=c99" " " +dg-runtest [lsort [glob -nocomplain $srcdir/c-c++-common/cilk-plus/CK/*.c]] " -fcilkplus -g -std=c99" " " +dg-runtest [lsort [glob -nocomplain $srcdir/c-c++-common/cilk-plus/CK/*.c]] " -fcilkplus -g -O0 -std=c99" " " +dg-runtest [lsort [glob -nocomplain $srcdir/c-c++-common/cilk-plus/CK/*.c]] " -fcilkplus -g -O1 -std=c99" " " +dg-runtest [lsort [glob -nocomplain $srcdir/c-c++-common/cilk-plus/CK/*.c]] " -fcilkplus -g -O2 -ftree-vectorize -std=c99" " " +dg-runtest [lsort [glob -nocomplain $srcdir/c-c++-common/cilk-plus/CK/*.c]] " -fcilkplus -g -O3 -std=c99" " " +dg-runtest [lsort [glob -nocomplain $srcdir/c-c++-common/cilk-plus/CK/*.c]] " -O3 -ftree-vectorize -std=c99 -g -fcilkplus" " " +dg-runtest [lsort [glob -nocomplain $srcdir/c-c++-common/cilk-plus/CK/*.c]] " -O0 -flto -g -fcilkplus" " " +dg-runtest [lsort [glob -nocomplain $srcdir/c-c++-common/cilk-plus/CK/*.c]] " -O2 -flto -g -fcilkplus" " " +dg-runtest [lsort [glob -nocomplain $srcdir/c-c++-common/cilk-plus/CK/*.c]] " -O3 -flto -g -fcilkplus" " " dg-finish diff --git a/gcc/tree-inline.h b/gcc/tree-inline.h index a78e4b69f0e..cd327154560 100644 --- a/gcc/tree-inline.h +++ b/gcc/tree-inline.h @@ -131,6 +131,10 @@ typedef struct copy_body_data the originals have been mapped to a value rather than to a variable. */ struct pointer_map_t *debug_map; + + /* Cilk keywords currently need to replace some variables that + ordinary nested functions do not. */ + bool remap_var_for_cilk; } copy_body_data; /* Weights of constructions for estimate_num_insns. */ diff --git a/gcc/tree-pretty-print.c b/gcc/tree-pretty-print.c index 9234706be6d..fe756339f6d 100644 --- a/gcc/tree-pretty-print.c +++ b/gcc/tree-pretty-print.c @@ -2644,6 +2644,15 @@ dump_generic_node (pretty_printer *buffer, tree node, int spc, int flags, dump_block_node (buffer, node, spc, flags); break; + case CILK_SPAWN_STMT: + pp_string (buffer, "_Cilk_spawn "); + dump_generic_node (buffer, TREE_OPERAND (node, 0), spc, flags, false); + break; + + case CILK_SYNC_STMT: + pp_string (buffer, "_Cilk_sync"); + break; + default: NIY; } diff --git a/gcc/tree.def b/gcc/tree.def index 1a2c2663502..399b5af6cbe 100644 --- a/gcc/tree.def +++ b/gcc/tree.def @@ -1271,6 +1271,12 @@ DEFTREECODE (TARGET_OPTION_NODE, "target_option_node", tcc_exceptional, 0) Operand 1 is the annotation id. */ DEFTREECODE (ANNOTATE_EXPR, "annotate_expr", tcc_expression, 2) +/* Cilk spawn statement + Operand 0 is the CALL_EXPR. */ +DEFTREECODE (CILK_SPAWN_STMT, "cilk_spawn_stmt", tcc_statement, 1) + +/* Cilk Sync statement: Does not have any operands. */ +DEFTREECODE (CILK_SYNC_STMT, "cilk_sync_stmt", tcc_statement, 0) /* Local variables: diff --git a/gcc/tree.h b/gcc/tree.h index ab1d0ab2153..cbe8272f646 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -790,6 +790,9 @@ extern void omp_clause_range_check_failed (const_tree, const char *, int, #define CALL_EXPR_RETURN_SLOT_OPT(NODE) \ (CALL_EXPR_CHECK (NODE)->base.private_flag) +/* Cilk keywords accessors. */ +#define CILK_SPAWN_FN(NODE) TREE_OPERAND (CILK_SPAWN_STMT_CHECK (NODE), 0) + /* In a RESULT_DECL, PARM_DECL and VAR_DECL, means that it is passed by invisible reference (and the TREE_TYPE is a pointer to the true type). */ |