%---------------------------------------------------------------------------% % vim: ft=mercury ts=8 sw=4 sts=4 et %---------------------------------------------------------------------------% % Copyright (C) 2006-2011 The University of Melbourne. % Copyright (C) 2014-2018, 2020, 2022 The Mercury team. % This file is distributed under the terms specified in COPYING.LIB. %---------------------------------------------------------------------------% % % File: par_builtin.m. % Main authors: wangp, pbone. % Stability: low. % % This file is automatically imported, as if via ":- use_module", into every % module in lowlevel parallel grades. It holds the builtin procedures % that the compiler generates implicit calls to when implementing parallel % conjunctions. % % This module is a private part of the Mercury implementation; user modules % should never explicitly import this module. The interface for this module % does not get included in the Mercury library reference manual. % %---------------------------------------------------------------------------% %---------------------------------------------------------------------------% :- module par_builtin. :- interface. :- import_module io. :- type future(T). % Allocate a new future object. A future acts as an intermediary for a % shared variable between parallel conjuncts, when one conjunct produces % the value for other conjuncts. % % The first argument is an integer that refers to a string (via a table) of % the future's name. It is used in threadscope grades. % :- pred new_future(int::in, future(T)::uo) is det. % wait_future(Future, Var): % % Wait until Future is signalled, blocking if necessary. Then set Var % to the value bound to the variable associated with the future. % % wait_future/2 doesn't actually have a side effect. However once it has % returned, get_future/2 is guaranteed to be safe. Therefore it must be % impure to prevent it from being optimized away, especially in (valid) % cases where its output occurs only once in a procedure. % :- impure pred wait_future(future(T)::in, T::out) is det. % get_future(Future, Var): % % Like wait_future, but assumes that the future has been signalled already. % :- pred get_future(future(T)::in, T::out) is det. % signal_future(Future, Value): % % Notify any waiting threads that the variable associated with Future % has been bound to Value. Threads waiting on Future will be woken up, % and any later waits on Future will succeed immediately. A future can % only be signalled once. % % XXX The implementation actually allows a future to be signalled % more than once, provided the signals specify the same value. % :- impure pred signal_future(future(T)::in, T::in) is det. %---------------------------------------------------------------------------% % % These types and predicates are used by the loop control transformation. % :- type loop_control. % Create a loop control structure. % For documentation, see MR_lc_create in mercury_par_builtin.h. % :- pred lc_create(int::in, loop_control::out) is det. % Finish a loop and finalize a loop control structure. % For documentation, see MR_lc_finish in mercury_par_builtin.h. % :- impure pred lc_finish(loop_control::in) is det. % Allocate a free slot from the loop control structure and return it. % For documentation, see MR_lc_try_get_free_slot in mercury_par_builtin.h % This call fails if there is no free slot available. % :- impure pred lc_free_slot(loop_control::in, int::out) is semidet. % Allocate a free slot from the loop control structure and return it. % This call blocks the context until a free slot is available. % :- impure pred lc_wait_free_slot(loop_control::in, int::out) is det. % Finish one iteration of the loop. This call does not return. % For documentation, see MR_lc_join_and_terminate in mercury_par_builtin.h. % :- impure pred lc_join_and_terminate(loop_control::in, int::in) is det. % Get the default number of contexts to use for loop control. % :- impure pred lc_default_num_contexts(int::out) is det. %---------------------------------------------------------------------------% % The following predicates are intended to be used as conditions to decide % whether the conjuncts of a conjunction should be executed in parallel or % in sequence. Success should indicate a preference for parallel execution, % failure a preference for sequential execution. % % They can be used both by compiler transformations and directly in user code. % The latter is useful for testing. % A hook for the compiler's granularity transformation to hang % an arbitrary test on. This predicate does not have a definition; % it is simply used as a hook by the compiler. % :- impure pred evaluate_parallelism_condition is semidet. % Close the file that was used to log the parallel condition decisions. % % The parallel condition stats file is opened the first time it is % required. If it is not open this predicate will do nothing. The recording % of statistics and the management of the parallel condition stats file % is enabled only if both of the following C compiler macros are set: % MR_LL_PARALLEL_CONJ and MR_DEBUG_RUNTIME_GRANULARITY_CONTROL. % This predicate will throw an exception unless both of these are set. % :- pred par_cond_close_stats_file(io::di, io::uo) is det. %---------------------------------------------------------------------------% %---------------------------------------------------------------------------% :- implementation. :- pragma foreign_type("C", future(T), "MR_Future *", [can_pass_as_mercury_type]). % Placeholders only. :- pragma foreign_type("C#", future(T), "object"). :- pragma foreign_type("Java", future(T), "java.lang.Object"). %---------------------------------------------------------------------------% :- pragma foreign_proc("C", new_future(Name::in, Future::uo), [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail, may_not_duplicate], " #ifdef MR_THREADSCOPE MR_par_builtin_new_future(Future, Name); #else MR_par_builtin_new_future(Future); #endif "). :- pragma foreign_proc("C", wait_future(Future::in, Var::out), [will_not_call_mercury, thread_safe, will_not_modify_trail, may_not_duplicate], " MR_par_builtin_wait_future(Future, Var); "). :- pragma foreign_proc("C", get_future(Future::in, Var::out), [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail, may_not_duplicate], " MR_par_builtin_get_future(Future, Var); "). :- pragma foreign_proc("C", signal_future(Future::in, Value::in), [will_not_call_mercury, thread_safe, will_not_modify_trail, may_not_duplicate], " MR_par_builtin_signal_future(Future, Value); "). :- pragma foreign_proc("C", evaluate_parallelism_condition, [will_not_call_mercury, thread_safe], " // All uses of this predicate should override the body. MR_fatal_error(""evaluate_parallelism_condition called""); "). %---------------------------------------------------------------------------% % wait_resume is the piece of code we jump to when a thread suspended % on a future resumes after the future is signalled. % :- pragma foreign_decl("C", " /* INIT mercury_sys_init_par_builtin_modules */ #if !defined(MR_HIGHLEVEL_CODE) && defined(MR_THREAD_SAFE) MR_define_extern_entry(mercury__par_builtin__wait_resume); #endif "). :- pragma foreign_code("C", " #if !defined(MR_HIGHLEVEL_CODE) && defined(MR_THREAD_SAFE) MR_BEGIN_MODULE(hand_written_par_builtin_module) MR_init_entry_ai(mercury__par_builtin__wait_resume); MR_BEGIN_CODE MR_define_entry(mercury__par_builtin__wait_resume); { MR_Future *Future; // Restore the address of the future after resuming. Future = (MR_Future *) MR_sv(1); MR_decr_sp(1); assert(Future->MR_fut_signalled); // Return to the caller of par_builtin.wait. MR_r1 = Future->MR_fut_value; MR_proceed(); } MR_END_MODULE #endif // Forward decls to suppress gcc warnings. void mercury_sys_init_par_builtin_modules_init(void); void mercury_sys_init_par_builtin_modules_init_type_tables(void); #ifdef MR_DEEP_PROFILING void mercury_sys_init_par_builtin_modules_write_out_proc_statics( FILE *deep_fp, FILE *procrep_fp); #endif void mercury_sys_init_par_builtin_modules_init(void) { #if (!defined MR_HIGHLEVEL_CODE) && (defined MR_THREAD_SAFE) hand_written_par_builtin_module(); #endif } void mercury_sys_init_par_builtin_modules_init_type_tables(void) { // No types to register. } #ifdef MR_DEEP_PROFILING void mercury_sys_init_par_builtin_modules_write_out_proc_statics( FILE *deep_fp, FILE *procrep_fp) { // No proc_statics to write out. } #endif "). %---------------------------------------------------------------------------% :- pragma foreign_type("C", loop_control, "MR_LoopControl *", [can_pass_as_mercury_type]). % Placeholders only. :- pragma foreign_type("C#", loop_control, "object"). :- pragma foreign_type("Java", loop_control, "java.lang.Object"). :- pragma foreign_proc("C", lc_create(NumWorkers::in, LC::out), [will_not_call_mercury, will_not_throw_exception, thread_safe, promise_pure], " #if defined(MR_THREAD_SAFE) && defined(MR_LL_PARALLEL_CONJ) LC = MR_lc_create(NumWorkers); #else MR_fatal_error(""lc_create is unavailable in this grade""); #endif "). % IMPORTANT: any changes or additions to external predicates should be % reflected in the definition of pred_is_external in mdbcomp/ % program_representation.m. The debugger needs to know what predicates % are defined externally, so that it knows not to expect events for those % predicates. :- pragma external_pred(lc_finish/1). :- pragma external_pred(lc_wait_free_slot/2). :- pragma foreign_code("C", " void mercury_sys_init_lc_init(void); void mercury_sys_init_lc_init_type_tables(void); #ifdef MR_DEEP_PROFILING void mercury_sys_init_lc_write_out_proc_statics(FILE *deep_fp, FILE *procrep_fp); #endif #ifdef MR_HIGHLEVEL_CODE void MR_CALL mercury__par_builtin__lc_finish_1_p_0(MR_Box lc) { MR_fatal_error(""lc_finish is unavailable with --highlevel-code""); } void MR_CALL mercury__par_builtin__lc_wait_free_slot_2_p_0(MR_Box lc, MR_Integer *slot) { MR_fatal_error(""lc_wait_free_slot is unavailable with --highlevel-code""); } #else // ! MR_HIGHLEVEL_CODE MR_def_extern_entry(par_builtin__lc_finish_1_0) MR_def_extern_entry(par_builtin__lc_wait_free_slot_2_0) MR_decl_label1(par_builtin__lc_finish_1_0, 1) MR_BEGIN_MODULE(par_builtin_module_lc_finish) MR_init_entry1(par_builtin__lc_finish_1_0); MR_INIT_PROC_LAYOUT_ADDR(mercury__par_builtin__lc_finish_1_0); MR_init_label1(par_builtin__lc_finish_1_0,1); MR_BEGIN_CODE #ifdef MR_maybe_local_thread_engine_base #undef MR_maybe_local_thread_engine_base #define MR_maybe_local_thread_engine_base MR_local_thread_engine_base #endif MR_define_entry(mercury__par_builtin__lc_finish_1_0) MR_MAYBE_INIT_LOCAL_THREAD_ENGINE_BASE MR_incr_sp(1); MR_sv(1) = MR_r1; // LC #if defined(MR_THREAD_SAFE) && defined(MR_LL_PARALLEL_CONJ) { MR_LoopControl *LC; LC = (MR_LoopControl *) MR_r1; MR_lc_finish_part1(LC, MR_LABEL_AP(par_builtin__lc_finish_1_0_i1)); } #else MR_fatal_error(""lc_finish is unavailable in this grade""); #endif MR_def_label(par_builtin__lc_finish_1_0,1) MR_MAYBE_INIT_LOCAL_THREAD_ENGINE_BASE #if defined(MR_THREAD_SAFE) && defined(MR_LL_PARALLEL_CONJ) { MR_LoopControl *LC; LC = (MR_LoopControl *) MR_sv(1); MR_lc_finish_part2(LC); } #endif MR_decr_sp(1); MR_proceed(); #ifdef MR_maybe_local_thread_engine_base #undef MR_maybe_local_thread_engine_base #define MR_maybe_local_thread_engine_base MR_thread_engine_base #endif MR_END_MODULE MR_decl_label1(par_builtin__lc_wait_free_slot_2_0, 1) MR_BEGIN_MODULE(par_builtin_module_lc_wait_free_slot) MR_init_entry1(par_builtin__lc_wait_free_slot_2_0); MR_INIT_PROC_LAYOUT_ADDR(mercury__par_builtin__lc_wait_free_slot_2_0); MR_init_label1(par_builtin__lc_wait_free_slot_2_0,1); MR_BEGIN_CODE #ifdef MR_maybe_local_thread_engine_base #undef MR_maybe_local_thread_engine_base #define MR_maybe_local_thread_engine_base MR_local_thread_engine_base #endif MR_define_entry(mercury__par_builtin__lc_wait_free_slot_2_0) MR_MAYBE_INIT_LOCAL_THREAD_ENGINE_BASE MR_incr_sp(1); MR_sv(1) = MR_r1; // LC // LC must be saved to the stack so that we can resume from the label below // and retrieve it. MR_def_label(par_builtin__lc_wait_free_slot_2_0,1) MR_MAYBE_INIT_LOCAL_THREAD_ENGINE_BASE #if defined(MR_LL_PARALLEL_CONJ) { MR_LoopControl *lc; MR_Unsigned lcs_idx; lc = (MR_LoopControl *) MR_sv(1); MR_lc_wait_free_slot(lc, lcs_idx, MR_LABEL_AP(par_builtin__lc_wait_free_slot_2_0_i1)); MR_r1 = (MR_Word)lcs_idx; } #else MR_fatal_error(""lc_wait_free_slot is unavailable in this grade""); #endif MR_decr_sp(1); MR_proceed(); #ifdef MR_maybe_local_thread_engine_base #undef MR_maybe_local_thread_engine_base #define MR_maybe_local_thread_engine_base MR_thread_engine_base #endif MR_END_MODULE #endif // ! MR_HIGHLEVEL_CODE // Module initialization. /* INIT mercury_sys_init_lc */ void mercury_sys_init_lc_init(void) { #ifndef MR_HIGHLEVEL_CODE par_builtin_module_lc_finish(); par_builtin_module_lc_wait_free_slot(); #endif } void mercury_sys_init_lc_init_type_tables(void) { // No types to register. } #ifdef MR_DEEP_PROFILING void mercury_sys_init_lc_write_out_proc_statics(FILE *deep_fp, FILE *procrep_fp) { // The deep profiler shouldn't notice loop control predicates. } #endif "). :- pragma foreign_proc("C", lc_free_slot(LC::in, LCS::out), [will_not_call_mercury, will_not_throw_exception, thread_safe], " #if defined(MR_THREAD_SAFE) && defined(MR_LL_PARALLEL_CONJ) { MR_Unsigned LCS_0; SUCCESS_INDICATOR = MR_lc_try_get_free_slot(LC, &LCS_0); LCS = (MR_Integer)LCS_0; } #else MR_fatal_error(""lc_free_slot is unavailable in this grade""); #endif "). :- pragma foreign_proc("C", lc_join_and_terminate(LC::in, LCS::in), [will_not_call_mercury, will_not_throw_exception, thread_safe], " #if defined(MR_THREAD_SAFE) && defined(MR_LL_PARALLEL_CONJ) MR_lc_join_and_terminate(LC, LCS); #else MR_fatal_error(""lc_join_and_terminate is unavailable in this grade""); #endif "). :- pragma foreign_proc("C", lc_default_num_contexts(NumContexts::out), [will_not_call_mercury, will_not_throw_exception, thread_safe], " NumContexts = MR_num_contexts_per_loop_control; "). %---------------------------------------------------------------------------% :- pragma foreign_proc("C", par_cond_close_stats_file(_IO0::di, _IO::uo), [will_not_call_mercury, thread_safe, promise_pure, tabled_for_io], " #if defined(MR_LL_PARALLEL_CONJ) && \ defined(MR_DEBUG_RUNTIME_GRANULARITY_CONTROL) MR_write_out_conditional_parallelism_log(); #else MR_fatal_error(""par_cond_close_stats_file is unavailable in this grade""); #endif "). %---------------------------------------------------------------------------% :- end_module par_builtin. %---------------------------------------------------------------------------%