Files
mercury/compiler/ml_commit_gen.m
Zoltan Somogyi ea4f95a7ed Use var_tables in lco.m, and when dumping goals.
Since this is the first converted module that dumps out goals when
debugging trace flags are enabled, this required generalizing the code
that does that, to take either varsets or var_tables as a means of
specifying the names of variables. We do this via a new type,
var_name_source, which contains either a varset or a var_table.

Almost all of this diff is there to implement this generalization.
A large part of it affects code in the parse_tree package that we use
to write out the parts of HLDS goals that are defined by types defined
in that package. Since we want to avoid making any part of the parse_tree
package dependent on the hlds package, this required defining the
var_name_source type in the parse_tree package, which in turn requires
var_table.m to be in that same package.

compiler/lco.m:
    Convert this module to use var_tables instead of varsets and vartypes.

compiler/var_table.m:
    Move this module from the hlds package to the parse_tree package.

    To make this, possible, move the parts that required access to the HLDS
    to hlds_pred.m, from where it was usually invoked.

    Export some utility predicates to allow the moved code to work
    in hlds_pred.m without access to the actual definition of the
    var_table type.

    Define the var_name_source type.

    Add some utility functions for use by code writing out variable names.

compiler/hlds_pred.m:
    Add the code moved from var_table.m.

compiler/vartypes.m:
    Move this module from the hlds package to the parse_tree package,
    for symmetry with var_table.m. It did not depend on being in hlds
    in any way.

compiler/hlds.m:
compiler/parse_tree.m:
    Move vartypes.m and var_table.m from the hlds package
    to the parse_tree package.

compiler/hlds_out_goal.m:
    Change all the predicates in this module to take a var_name_source
    instead of a prog_varset.

    Fix some comments.

compiler/hlds_out_util.m:
    Change some of the predicates in this module (those called from
    hlds_out_goal.m) to take a var_name_source instead of a prog_varset.

compiler/parse_tree_out_term.m:
    Provide variants of some existing predicates and functions that take
    var_name_sources instead of varsets. The code of the copies
    duplicates the logic of the originals, though I hope that this
    duplication can be done away with at the end of the transition.
    (The best solution would be to use a typeclass with methods
    that convert vars to their names, but we would want to ensure
    that the compiler can specialize all the affected predicates
    and functions to the two instances of this typeclass, which is
    something that we cannot do yet. In the meantime, the lack of
    any generalization in the old versions preserves their performance.)

tools/sort_imports:
tools/filter_sort_imports:
    A new tool that automatically sorts any occurrences of consecutive
    ":- import_module" declarations in the named files. The sorting is done
    in filter_sort_imports; sort_imports loops over the named files.

    After automatically replacing all occurrences of hlds.{vartypes,var_table}
    in import_module declarations with their parse_tree versions, the updated
    import_module declarations were usually out of order with respect to
    their neighbours. I used this script to fix that, and some earlier
    out-of-order imports.

compiler/accumulator.m:
compiler/add_class.m:
compiler/add_clause.m:
compiler/add_foreign_proc.m:
compiler/add_heap_ops.m:
compiler/add_pragma_type_spec.m:
compiler/add_pred.m:
compiler/add_trail_ops.m:
compiler/analysis.m:
compiler/arg_info.m:
compiler/build_mode_constraints.m:
compiler/bytecode_gen.m:
compiler/call_gen.m:
compiler/check_promise.m:
compiler/closure_analysis.m:
compiler/closure_gen.m:
compiler/code_info.m:
compiler/code_loc_dep.m:
compiler/common.m:
compiler/compile_target_code.m:
compiler/complexity.m:
compiler/const_prop.m:
compiler/constraint.m:
compiler/continuation_info.m:
compiler/convert_parse_tree.m:
compiler/coverage_profiling.m:
compiler/cse_detection.m:
compiler/ctgc.datastruct.m:
compiler/ctgc.util.m:
compiler/dead_proc_elim.m:
compiler/deep_profiling.m:
compiler/deforest.m:
compiler/delay_construct.m:
compiler/delay_partial_inst.m:
compiler/dep_par_conj.m:
compiler/det_analysis.m:
compiler/det_report.m:
compiler/det_util.m:
compiler/direct_arg_in_out.m:
compiler/disj_gen.m:
compiler/distance_granularity.m:
compiler/equiv_type_hlds.m:
compiler/exception_analysis.m:
compiler/file_names.m:
compiler/float_regs.m:
compiler/follow_vars.m:
compiler/format_call.m:
compiler/generate_dep_d_files.m:
compiler/get_dependencies.m:
compiler/goal_expr_to_goal.m:
compiler/goal_mode.m:
compiler/goal_path.m:
compiler/goal_store.m:
compiler/goal_util.m:
compiler/granularity.m:
compiler/hhf.m:
compiler/higher_order.m:
compiler/hlds_clauses.m:
compiler/hlds_code_util.m:
compiler/hlds_error_util.m:
compiler/hlds_goal.m:
compiler/hlds_llds.m:
compiler/hlds_out_pred.m:
compiler/hlds_rtti.m:
compiler/hlds_statistics.m:
compiler/inlining.m:
compiler/inst_check.m:
compiler/inst_test.m:
compiler/inst_user.m:
compiler/instance_method_clauses.m:
compiler/instmap.m:
compiler/intermod.m:
compiler/intermod_analysis.m:
compiler/interval.m:
compiler/introduce_exists_casts.m:
compiler/introduce_parallelism.m:
compiler/item_util.m:
compiler/lambda.m:
compiler/live_vars.m:
compiler/liveness.m:
compiler/llds.m:
compiler/llds_out_data.m:
compiler/llds_out_file.m:
compiler/llds_out_util.m:
compiler/lookup_switch.m:
compiler/loop_inv.m:
compiler/make.module_target.m:
compiler/make.util.m:
compiler/make_goal.m:
compiler/make_hlds_separate_items.m:
compiler/make_hlds_types.m:
compiler/mark_tail_calls.m:
compiler/mercury_compile_mlds_back_end.m:
compiler/middle_rec.m:
compiler/ml_accurate_gc.m:
compiler/ml_args_util.m:
compiler/ml_call_gen.m:
compiler/ml_closure_gen.m:
compiler/ml_code_gen.m:
compiler/ml_code_util.m:
compiler/ml_commit_gen.m:
compiler/ml_disj_gen.m:
compiler/ml_foreign_proc_gen.m:
compiler/ml_gen_info.m:
compiler/ml_lookup_switch.m:
compiler/ml_proc_gen.m:
compiler/ml_simplify_switch.m:
compiler/ml_switch_gen.m:
compiler/ml_tag_switch.m:
compiler/ml_unify_gen.m:
compiler/ml_unify_gen_construct.m:
compiler/ml_unify_gen_deconstruct.m:
compiler/ml_unify_gen_test.m:
compiler/ml_unify_gen_util.m:
compiler/mlds_to_c_data.m:
compiler/mlds_to_c_func.m:
compiler/mlds_to_c_global.m:
compiler/mlds_to_cs_class.m:
compiler/mlds_to_cs_file.m:
compiler/mlds_to_java_data.m:
compiler/mlds_to_java_file.m:
compiler/mlds_to_java_stmt.m:
compiler/mlds_to_java_type.m:
compiler/mmc_analysis.m:
compiler/mode_comparison.m:
compiler/mode_constraints.m:
compiler/mode_debug.m:
compiler/mode_errors.m:
compiler/mode_info.m:
compiler/mode_ordering.m:
compiler/modecheck_call.m:
compiler/modecheck_coerce.m:
compiler/modecheck_goal.m:
compiler/modecheck_unify.m:
compiler/modecheck_util.m:
compiler/modes.m:
compiler/module_cmds.m:
compiler/old_type_constraints.m:
compiler/opt_debug.m:
compiler/optimize.m:
compiler/options_file.m:
compiler/ordering_mode_constraints.m:
compiler/par_loop_control.m:
compiler/parse_item.m:
compiler/parse_string_format.m:
compiler/parse_tree_out_inst.m:
compiler/parse_tree_to_term.m:
compiler/parse_util.m:
compiler/pd_debug.m:
compiler/pd_info.m:
compiler/pd_util.m:
compiler/peephole.m:
compiler/polymorphism.m:
compiler/polymorphism_info.m:
compiler/polymorphism_lambda.m:
compiler/polymorphism_type_class_info.m:
compiler/polymorphism_type_info.m:
compiler/post_typecheck.m:
compiler/pragma_c_gen.m:
compiler/pred_name.m:
compiler/pred_table.m:
compiler/prog_item.m:
compiler/prog_rep.m:
compiler/prop_mode_constraints.m:
compiler/purity.m:
compiler/push_goals_together.m:
compiler/qual_info.m:
compiler/quantification.m:
compiler/rbmm.execution_path.m:
compiler/rbmm.m:
compiler/rbmm.points_to_analysis.m:
compiler/rbmm.points_to_graph.m:
compiler/rbmm.points_to_info.m:
compiler/rbmm.region_resurrection_renaming.m:
compiler/rbmm.region_transformation.m:
compiler/recompilation.used_file.m:
compiler/recompilation.version.m:
compiler/recompute_instmap_deltas.m:
compiler/resolve_unify_functor.m:
compiler/rtti.m:
compiler/rtti_out.m:
compiler/rtti_to_mlds.m:
compiler/saved_vars.m:
compiler/set_of_var.m:
compiler/simplify_goal_call.m:
compiler/simplify_goal_conj.m:
compiler/simplify_goal_disj.m:
compiler/simplify_goal_ite.m:
compiler/simplify_goal_scope.m:
compiler/simplify_goal_switch.m:
compiler/simplify_goal_unify.m:
compiler/simplify_info.m:
compiler/simplify_proc.m:
compiler/size_prof.m:
compiler/smm_common.m:
compiler/ssdebug.m:
compiler/stack_alloc.m:
compiler/stack_layout.m:
compiler/stack_opt.m:
compiler/stm_expand.m:
compiler/store_alloc.m:
compiler/structure_reuse.analysis.m:
compiler/structure_reuse.direct.choose_reuse.m:
compiler/structure_reuse.direct.detect_garbage.m:
compiler/structure_reuse.domain.m:
compiler/structure_reuse.indirect.m:
compiler/structure_reuse.lbu.m:
compiler/structure_reuse.lfu.m:
compiler/structure_sharing.analysis.m:
compiler/structure_sharing.domain.m:
compiler/superhomogeneous.m:
compiler/switch_detection.m:
compiler/switch_gen.m:
compiler/switch_util.m:
compiler/table_gen.m:
compiler/tabling_analysis.m:
compiler/term_constr_build.m:
compiler/term_constr_data.m:
compiler/term_constr_initial.m:
compiler/term_constr_main.m:
compiler/term_constr_main_types.m:
compiler/term_constr_util.m:
compiler/term_pass1.m:
compiler/term_traversal.m:
compiler/term_util.m:
compiler/trace_gen.m:
compiler/trailing_analysis.m:
compiler/transform_llds.m:
compiler/try_expand.m:
compiler/tupling.m:
compiler/type_assign.m:
compiler/type_ctor_info.m:
compiler/type_util.m:
compiler/typecheck.m:
compiler/typecheck_debug.m:
compiler/typecheck_errors.m:
compiler/typecheck_info.m:
compiler/unify_gen_construct.m:
compiler/unify_gen_deconstruct.m:
compiler/unify_proc.m:
compiler/unique_modes.m:
compiler/unneeded_code.m:
compiler/untupling.m:
compiler/unused_args.m:
compiler/unused_imports.m:
compiler/var_locn.m:
compiler/write_deps_file.m:
compiler/write_module_interface_files.m:
    Conform to the changes above.
2022-04-18 02:00:38 +10:00

535 lines
21 KiB
Mathematica

%---------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%---------------------------------------------------------------------------%
% Copyright (C) 2009, 2011 The University of Melbourne.
% This file may only be copied under the terms of the GNU General
% Public License - see the file COPYING in the Mercury distribution.
%---------------------------------------------------------------------------%
%
% File: ml_commit_gen.m.
% Main author: fjh.
%
% This module handles code generation for commits.
%
% There are several different ways of handling commits:
% - using catch/throw
% - using setjmp/longjmp
% - using GCC's __builtin_setjmp/__builtin_longjmp
% - exiting nested functions via gotos to their containing functions
%
% The MLDS data structure abstracts away these differences using the
% `try_commit' and `do_commit' instructions. The comments below show
% the MLDS try_commit/do_commit version first, but for clarity I've also
% included sample code using each of the three different techniques.
% This shows how the MLDS->target back-end can map mlds_commit_type,
% do_commit and try_commit into target language constructs.
%
% Note that if we are using GCC's __builtin_longjmp(), then it is important
% that the call to __builtin_longjmp() be put in its own function, to ensure
% that it is not in the same function as the __builtin_setjmp(). The code
% generation schema below does that automatically. We will need to be careful
% with MLDS optimizations to ensure that we preserve that invariant, though.
% (Alternatively, we could just call a function that calls __builtin_longjmp()
% rather than calling it directly. But that would be a little less efficient.)
%
% If those methods turn out to be too inefficient, another alternative would be
% to change the generated code so that after every function call, it would
% check a flag, and if that flag was set, it would return. Then MR_DO_COMMIT
% would just set the flag and return. The flag could be in a global
% (or thread-local) variable, or it could be an additional value returned
% from each function.
%
% model_non in semi context: (using try_commit/do_commit)
% <succeeded = Goal>
% ===>
% MR_COMMIT_TYPE ref;
% void success() {
% MR_DO_COMMIT(ref);
% }
% MR_TRY_COMMIT(ref, {
% <Goal && success()>
% succeeded = MR_FALSE;
% }, {
% succeeded = MR_TRUE;
% })
%
% model_non in semi context: (using catch/throw)
% <succeeded = Goal>
% ===>
% void success() {
% throw COMMIT();
% }
% try {
% <Goal && success()>
% succeeded = MR_FALSE;
% } catch (COMMIT) {
% succeeded = MR_TRUE;
% }
%
% The above is using C++ syntax. Here COMMIT is an exception type, which
% can be defined trivially (e.g. "class COMMIT {};"). Note that when using
% catch/throw, we don't need the "ref" argument at all; the target language's
% exception handling implementation keeps track of all the information needed
% to unwind the stack.
%
% model_non in semi context: (using setjmp/longjmp)
% <succeeded = Goal>
% ===>
% jmp_buf ref;
% void success() {
% longjmp(ref, 1);
% }
% if (setjmp(ref)) {
% succeeded = MR_TRUE;
% } else {
% <Goal && success()>
% succeeded = MR_FALSE;
% }
%
% model_non in semi context: (using GNU C nested functions,
% GNU C local labels, and exiting
% the nested function by a goto
% to a label in the containing function)
% <succeeded = Goal>
% ===>
% __label__ commit;
% void success() {
% goto commit;
% }
% <Goal && success()>
% succeeded = MR_FALSE;
% goto commit_done;
% commit:
% succeeded = MR_TRUE;
% commit_done:
% ;
%
% model_non in det context: (using try_commit/do_commit)
% <do Goal>
% ===>
% MR_COMMIT_TYPE ref;
% void success() {
% MR_DO_COMMIT(ref);
% }
% MR_TRY_COMMIT(ref, {
% <Goal && success()>
% }, {})
%
% model_non in det context (using GNU C nested functions,
% GNU C local labels, and exiting
% the nested function by a goto
% to a label in the containing function)
% <do Goal>
% ===>
% __label__ done;
% void success() {
% goto done;
% }
% <Goal && success()>
% done: ;
%
% model_non in det context (using catch/throw):
% <do Goal>
% ===>
% void success() {
% throw COMMIT();
% }
% try {
% <Goal && success()>
% } catch (COMMIT) {}
%
% model_non in det context (using setjmp/longjmp):
% <do Goal>
% ===>
% jmp_buf ref;
% void success() {
% longjmp(ref, 1);
% }
% if (setjmp(ref) == 0) {
% <Goal && success()>
% }
%
% Note that for all of these versions, we must hoist any static declarations
% generated for <Goal> out to the top level; this is needed so that such
% declarations remain in scope for any following goals.
:- module ml_backend.ml_commit_gen.
:- interface.
:- import_module hlds.
:- import_module hlds.code_model.
:- import_module hlds.hlds_goal.
:- import_module ml_backend.ml_gen_info.
:- import_module ml_backend.mlds.
:- import_module parse_tree.
:- import_module parse_tree.prog_data.
:- import_module list.
% Generate code for a commit.
%
:- pred ml_gen_commit(hlds_goal::in, code_model::in, prog_context::in,
list(mlds_local_var_defn)::out, list(mlds_function_defn)::out,
list(mlds_stmt)::out, ml_gen_info::in, ml_gen_info::out) is det.
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
:- implementation.
:- import_module ml_backend.ml_accurate_gc.
:- import_module ml_backend.ml_code_gen.
:- import_module ml_backend.ml_code_util.
:- import_module parse_tree.prog_type.
:- import_module parse_tree.set_of_var.
:- import_module parse_tree.var_table.
:- import_module bool.
:- import_module map.
:- import_module maybe.
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
ml_gen_commit(Goal, CodeModel, Context, LocalVarDefns, FuncDefns, Stmts,
!Info) :-
Goal = hlds_goal(_, GoalInfo),
GoalCodeModel = goal_info_get_code_model(GoalInfo),
GoalContext = goal_info_get_context(GoalInfo),
( if
GoalCodeModel = model_non,
CodeModel = model_semi
then
% model_non in semi context: (using try_commit/do_commit)
% <succeeded = Goal>
% ===>
% MR_bool succeeded;
% #ifdef NONDET_COPY_OUT
% <local var decls>
% #endif
% #ifdef PUT_COMMIT_IN_OWN_FUNC
% /*
% ** to avoid problems with setjmp() and non-volatile
% ** local variables, we need to put the call to
% ** setjmp() in its own nested function
% */
% void commit_func()
% {
% #endif
% MR_COMMIT_TYPE ref;
%
% void success() {
% MR_DO_COMMIT(ref);
% }
%
% MR_TRY_COMMIT(ref, {
% <Goal && success()>
% succeeded = MR_FALSE;
% }, {
% #ifdef NONDET_COPY_OUT
% <copy local vars to output args>
% #endif
% succeeded = MR_TRUE;
% })
% #ifdef PUT_COMMIT_IN_OWN_FUNC
%
% commit_func();
% #endif
ml_gen_info_get_var_lvals(!.Info, OrigVarLvalMap),
ml_gen_maybe_make_locals_for_output_args(GoalInfo,
OutputArgLocalVarDefns, CopyLocalsToOutputArgs, !Info),
% Generate the `success()' function.
ml_gen_new_func_label(no, SuccessFuncLabel, SuccessFuncLabelRval,
!Info),
% Push nesting level.
ml_gen_info_new_aux_var_name(mcav_commit, CommitRef, !Info),
CommitRefLval = ml_local_var(CommitRef, mlds_commit_type),
CommitRefDefn = ml_gen_commit_var_decl(Context, CommitRef),
DoCommitStmt = ml_stmt_do_commit(ml_lval(CommitRefLval), Context),
% Pop nesting level.
ml_gen_nondet_label_func(!.Info, SuccessFuncLabel, Context,
DoCommitStmt, SuccessFuncDefn),
ml_get_env_ptr(EnvPtrRval),
SuccessCont = success_cont(SuccessFuncLabelRval, EnvPtrRval, []),
ml_gen_info_push_success_cont(SuccessCont, !Info),
% Do not take any information about packed args into the new function
% depth, since that may cause dangling cross-function references
% when the new function depth is flattened out.
ml_gen_info_set_packed_word_map(map.init, !Info),
ml_gen_info_increment_func_nest_depth(!Info),
ml_gen_goal(model_non, Goal, GoalLocalVarDefns, GoalFuncDefns,
GoalStmts, !Info),
GoalStmt = ml_gen_block(GoalLocalVarDefns, GoalFuncDefns, GoalStmts,
GoalContext),
ml_gen_info_decrement_func_nest_depth(!Info),
% Do not take any information about packed args out of the new function
% depth, for the same reason.
ml_gen_info_set_packed_word_map(map.init, !Info),
ml_gen_info_pop_success_cont(!Info),
ml_gen_set_success(ml_const(mlconst_false), Context, SetSuccessFalse,
!Info),
ml_gen_set_success(ml_const(mlconst_true), Context, SetSuccessTrue,
!Info),
TryCommitStmt = ml_stmt_try_commit(CommitRefLval,
ml_gen_block([], [], [GoalStmt, SetSuccessFalse], Context),
ml_gen_block([], [], CopyLocalsToOutputArgs ++ [SetSuccessTrue],
Context),
Context
),
maybe_put_commit_in_own_func(CommitRefDefn, SuccessFuncDefn,
[TryCommitStmt], Context,
CommitFuncLocalVarDefns, CommitFuncFuncDefns, Stmts, !Info),
LocalVarDefns = OutputArgLocalVarDefns ++ CommitFuncLocalVarDefns,
FuncDefns = CommitFuncFuncDefns,
ml_gen_info_set_var_lvals(OrigVarLvalMap, !Info)
else if
GoalCodeModel = model_non,
CodeModel = model_det
then
% model_non in det context: (using try_commit/do_commit)
% <do Goal>
% ===>
% #ifdef NONDET_COPY_OUT
% <local var decls>
% #endif
% #ifdef PUT_COMMIT_IN_NESTED_FUNC
% /*
% ** to avoid problems with setjmp() and non-volatile
% ** local variables, we need to put the call to
% ** setjmp() in its own nested functions
% */
% void commit_func()
% {
% #endif
% MR_COMMIT_TYPE ref;
% void success() {
% MR_DO_COMMIT(ref);
% }
% MR_TRY_COMMIT(ref, {
% <Goal && success()>
% }, {
% #ifdef NONDET_COPY_OUT
% <copy local vars to output args>
% #endif
% })
% #ifdef PUT_COMMIT_IN_NESTED_FUNC
%
% commit_func();
% #endif
ml_gen_info_get_var_lvals(!.Info, OrigVarLvalMap),
ml_gen_maybe_make_locals_for_output_args(GoalInfo,
OutputArgLocalVarDefns, CopyLocalsToOutputArgs, !Info),
% Generate the `success()' function.
ml_gen_new_func_label(no, SuccessFuncLabel, SuccessFuncLabelRval,
!Info),
% push nesting level
ml_gen_info_new_aux_var_name(mcav_commit, CommitRef, !Info),
CommitRefLval = ml_local_var(CommitRef, mlds_commit_type),
CommitRefDefn = ml_gen_commit_var_decl(Context, CommitRef),
DoCommitStmt = ml_stmt_do_commit(ml_lval(CommitRefLval), Context),
% pop nesting level
ml_gen_nondet_label_func(!.Info, SuccessFuncLabel, Context,
DoCommitStmt, SuccessFuncDefn),
ml_get_env_ptr(EnvPtrRval),
SuccessCont = success_cont(SuccessFuncLabelRval, EnvPtrRval, []),
ml_gen_info_push_success_cont(SuccessCont, !Info),
ml_gen_info_increment_func_nest_depth(!Info),
ml_gen_goal(model_non, Goal, GoalLocalVarDefns, GoalFuncDefns,
GoalStmts, !Info),
ml_gen_info_decrement_func_nest_depth(!Info),
% Hoist any static constant declarations for Goal out to the top level.
GoalStmt = ml_gen_block(GoalLocalVarDefns, GoalFuncDefns, GoalStmts,
GoalContext),
ml_gen_info_pop_success_cont(!Info),
TryCommitStmt = ml_stmt_try_commit(CommitRefLval, GoalStmt,
ml_gen_block([], [], CopyLocalsToOutputArgs, Context), Context),
maybe_put_commit_in_own_func(CommitRefDefn, SuccessFuncDefn,
[TryCommitStmt], Context,
CommitFuncLocalVarDefns, CommitFuncFuncDefns, Stmts, !Info),
LocalVarDefns = OutputArgLocalVarDefns ++ CommitFuncLocalVarDefns,
FuncDefns = CommitFuncFuncDefns,
ml_gen_info_set_var_lvals(OrigVarLvalMap, !Info)
else
% No commit required.
ml_gen_goal(CodeModel, Goal, LocalVarDefns, FuncDefns, Stmts, !Info)
).
% maybe_put_commit_in_own_func(LocalVarDefs0, FuncDefn0,
% Stmts0, Defns, Stmts):
%
% If the --put-commit-in-own-func option is set, put the commit in its
% own function. This is needed for the high-level C back-end, to handle
% problems with setjmp()/longjmp() clobbering non-volatile local variables.
%
% Detailed explanation:
%
% For the high-level C back-end, we implement commits using
% setjmp()/longjmp(). Unfortunately for us, ANSI/ISO C says that longjmp()
% is allowed to clobber the values of any non-volatile local variables
% in the function that called setjmp() which have been modified between
% the setjmp() and the longjmp().
%
% To avoid this, whenever we generate a commit, we put it in its own
% nested function, with the local variables (e.g. `succeeded', plus any
% outputs from the goal that we are committing over) remaining in the
% containing function. This ensures that none of the variables which
% get modified between the setjmp() and the longjmp() and which get
% referenced after the longjmp() are local variables in the function
% containing the setjmp().
%
% [The obvious alternative of declaring the local variables in the function
% containing setjmp() as `volatile' doesn't work, since the assignments
% to those output variables may be deep in some function called indirectly
% from the goal that we are committing across, and assigning to a
% volatile-qualified variable via a non-volatile pointer is undefined
% behaviour. The only way to make it work would be to be to declare
% *every* output argument that we pass by reference as `volatile T *'.
% But that would impose distributed fat and would make interoperability
% difficult.]
%
:- pred maybe_put_commit_in_own_func(
mlds_local_var_defn::in, mlds_function_defn::in, list(mlds_stmt)::in,
prog_context::in,
list(mlds_local_var_defn)::out, list(mlds_function_defn)::out,
list(mlds_stmt)::out, ml_gen_info::in, ml_gen_info::out) is det.
maybe_put_commit_in_own_func(LocalVarDefn0, FuncDefn0, TryCommitStmts,
Context, LocalVarDefns, FuncDefns, Stmts, !Info) :-
ml_gen_info_put_commit_in_own_func(!.Info, PutCommitInOwnFunc),
(
PutCommitInOwnFunc = yes,
% Generate the `void commit_func() { ... }' wrapper
% around the main body that we generated above.
ml_gen_new_func_label(no, CommitFuncLabel, CommitFuncLabelRval, !Info),
% Push nesting level.
CommitFuncBody = ml_gen_block([LocalVarDefn0], [FuncDefn0],
TryCommitStmts, Context),
% Pop nesting level.
ml_gen_nondet_label_func(!.Info, CommitFuncLabel, Context,
CommitFuncBody, CommitFuncDefn),
% Generate the call to `commit_func();'
ml_get_env_ptr(EnvPtrRval),
ArgRvals = [EnvPtrRval],
ArgTypes = [mlds_generic_env_ptr_type],
RetTypes = [],
Signature = mlds_func_signature(ArgTypes, RetTypes),
CallKind = ordinary_call,
CallStmt = ml_stmt_call(Signature, CommitFuncLabelRval, ArgRvals,
[], CallKind, Context),
% Package it all up.
LocalVarDefns = [],
FuncDefns = [CommitFuncDefn],
Stmts = [CallStmt]
;
PutCommitInOwnFunc = no,
LocalVarDefns = [LocalVarDefn0],
FuncDefns = [FuncDefn0],
Stmts = TryCommitStmts
).
% In commits, you have model_non code called from a model_det or model_semi
% context. With --nondet-copy-out, when generating code for commits,
% if the context is a model_det or model_semi procedure with output
% arguments passed by reference, then we need to introduce local variables
% corresponding to those output arguments, and at the end of the commit
% we will copy the local variables into the output arguments.
%
:- pred ml_gen_maybe_make_locals_for_output_args(hlds_goal_info::in,
list(mlds_local_var_defn)::out, list(mlds_stmt)::out,
ml_gen_info::in, ml_gen_info::out) is det.
ml_gen_maybe_make_locals_for_output_args(GoalInfo, LocalVarDecls,
CopyLocalsToOutputArgs, !Info) :-
ml_gen_info_get_nondet_copy_out(!.Info, NondetCopyOut),
(
NondetCopyOut = yes,
Context = goal_info_get_context(GoalInfo),
NonLocals = goal_info_get_nonlocals(GoalInfo),
ml_gen_info_get_byref_output_vars(!.Info, ByRefOutputVars),
VarsToCopy = set_of_var.intersect(ByRefOutputVars, NonLocals),
ml_gen_make_locals_for_output_args(
set_of_var.to_sorted_list(VarsToCopy),
Context, LocalVarDecls, CopyLocalsToOutputArgs, !Info)
;
NondetCopyOut = no,
LocalVarDecls = [],
CopyLocalsToOutputArgs = []
).
:- pred ml_gen_make_locals_for_output_args(list(prog_var)::in,
prog_context::in, list(mlds_local_var_defn)::out, list(mlds_stmt)::out,
ml_gen_info::in, ml_gen_info::out) is det.
ml_gen_make_locals_for_output_args([], _, [], [], !Info).
ml_gen_make_locals_for_output_args([Var | Vars], Context,
LocalDefns, Assigns, !Info) :-
ml_gen_make_locals_for_output_args(Vars, Context,
LocalDefnsTail, AssignsTail, !Info),
ml_gen_info_get_var_table(!.Info, VarTable),
lookup_var_entry(VarTable, Var, Entry),
IsDummy = Entry ^ vte_is_dummy,
(
IsDummy = is_dummy_type,
LocalDefns = LocalDefnsTail,
Assigns = AssignsTail
;
IsDummy = is_not_dummy_type,
ml_gen_make_local_for_output_arg(Var, Entry, Context,
HeadLocalDefn, HeadAssign, !Info),
LocalDefns = [HeadLocalDefn | LocalDefnsTail],
Assigns = [HeadAssign | AssignsTail]
).
:- pred ml_gen_make_local_for_output_arg(prog_var::in, var_table_entry::in,
prog_context::in, mlds_local_var_defn::out, mlds_stmt::out,
ml_gen_info::in, ml_gen_info::out) is det.
ml_gen_make_local_for_output_arg(OutputVar, OutputVarEntry, Context,
LocalVarDefn, Assign, !Info) :-
% Look up the name of the output variable.
OutputVarName = ml_gen_local_var_name(OutputVar, OutputVarEntry),
% Generate a declaration for a corresponding local variable.
OutputVarName = lvn_prog_var(OutputVarNameStr, MaybeNum),
LocalVarName = lvn_local_var(OutputVarNameStr, MaybeNum),
Type = OutputVarEntry ^ vte_type,
ml_gen_mlds_type(!.Info, Type, MLDS_Type),
ml_gen_gc_statement(LocalVarName, Type, Context, GCStmt, !Info),
LocalVarDefn = ml_gen_mlds_var_decl(LocalVarName, MLDS_Type,
GCStmt, Context),
% Generate code to assign from the local var to the output var.
ml_gen_var(!.Info, OutputVar, OutputVarEntry, OutputVarLval),
LocalVarLval = ml_local_var(LocalVarName, MLDS_Type),
Assign = ml_gen_assign(OutputVarLval, ml_lval(LocalVarLval), Context),
% Update the lval for this variable so that any references to it inside
% the commit refer to the local variable rather than to the output
% argument. (Note that we reset all the var lvals at the end of the
% commit.)
ml_gen_info_set_var_lval(OutputVar, LocalVarLval, !Info).
% Generate the declaration for the `commit' variable.
%
:- func ml_gen_commit_var_decl(prog_context, mlds_local_var_name)
= mlds_local_var_defn.
ml_gen_commit_var_decl(Context, VarName) =
ml_gen_mlds_var_decl(VarName, mlds_commit_type, gc_no_stmt, Context).
%---------------------------------------------------------------------------%
:- end_module ml_backend.ml_commit_gen.
%---------------------------------------------------------------------------%