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; }
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); }
static tree set_cilk_except_data (tree frame) { tree except_data = cilk_dot (frame, CILK_TI_FRAME_EXCEPTION, 0); tree uresume_fn = builtin_decl_implicit (BUILT_IN_EH_POINTER); tree ret_expr; uresume_fn = build_call_expr (uresume_fn, 1, build_int_cst (integer_type_node, 0)); ret_expr = build2 (MODIFY_EXPR, void_type_node, except_data, uresume_fn); return ret_expr; }
static tree set_cilk_except_flag (tree frame) { tree flags = cilk_dot (frame, CILK_TI_FRAME_FLAGS, 0); flags = build2 (MODIFY_EXPR, void_type_node, flags, build2 (BIT_IOR_EXPR, TREE_TYPE (flags), flags, build_int_cst (TREE_TYPE (flags), CILK_FRAME_EXCEPTING))); return flags; }
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; }
tree cilk_arrow (tree frame_ptr, int field_number, bool volatil) { return cilk_dot (build_simple_mem_ref (frame_ptr), field_number, volatil); }
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; }
void expand_builtin_cilk_detach (tree exp) { rtx_insn *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_arrow (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_to_shwi (DECL_FIELD_OFFSET (cilk_trees[CILK_TI_WORKER_TAIL])) + tree_to_shwi (DECL_FIELD_BIT_OFFSET (cilk_trees[CILK_TI_WORKER_TAIL])) / 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); }