mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-15 05:44:58 +00:00
Now that we can optimize tail recursion for all MLDS targets better via
the MLDS code generator than via ml_tailcall.m, we don't need it anymore.
compiler/ml_tailcall.m:
Delete this module.
compiler/ml_backend.m:
compiler/notes/compiler_design.html:
Delete the inclusion and the documentation of the deleted module.
compiler/mark_tail_calls.m:
Update old references to the deleted module, as well as some comments.
compiler/mercury_compile_mlds_back_end.m:
Don't invoke the deleted module.
compiler/options.m:
Delete the (developer-only) options that used to control whether
we did tail call optimization (TCO) via ml_tailcall.m or not.
compiler/ml_optimize.m:
Delete the parts of this module that worked in concert with ml_tailcall.m
to implement TCO.
compiler/mlds.m:
Delete the field from ml_call_stmts that was needed only by ml_tailcall.m.
compiler/ml_call_gen.m:
Don't fill in the deleted field.
Shift here the only part of the old contents of ml_tailcall.m that is
still needed, the check for whether rvals would become dangling references
if we discarded the current call's stack frame.
compiler/ml_elim_nested.m:
Conform to the change to mlds.m, and eliminate an unused field
in elim_info.
compiler/ml_accurate_gc.m:
compiler/ml_code_util.m:
compiler/ml_commit_gen.m:
compiler/ml_proc_gen.m:
compiler/ml_rename_classes.m:
compiler/ml_util.m:
compiler/mlds_to_c.m:
compiler/mlds_to_cs.m:
compiler/mlds_to_java.m:
compiler/mlds_to_target_util.m:
Conform to the changes above.
527 lines
20 KiB
Mathematica
527 lines
20 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 check_hlds.
|
|
:- import_module check_hlds.type_util.
|
|
:- 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.set_of_var.
|
|
|
|
:- import_module bool.
|
|
:- 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),
|
|
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),
|
|
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, LocalDefns0, Assigns0,
|
|
!Info),
|
|
ml_gen_info_get_module_info(!.Info, ModuleInfo),
|
|
ml_variable_type(!.Info, Var, Type),
|
|
IsDummy = check_dummy_type(ModuleInfo, Type),
|
|
(
|
|
IsDummy = is_dummy_type,
|
|
LocalDefns = LocalDefns0,
|
|
Assigns = Assigns0
|
|
;
|
|
IsDummy = is_not_dummy_type,
|
|
ml_gen_make_local_for_output_arg(Var, Type, Context,
|
|
LocalDefn, Assign, !Info),
|
|
LocalDefns = [LocalDefn | LocalDefns0],
|
|
Assigns = [Assign | Assigns0]
|
|
).
|
|
|
|
:- pred ml_gen_make_local_for_output_arg(prog_var::in, mer_type::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, Type, Context,
|
|
LocalVarDefn, Assign, !Info) :-
|
|
% Look up the name of the output variable.
|
|
ml_gen_info_get_varset(!.Info, VarSet),
|
|
OutputVarName = ml_gen_local_var_name(VarSet, OutputVar),
|
|
|
|
% Generate a declaration for a corresponding local variable.
|
|
OutputVarName = lvn_prog_var(OutputVarNameStr, MaybeNum),
|
|
LocalVarName = lvn_local_var(OutputVarNameStr, MaybeNum),
|
|
ml_gen_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, 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.
|
|
%---------------------------------------------------------------------------%
|