mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-24 05:43:53 +00:00
compiler/decide_type_repn.m:
Previously, this module computed type_repn items to put into .int3 files
for a subset of the type constructors defined in the current module:
the direct_dummy, enum and notag types (the *simple* types),
and the du types whose representation is guaranteed to be
a word-aligned pointer when targeting C. (We care about pointers
being word-aligned only when applying the direct arg optimization.
This optimization is applicable only with the low level data
representation, which we use only when targeting C.)
This diff adds code to decide the representations of *all* the
type constructors defined in the current module.
This code is based on the existing code in du_type_layout.m,
which it is intended to eventually replace, but its job is more general,
because it decides the representation of each type not just for
one platform (the one we want to generate code), but for all possible
platforms. This is because we want to put the descriptions of type
representations into the module's .int file to serve as a single source
of truth for all modules that use the types defined in this module,
and the contents of .int files should be platform-independent.
For our purposes, there are six kinds of platforms, which are
distinguished along three axes: 64 vs 32 bit machines, spf vs non-spf
grades, and direct arg optimization enabled vs disabled. That is eight
combinations, but on 64 bit machines, a float takes up one word whether
that float is single or double precision, so two combinations aren't valid.
Some of the change to this module consists of generalizing the existing
code so that it can decide simple types not just when targeting .int3 files
but .int files as well. However, the bulk of it is code for deciding
the representations of non-simple types. The code is not lifted straight
from du_type_layout.m. There are two main kinds of changes.
First, I took the opportunity to simplify the algorithms used.
For example, while du_type_layout.m passes over each function symbol
in the most general kind of type twice: once to assign it a cons_tag,
and once to decide how to pack its arguments, the code here does both jobs
in one pass. Another example is that for historical reasons,
du_type_layout.m computed the amount of space needed for an argument
in one place for sub-word-sized arguments, and in another place
for more-than-word-sized arguments; decide_type_repn.m does it all
in one place.
Second, since we compute a representation for each type six times,
I tried to avoid obvious inefficiencies, but only if the code
remained simple. In the future, we may want to use an approach
based on the idea that in the process of computing the first
representation, we look out for any indication that the representation
may be different on any of the other five platforms, and if not,
we just reuse the first representation on the other five platforms as well.
However, that would be appropriate only *after* we have a simpler
system that has proven to work in practice.
There is a third, smaller change: when deciding whether an argument
is packable, we take into account not just equivalence type
definitions, but the definitions of notag types as well.
This takes advantage of the fact that if a notag type is abstract
exported, its representation is put into the relevant .int3 file
even though its definition isn't. (This is why du_type_layout.m
couldn't "see through" notag types: it couldn't depend on knowing
which types were notags.)
compiler/prog_item.m:
Change the types we use for type representation information.
Their previous definitions baked in the assumption that the only
distinction between platforms that mattered was the 64 vs 32 bit
distinction, which is not the case.
Use a more consistent naming scheme for the types we use
to represent type representation information.
Include the "dereferenced" types of the arguments in functors'
representations. (I use "dereferencing" here to mean expanding
equivalence types and throwing away any notag wrappers.).
We don't need it when generating C code using the low level
data representation, but we do need it to create constructors
when generating e.g. Java code that uses the high level data
representation.
compiler/parse_type_repn.m:
Rewrite most of this module due to the changes in prog_item.m.
compiler/parse_tree_out_type_repn.m:
A new module containing the code for writing out type representations.
The original code used to be in parse_tree_out.m, but it has been
mostly rewritten. Partly this is due the changes in prog_item.m,
but partly it is to provide much more structured output for humans,
since this makes debugging so much easier.
compiler/parse_tree.m:
Add the new module to the parse_tree package.
compiler/parse_tree_out.m:
Delete the code moved to parse_tree_out_type_repn.m.
compiler/parse_tree_out_info.m:
Provide a mechanism for selecting between output for machines
(the default) and output for humans.
compiler/hlds_data.m:
compiler/prog_data.m:
Move the ptag type from hlds_data.m to prog_data.m, to make it
accessible in prog_item.m.
Add some documentation in prog_data.m.
compiler/comp_unit_interface.m:
If the experiment1 option is enabled, invoke decide_type_repn.m
to decide what type_repn items to put into the .int file we are
generating. Otherwise, maintain the status quo.
compiler/write_module_interface_files.m:
Pass the globals to comp_unit_interface.m so it can look up experiment1.
compiler/equiv_type.m:
Add a predicate for expanding equivalence types for use by
decide_type_repn.m. This predicate expands just one type,
but reports any use of circular equivalence types in that type.
Improve the error message for circular equivalence types by *naming*
the type constructors involved. To make this possible, pass around
sets of such type constructors instead of just a boolean saying
*whether* we have found *some* circular equivalence type.
Replace bools used as changed/unchanged flag with a bespoke type.
Standardize some variable names.
compiler/options.m:
Add the developer-only option --pack-everything, which, if set,
tells decide_type_repn.m to turn on all the packing options
that currently work. This is to allow the testing of decide_type_repn.m
in the eventual intended mode of operation, even if the various
allow-packing-... options used by du_type_layout.m are set to "no".
compiler/disj_gen.m:
compiler/equiv_type_hlds.m:
compiler/llds_out_data.m:
compiler/lookup_util.m:
compiler/ml_call_gen.m:
compiler/ml_closure_gen.m:
compiler/mlds_to_c_data.m:
compiler/rtti.m:
compiler/rtti_out.m:
compiler/rtti_to_mlds.m:
compiler/tag_switch.m:
Conform to the changes above (mostly the move of ptag to prog_data.m.)
compiler/parse_pragma.m:
Improve indentation.
tests/valid_make_int/test_repn.m:
tests/valid_make_int/test_repn_sub.m:
A fairly comprehensive test case of the new functionality.
test_repn_sub.m defines one ore more simple type constructors
of each possible kind, and test_repn.m uses them to define types
that use each possible kind of complex type representation.
tests/valid_make_int/Mmakefile:
tests/valid_make_int/Mercury.options:
Enable the new test case.
1153 lines
48 KiB
Mathematica
1153 lines
48 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 1999-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_call_gen.m.
|
|
% Main author: fjh.
|
|
%
|
|
% This module is part of the MLDS code generator. It handles code generation
|
|
% for both generic and plain procedure calls, and calls to builtins.
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module ml_backend.ml_call_gen.
|
|
:- interface.
|
|
|
|
:- import_module hlds.
|
|
:- import_module hlds.code_model.
|
|
:- import_module hlds.hlds_goal.
|
|
:- import_module hlds.hlds_pred.
|
|
:- import_module hlds.mark_tail_calls. % for nontail_rec_call_reason
|
|
:- import_module ml_backend.ml_args_util.
|
|
:- 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.
|
|
:- import_module set.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Generate MLDS code for an HLDS generic_call goal.
|
|
% This includes boxing/unboxing the arguments if necessary.
|
|
%
|
|
:- pred ml_gen_generic_call(generic_call::in, list(prog_var)::in,
|
|
list(mer_mode)::in, determinism::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.
|
|
|
|
% ml_gen_plain_call(PredId, ProcId, CodeModel, GoalInfo,
|
|
% Args, LocalVarDefns, FuncDefns, Stmts):
|
|
%
|
|
% Generate MLDS code for a plain HLDS procedure call, making sure to
|
|
% box/unbox the arguments if necessary.
|
|
%
|
|
% If the arguments are arg_for_closure_wrapper, then the type_info
|
|
% for type variables in the caller type may not be available in the
|
|
% current procedure, so the GC tracing code for temps introduced for
|
|
% boxing/unboxing (if any) should obtain the type_info from the
|
|
% corresponding entry in the `type_params' local.
|
|
%
|
|
:- pred ml_gen_plain_call(pred_id::in, proc_id::in, code_model::in,
|
|
hlds_goal_info::in, list(prog_var)::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.
|
|
|
|
:- pred ml_gen_plain_non_tail_call(pred_proc_id, code_model, prog_context,
|
|
list(ml_call_arg), nontail_rec_call_reason, set(goal_feature),
|
|
list(mlds_local_var_defn), list(mlds_function_defn), list(mlds_stmt),
|
|
ml_gen_info, ml_gen_info).
|
|
:- mode ml_gen_plain_non_tail_call(in, in, in, in(list_skel(not_fcw)), in, in,
|
|
out, out, out, in, out) is det.
|
|
:- mode ml_gen_plain_non_tail_call(in, in, in, in(list_skel(fcw)), in, in,
|
|
out, out, out, in, out) is det.
|
|
|
|
% Generate MLDS code for a call to a builtin procedure.
|
|
%
|
|
:- pred ml_gen_builtin(pred_id::in, proc_id::in, list(prog_var)::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 backend_libs.
|
|
:- import_module backend_libs.builtin_ops.
|
|
:- import_module check_hlds.
|
|
:- import_module check_hlds.type_util.
|
|
:- import_module hlds.hlds_module.
|
|
:- import_module mdbcomp.
|
|
:- import_module mdbcomp.prim_data.
|
|
:- import_module mdbcomp.sym_name.
|
|
:- import_module ml_backend.ml_code_util.
|
|
:- import_module parse_tree.prog_data_foreign.
|
|
|
|
:- import_module assoc_list.
|
|
:- import_module bool.
|
|
:- import_module int.
|
|
:- import_module map.
|
|
:- import_module maybe.
|
|
:- import_module pair.
|
|
:- import_module require.
|
|
:- import_module term.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Code for generic calls.
|
|
%
|
|
|
|
ml_gen_generic_call(GenericCall, ArgVars, ArgModes, Determinism, Context,
|
|
LocalVarDefns, FuncDefns, Stmts, !Info) :-
|
|
% XXX For typeclass method calls, we do some unnecessary
|
|
% boxing/unboxing of the arguments.
|
|
(
|
|
GenericCall = higher_order(_, _, _, _),
|
|
ml_gen_main_generic_call(GenericCall, ArgVars, ArgModes, Determinism,
|
|
Context, LocalVarDefns, FuncDefns, Stmts, !Info)
|
|
;
|
|
GenericCall = class_method(_, _, _, _),
|
|
ml_gen_main_generic_call(GenericCall, ArgVars, ArgModes, Determinism,
|
|
Context, LocalVarDefns, FuncDefns, Stmts, !Info)
|
|
;
|
|
GenericCall = event_call(_),
|
|
% XXX For now, we can't generate events from the MLDS backend.
|
|
LocalVarDefns = [],
|
|
FuncDefns = [],
|
|
Stmts = []
|
|
;
|
|
GenericCall = cast(_),
|
|
ml_gen_cast(Context, ArgVars, LocalVarDefns, FuncDefns, Stmts, !Info)
|
|
).
|
|
|
|
:- inst main_generic_call for generic_call/0
|
|
---> higher_order(ground, ground, ground, ground)
|
|
; class_method(ground, ground, ground, ground).
|
|
|
|
:- pred ml_gen_main_generic_call(generic_call::in(main_generic_call),
|
|
list(prog_var)::in, list(mer_mode)::in, determinism::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.
|
|
|
|
ml_gen_main_generic_call(GenericCall, ArgVars, ArgModes, Determinism, Context,
|
|
LocalVarDefns, FuncDefns, Stmts, !Info) :-
|
|
% Allocate some fresh type variables to use as the Mercury types
|
|
% of the boxed arguments.
|
|
NumArgs = list.length(ArgVars),
|
|
BoxedArgTypes = ml_make_boxed_types(NumArgs),
|
|
|
|
% Create the boxed parameter types for the called function.
|
|
ml_gen_info_get_module_info(!.Info, ModuleInfo),
|
|
ml_gen_info_get_varset(!.Info, VarSet),
|
|
ArgNames = ml_gen_local_var_names(VarSet, ArgVars),
|
|
PredOrFunc = generic_call_pred_or_func(GenericCall),
|
|
determinism_to_code_model(Determinism, CodeModel),
|
|
ml_gen_params_no_gc_stmts(ModuleInfo, PredOrFunc, CodeModel,
|
|
ArgVars, ArgNames, BoxedArgTypes, ArgModes, _ArgTuples, Params0),
|
|
|
|
% Insert the `closure_arg' parameter.
|
|
%
|
|
% The GCStmt for `closure_arg' here is wrong, but it doesn't matter,
|
|
% since `closure_arg' is only part of a type (a function parameter in the
|
|
% function type). We won't use the GC tracing code generated here, since
|
|
% we don't generate any actual local variable or parameter for
|
|
% `closure_arg'.
|
|
GCStmt = gc_no_stmt,
|
|
ClosureArgType = mlds_generic_type,
|
|
ClosureArg = mlds_argument(lvn_comp_var(lvnc_closure_arg), ClosureArgType,
|
|
GCStmt),
|
|
Params0 = mlds_func_params(ArgParams0, RetParam),
|
|
Params = mlds_func_params([ClosureArg | ArgParams0], RetParam),
|
|
Signature = mlds_get_func_signature(Params),
|
|
|
|
% Compute the function address.
|
|
(
|
|
GenericCall = higher_order(ClosureVar, _Purity, _PredOrFunc, _Arity),
|
|
ml_gen_var(!.Info, ClosureVar, ClosureLval),
|
|
FieldId = ml_field_offset(ml_const(mlconst_int(1))),
|
|
% XXX are these types right?
|
|
FuncLval = ml_field(yes(ptag(0u8)),
|
|
ml_lval(ClosureLval), ClosureArgType,
|
|
FieldId, mlds_generic_type),
|
|
FuncType = mlds_func_type(Params),
|
|
FuncRval = ml_unbox(FuncType, ml_lval(FuncLval))
|
|
;
|
|
GenericCall = class_method(TypeClassInfoVar, MethodNum,
|
|
_ClassId, _PredName),
|
|
|
|
% Create the lval for the typeclass_info, which is also the closure
|
|
% in this case.
|
|
ml_gen_var(!.Info, TypeClassInfoVar, TypeClassInfoLval),
|
|
ClosureLval = TypeClassInfoLval,
|
|
|
|
% Extract the base_typeclass_info from the typeclass_info.
|
|
BaseTypeclassInfoFieldId = ml_field_offset(ml_const(mlconst_int(0))),
|
|
BaseTypeclassInfoLval = ml_field(yes(ptag(0u8)),
|
|
ml_lval(TypeClassInfoLval), ClosureArgType,
|
|
BaseTypeclassInfoFieldId, mlds_generic_type),
|
|
|
|
% Extract the method address from the base_typeclass_info.
|
|
Offset = ml_base_typeclass_info_method_offset,
|
|
MethodFieldNum = MethodNum + Offset,
|
|
MethodFieldId = ml_field_offset(ml_const(mlconst_int(MethodFieldNum))),
|
|
FuncLval = ml_field(yes(ptag(0u8)),
|
|
ml_lval(BaseTypeclassInfoLval), mlds_generic_type,
|
|
MethodFieldId, mlds_generic_type),
|
|
FuncType = mlds_func_type(Params),
|
|
FuncRval = ml_unbox(FuncType, ml_lval(FuncLval))
|
|
),
|
|
|
|
% Assign the function address rval to a new local variable. This makes
|
|
% the generated code slightly more readable. More importantly, this is also
|
|
% necessary when using a non-standard calling convention with GNU C,
|
|
% since GNU C (2.95.2) ignores the function attributes on function
|
|
% pointer types in casts.
|
|
% XXX Is this limitation still there in currently used C compilers?
|
|
%
|
|
ml_gen_info_new_conv_var(ConvVarSeq, !Info),
|
|
ConvVarSeq = conv_seq(ConvVarNum),
|
|
FuncVarName = lvn_comp_var(lvnc_conv_var(ConvVarNum)),
|
|
% The function address is always a pointer to code,
|
|
% not to the heap, so the GC doesn't need to trace it.
|
|
GCStmt = gc_no_stmt,
|
|
FuncVarDecl = ml_gen_mlds_var_decl(FuncVarName, FuncType, GCStmt, Context),
|
|
FuncVarLval = ml_local_var(FuncVarName, FuncType),
|
|
AssignFuncVar = ml_gen_assign(FuncVarLval, FuncRval, Context),
|
|
FuncVarRval = ml_lval(FuncVarLval),
|
|
|
|
% Generate code to box/unbox the arguments and compute the list of properly
|
|
% converted rvals/lvals to pass as the function call's arguments and
|
|
% return values.
|
|
CallerArgs = wrap_plain_not_fcw_args(ArgVars),
|
|
ml_gen_args(PredOrFunc, CodeModel, Context, input_and_output_params,
|
|
BoxedArgTypes, ArgModes, CallerArgs, InputRvals, OutputLvalsTypes,
|
|
ConvArgLocalVarDefns, ConvOutputStmts, !Info),
|
|
ClosureRval = ml_unbox(ClosureArgType, ml_lval(ClosureLval)),
|
|
|
|
( if
|
|
ConvArgLocalVarDefns = [],
|
|
ConvOutputStmts = []
|
|
then
|
|
% Generate the call directly (as opposed to via DoGenCall)
|
|
% in the common case.
|
|
ml_gen_mlds_call(Signature, FuncVarRval,
|
|
[ClosureRval | InputRvals], OutputLvalsTypes,
|
|
Determinism, Context, LocalVarDefns0, FuncDefns0, Stmts0, !Info)
|
|
else
|
|
% Prepare to generate the call, passing the closure as the first
|
|
% argument. We can't actually generate the call yet, since it might be
|
|
% nondet, and we don't yet know what its success continuation will be.
|
|
% Instead we construct a higher-order term `DoGenCall', which, when
|
|
% called by ml_combine_conj, will generate it.
|
|
DoGenCall = ml_gen_mlds_call(Signature, FuncVarRval,
|
|
[ClosureRval | InputRvals], OutputLvalsTypes,
|
|
Determinism, Context),
|
|
|
|
% Construct a closure to generate code to convert the output arguments
|
|
% and then succeed.
|
|
DoGenConvOutputAndSucceed =
|
|
( pred(COAS_LocalVarDefns::out, COAS_FuncDefns::out,
|
|
COAS_Stmts::out, Info0::in, Info::out) is det :-
|
|
COAS_LocalVarDefns = [],
|
|
COAS_FuncDefns = [],
|
|
ml_gen_success(CodeModel, Context, SucceedStmts, Info0, Info),
|
|
COAS_Stmts = ConvOutputStmts ++ SucceedStmts
|
|
),
|
|
|
|
% Conjoin the code generated by the two closures that we computed
|
|
% above. `ml_combine_conj' will generate whatever kind of sequence
|
|
% is necessary for this code model.
|
|
ml_combine_conj(CodeModel, Context, DoGenCall,
|
|
DoGenConvOutputAndSucceed,
|
|
CallAndConvOutputLocalVarDefns, CallAndConvOutputFuncDefns,
|
|
CallAndConvOutputStmts, !Info),
|
|
LocalVarDefns0 =
|
|
ConvArgLocalVarDefns ++ CallAndConvOutputLocalVarDefns,
|
|
FuncDefns0 = CallAndConvOutputFuncDefns,
|
|
Stmts0 = CallAndConvOutputStmts
|
|
),
|
|
LocalVarDefns = [FuncVarDecl | LocalVarDefns0],
|
|
FuncDefns = FuncDefns0,
|
|
Stmts = [AssignFuncVar | Stmts0].
|
|
|
|
% Generate MLDS code for a cast. The list of argument variables
|
|
% must have only two elements, the input and the output.
|
|
%
|
|
:- pred ml_gen_cast(prog_context::in, list(prog_var)::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.
|
|
|
|
ml_gen_cast(Context, ArgVars, LocalVarDefns, FuncDefns, Stmts, !Info) :-
|
|
ml_gen_var_list(!.Info, ArgVars, ArgLvals),
|
|
ml_variable_types(!.Info, ArgVars, ArgTypes),
|
|
( if
|
|
ArgVars = [SrcVar, DestVar],
|
|
ArgLvals = [SrcLval, DestLval],
|
|
ArgTypes = [SrcType, DestType]
|
|
then
|
|
ml_gen_info_get_module_info(!.Info, ModuleInfo),
|
|
IsDummy = is_type_a_dummy(ModuleInfo, DestType),
|
|
(
|
|
IsDummy = is_dummy_type,
|
|
Stmts = []
|
|
;
|
|
IsDummy = is_not_dummy_type,
|
|
ml_gen_box_or_unbox_rval(ModuleInfo, SrcType, DestType,
|
|
bp_native_if_possible, ml_lval(SrcLval), CastRval),
|
|
Assign = ml_gen_assign(DestLval, CastRval, Context),
|
|
Stmts = [Assign]
|
|
),
|
|
LocalVarDefns = [],
|
|
FuncDefns = [],
|
|
( if ml_gen_info_search_const_var(!.Info, SrcVar, GroundTerm) then
|
|
% If the source variable is a constant, so is the target after
|
|
% this cast.
|
|
ml_gen_info_set_const_var(DestVar, GroundTerm, !Info)
|
|
else
|
|
true
|
|
)
|
|
else
|
|
unexpected($pred, "wrong number of args for cast")
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Code for ordinary calls.
|
|
%
|
|
|
|
ml_gen_plain_call(CalleePredId, CalleeProcId, CodeModel, GoalInfo, ArgVars,
|
|
LocalVarDefns, FuncDefns, Stmts, !Info) :-
|
|
CalleePredProcId = proc(CalleePredId, CalleeProcId),
|
|
Context = goal_info_get_context(GoalInfo),
|
|
Features = goal_info_get_features(GoalInfo),
|
|
( if set.contains(Features, feature_self_or_mutual_tail_rec_call) then
|
|
ml_gen_plain_tail_call(CalleePredProcId, CodeModel, Context,
|
|
ArgVars, Features,
|
|
LocalVarDefns, FuncDefns, Stmts, !Info)
|
|
else
|
|
% We change representation because ml_closure_gen.m also calls
|
|
% ml_gen_plain_non_tail_call with a different mechanism for specifying
|
|
% the actual parameters.
|
|
CallerArgs = wrap_plain_not_fcw_args(ArgVars),
|
|
ml_gen_plain_non_tail_call(CalleePredProcId, CodeModel, Context,
|
|
CallerArgs, ntrcr_program, Features,
|
|
LocalVarDefns, FuncDefns, Stmts, !Info)
|
|
).
|
|
|
|
:- pred ml_gen_plain_tail_call(pred_proc_id::in, code_model::in,
|
|
prog_context::in, list(prog_var)::in, set(goal_feature)::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.
|
|
|
|
ml_gen_plain_tail_call(CalleePredProcId, CodeModel, Context, ArgVars, Features,
|
|
LocalVarDefns, FuncDefns, Stmts, !Info) :-
|
|
% Compute the callee's Mercury argument types and modes.
|
|
ml_gen_info_get_module_info(!.Info, ModuleInfo),
|
|
module_info_pred_proc_info(ModuleInfo, CalleePredProcId,
|
|
PredInfo, ProcInfo),
|
|
PredOrFunc = pred_info_is_pred_or_func(PredInfo),
|
|
pred_info_get_arg_types(PredInfo, CalleeArgTypes),
|
|
proc_info_get_argmodes(ProcInfo, CalleeArgModes),
|
|
|
|
% Generate code to box/unbox the arguments and compute the list of
|
|
% properly converted rvals/lvals to pass as the function call's
|
|
% *input* arguments. We don't need to handle the output arguments.
|
|
CallerArgs = wrap_plain_not_fcw_args(ArgVars),
|
|
ml_gen_args(PredOrFunc, CodeModel, Context, input_params_only,
|
|
CalleeArgTypes, CalleeArgModes, CallerArgs,
|
|
InputRvals, OutputLvalsTypes,
|
|
ConvOutputDefns, ConvOutputStmts, !Info),
|
|
expect(unify(OutputLvalsTypes, []), $pred, "OutputLvalsTypes != []"),
|
|
expect(unify(ConvOutputDefns, []), $pred, "ConvOutputDefns != []"),
|
|
expect(unify(ConvOutputStmts, []), $pred, "ConvOutputStmts != []"),
|
|
|
|
ml_gen_info_get_tail_rec_info(!.Info, TailRecInfo0),
|
|
InSccMap0 = TailRecInfo0 ^ tri_in_scc_map,
|
|
% The callee of this tail call will be in InSccMap0 only if it can
|
|
% call the caller back directly or indirectly.
|
|
( if map.search(InSccMap0, CalleePredProcId, InSccInfo0) then
|
|
InSccInfo0 = in_scc_info(MaybeInTscc,
|
|
IsTargetOfSelfTRCall0, IsTargetOfMutualTRCall0, _),
|
|
(
|
|
MaybeInTscc = in_tscc(IdInTSCC, FuncInputArgs),
|
|
DanglingStackRef = may_rvals_yield_dangling_stack_ref(InputRvals),
|
|
(
|
|
DanglingStackRef = will_not_yield_dangling_stack_ref,
|
|
ml_gen_info_get_func_nest_depth(!.Info, FuncNestDepth),
|
|
( if FuncNestDepth = 0 then
|
|
CommentStmt =
|
|
ml_stmt_atomic(comment("direct tailcall eliminated"),
|
|
Context),
|
|
|
|
ml_gen_info_get_module_name(!.Info, ModuleName),
|
|
MLDS_ModuleName = mercury_module_name_to_mlds(ModuleName),
|
|
tail_rec_call_assign_input_args(MLDS_ModuleName, Context,
|
|
FuncInputArgs, InputRvals, InitStmts, AssignStmts,
|
|
LocalVarDefns),
|
|
FuncDefns = [],
|
|
LoopKind = TailRecInfo0 ^ tri_loop_kind,
|
|
TsccKind = TailRecInfo0 ^ tri_tscc_kind,
|
|
(
|
|
LoopKind = tail_rec_loop_while_continue,
|
|
(
|
|
TsccKind = tscc_self_rec_only,
|
|
SetSelectorStmts = []
|
|
;
|
|
TsccKind = tscc_self_and_mutual_rec,
|
|
IdInTSCC = proc_id_in_tscc(TsccProcNum),
|
|
IntType = mlds_builtin_type_int(int_type_int),
|
|
SelectorVar =
|
|
lvn_comp_var(lvnc_tscc_proc_selector),
|
|
SelectorLval = ml_local_var(SelectorVar, IntType),
|
|
SetSelectorStmt = ml_stmt_atomic(
|
|
assign(SelectorLval,
|
|
ml_const(mlconst_int(TsccProcNum))),
|
|
Context),
|
|
SetSelectorStmts = [SetSelectorStmt]
|
|
),
|
|
GotoTarget = goto_continue_loop
|
|
;
|
|
LoopKind = tail_rec_loop_label_goto,
|
|
SetSelectorStmts = [],
|
|
StartLabel = generate_tail_rec_start_label(TsccKind,
|
|
IdInTSCC),
|
|
GotoTarget = goto_label(StartLabel)
|
|
),
|
|
GotoStmt = ml_stmt_goto(GotoTarget, Context),
|
|
Stmts = [CommentStmt] ++ InitStmts ++ AssignStmts ++
|
|
SetSelectorStmts ++ [GotoStmt],
|
|
|
|
ml_gen_info_get_pred_proc_id(!.Info, CallerPredProcId),
|
|
( if CalleePredProcId = CallerPredProcId then
|
|
(
|
|
IsTargetOfSelfTRCall0 = is_target_of_self_trcall
|
|
;
|
|
IsTargetOfSelfTRCall0 =
|
|
is_not_target_of_self_trcall,
|
|
InSccInfo = InSccInfo0 ^ isi_is_target_of_self_tr
|
|
:= is_target_of_self_trcall,
|
|
map.det_update(CalleePredProcId, InSccInfo,
|
|
InSccMap0, InSccMap),
|
|
TailRecInfo = TailRecInfo0 ^
|
|
tri_in_scc_map := InSccMap,
|
|
ml_gen_info_set_tail_rec_info(TailRecInfo, !Info)
|
|
)
|
|
else
|
|
(
|
|
IsTargetOfMutualTRCall0 =
|
|
is_target_of_mutual_trcall
|
|
;
|
|
IsTargetOfMutualTRCall0 =
|
|
is_not_target_of_mutual_trcall,
|
|
InSccInfo = InSccInfo0 ^ isi_is_target_of_mutual_tr
|
|
:= is_target_of_mutual_trcall,
|
|
map.det_update(CalleePredProcId, InSccInfo,
|
|
InSccMap0, InSccMap),
|
|
TailRecInfo = TailRecInfo0 ^
|
|
tri_in_scc_map := InSccMap,
|
|
ml_gen_info_set_tail_rec_info(TailRecInfo, !Info)
|
|
)
|
|
)
|
|
else
|
|
ml_gen_plain_non_tail_call(CalleePredProcId, CodeModel,
|
|
Context, CallerArgs,
|
|
ntrcr_mlds_model_non_in_cont_func, Features,
|
|
LocalVarDefns, FuncDefns, Stmts, !Info)
|
|
)
|
|
;
|
|
DanglingStackRef = may_yield_dangling_stack_ref,
|
|
ml_gen_plain_non_tail_call(CalleePredProcId, CodeModel,
|
|
Context, CallerArgs,
|
|
ntrcr_mlds_in_tscc_stack_ref, Features,
|
|
LocalVarDefns, FuncDefns, Stmts, !Info)
|
|
)
|
|
;
|
|
MaybeInTscc = not_in_tscc,
|
|
ml_gen_plain_non_tail_call(CalleePredProcId, CodeModel,
|
|
Context, CallerArgs, ntrcr_mlds_in_scc_not_in_tscc, Features,
|
|
LocalVarDefns, FuncDefns, Stmts, !Info)
|
|
)
|
|
else
|
|
ml_gen_plain_non_tail_call(CalleePredProcId, CodeModel,
|
|
Context, CallerArgs, ntrcr_program, Features,
|
|
LocalVarDefns, FuncDefns, Stmts, !Info)
|
|
).
|
|
|
|
% Assign the given list of rvals (the actual parameter) to the given list
|
|
% of mlds_arguments (the formal parameter). This is used as part of tail
|
|
% recursion optimization (see above).
|
|
%
|
|
% For each actual parameter that differs from its corresponding formal
|
|
% parameter, we declare a temporary variable to hold the next value
|
|
% of the formal parameter, and assign it the value of the actual parameter.
|
|
% Once this has been done for all parameters, we then assign each formal
|
|
% parameter its next value. The code we generate looks like this:
|
|
%
|
|
% % These are returned in TempDefns.
|
|
% SomeType new_value_of_arg1;
|
|
% SomeType new_value_of_arg3;
|
|
% SomeType new_value_of_arg3;
|
|
%
|
|
% % These are returned in InitStmts.
|
|
% new_value_of_arg1 = <the new value of parameter 1>
|
|
% new_value_of_arg2 = <the new value of parameter 2>
|
|
% new_value_of_arg3 = <the new value of parameter 3>
|
|
%
|
|
% % These are returned in AssignStmts.
|
|
% arg1 = new_value_of_arg1;
|
|
% arg2 = new_value_of_arg2;
|
|
% arg3 = new_value_of_arg3;
|
|
%
|
|
% The temporaries are needed for tail calls such as
|
|
%
|
|
% p(In1, In2, ...) :-
|
|
% ...
|
|
% p(In2, In1, ...).
|
|
%
|
|
% We don't want to assign In1 to In2 (in the second parameter position)
|
|
% after we have already clobbered the value of In1 by assigning it In2
|
|
% (when processing the first parameter position).
|
|
%
|
|
% This predicate doesn't pay attention to the gc statement field
|
|
% inside the mlds_arguments it is given; it needs only the variable name
|
|
% and type fields.
|
|
%
|
|
:- pred tail_rec_call_assign_input_args(mlds_module_name::in, prog_context::in,
|
|
list(mlds_argument)::in, list(mlds_rval)::in,
|
|
list(mlds_stmt)::out, list(mlds_stmt)::out,
|
|
list(mlds_local_var_defn)::out) is det.
|
|
|
|
tail_rec_call_assign_input_args(_, _, [], [], [], [], []).
|
|
tail_rec_call_assign_input_args(_, _, [_|_], [], [], [], []) :-
|
|
unexpected($pred, "length mismatch").
|
|
tail_rec_call_assign_input_args(_, _, [], [_|_], [], [], []) :-
|
|
unexpected($pred, "length mismatch").
|
|
tail_rec_call_assign_input_args(ModuleName, Context,
|
|
[Arg | Args], [ArgRval | ArgRvals],
|
|
!:InitStmts, !:AssignStmts, !:TempDefns) :-
|
|
tail_rec_call_assign_input_args(ModuleName, Context, Args, ArgRvals,
|
|
!:InitStmts, !:AssignStmts, !:TempDefns),
|
|
|
|
Arg = mlds_argument(VarName, Type, _ArgGCStmt),
|
|
% Don't bother assigning a variable to itself.
|
|
( if ArgRval = ml_lval(ml_local_var(VarName, _VarType)) then
|
|
true
|
|
else
|
|
( if VarName = lvn_prog_var(VarNameStr, VarNum) then
|
|
NextValueName = lvn_prog_var_next_value(VarNameStr, VarNum)
|
|
else
|
|
% This should not happen; the head variables of a procedure
|
|
% should all be lvn_prog_vars, even the ones representing
|
|
% the typeinfos and typeclassinfos added by the compiler.
|
|
% However, better safe than sorry.
|
|
VarNameStr = ml_local_var_name_to_string(VarName),
|
|
NextValueName =
|
|
lvn_comp_var(lvnc_non_prog_var_next_value(VarNameStr))
|
|
),
|
|
% Note that we have to use an assignment rather than an initializer
|
|
% to initialize the temp, because this pass comes before
|
|
% ml_elem_nested.m, and ml_elim_nested.m doesn't handle code
|
|
% containing initializers.
|
|
% XXX We should teach it to handle them.
|
|
NextValueInitStmt = ml_stmt_atomic(
|
|
assign(ml_local_var(NextValueName, Type), ArgRval),
|
|
Context),
|
|
!:InitStmts = [NextValueInitStmt | !.InitStmts],
|
|
AssignStmt = ml_stmt_atomic(
|
|
assign(
|
|
ml_local_var(VarName, Type),
|
|
ml_lval(ml_local_var(NextValueName, Type))),
|
|
Context),
|
|
!:AssignStmts = [AssignStmt | !.AssignStmts],
|
|
% We don't need to trace the temporary variables for GC, since they
|
|
% are not live across a call or a heap allocation.
|
|
TempDefn = ml_gen_mlds_var_decl_init(NextValueName, Type,
|
|
no_initializer, gc_no_stmt, Context),
|
|
!:TempDefns = [TempDefn | !.TempDefns]
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
ml_gen_plain_non_tail_call(CalleePredProcId, CodeModel, Context, CallerArgs,
|
|
NonTailCallReason, Features, LocalVarDefns, FuncDefns, Stmts, !Info) :-
|
|
% Generate the various parts of the code that is needed
|
|
% for a procedure call:
|
|
%
|
|
% - declarations of variables needed for boxing/unboxing output arguments;
|
|
% - code to call the function with the input arguments appropriately boxed,
|
|
% and
|
|
% - code to unbox/box the return values.
|
|
%
|
|
% For example, if the callee is declared as
|
|
%
|
|
% :- some [T2]
|
|
% pred callee(float::in, T1::in, float::out, T2::out, ...).
|
|
%
|
|
% then for a call `callee(Arg1, Arg2, Arg3, Arg4, ...)'
|
|
% with arguments of types `U1, float, U2, float, ...',
|
|
% we generate the following fragments:
|
|
%
|
|
% /* declarations of variables needed for boxing/unboxing */
|
|
% Float conv_Arg3;
|
|
% MR_Box conv_Arg4;
|
|
% ...
|
|
%
|
|
% /* code to call the function */
|
|
% func(unbox(Arg1), box(Arg2), &conv0_Arg3, &conv1_Arg4);
|
|
%
|
|
% /* code to box/unbox the output arguments */
|
|
% *Arg3 = box(conv0_Arg3);
|
|
% *Arg4 = unbox(conv1_Arg4);
|
|
% ...
|
|
%
|
|
% Note that in general not every argument will need to be boxed/unboxed;
|
|
% for those where no conversion is required, we just pass the
|
|
% original argument unchanged.
|
|
%
|
|
|
|
% Compute the function signature.
|
|
ml_gen_info_get_module_info(!.Info, ModuleInfo),
|
|
ml_gen_proc_params_no_gc_stmts(ModuleInfo, CalleePredProcId,
|
|
_ArgTuples, Params),
|
|
Signature = mlds_get_func_signature(Params),
|
|
|
|
% Compute the function address.
|
|
ml_gen_proc_addr_rval(CalleePredProcId, _FuncProcLabel, FuncRval, !Info),
|
|
|
|
% Compute the callee's Mercury argument types and modes.
|
|
module_info_pred_proc_info(ModuleInfo, CalleePredProcId,
|
|
PredInfo, ProcInfo),
|
|
PredOrFunc = pred_info_is_pred_or_func(PredInfo),
|
|
pred_info_get_arg_types(PredInfo, CalleeArgTypes),
|
|
proc_info_get_argmodes(ProcInfo, CalleeArgModes),
|
|
|
|
% Generate code to box/unbox the arguments and compute the list of
|
|
% properly converted rvals/lvals to pass as the function call's arguments
|
|
% and return values.
|
|
ml_gen_args(PredOrFunc, CodeModel, Context, input_and_output_params,
|
|
CalleeArgTypes, CalleeArgModes, CallerArgs,
|
|
InputRvals, OutputLvalsTypes,
|
|
ConvOutputDefns, ConvOutputStmts, !Info),
|
|
|
|
proc_info_interface_determinism(ProcInfo, Detism),
|
|
|
|
( if
|
|
ConvOutputDefns = [],
|
|
ConvOutputStmts = []
|
|
then
|
|
% Generate the call directly (as opposed to via DoGenCall)
|
|
% in the common case.
|
|
ml_gen_mlds_call(Signature, FuncRval,
|
|
InputRvals, OutputLvalsTypes, Detism, Context,
|
|
LocalVarDefns, FuncDefns, Stmts, !Info)
|
|
else
|
|
% Construct a closure to generate the call. We can't actually generate
|
|
% the call yet, since it might be nondet, and we don't yet know
|
|
% what its success continuation will be. That is why we construct
|
|
% a closure `DoGenCall', which, when called by ml_combine_conj, will
|
|
% generate it.
|
|
DoGenCall = ml_gen_mlds_call(Signature, FuncRval,
|
|
InputRvals, OutputLvalsTypes, Detism, Context),
|
|
|
|
% Construct a closure to generate code to convert the output arguments
|
|
% and then succeed.
|
|
DoGenConvOutputAndSucceed =
|
|
( pred(COAS_LocalVarDefns::out, COAS_FuncDefns::out,
|
|
COAS_Stmts::out, Info0::in, Info::out) is det :-
|
|
COAS_LocalVarDefns = [],
|
|
COAS_FuncDefns = [],
|
|
ml_gen_success(CodeModel, Context, SucceedStmts, Info0, Info),
|
|
COAS_Stmts = ConvOutputStmts ++ SucceedStmts
|
|
),
|
|
|
|
% Conjoin the code generated by the two closures that we computed
|
|
% above. `ml_combine_conj' will generate whatever kind of sequence
|
|
% is necessary for this code model.
|
|
ml_combine_conj(CodeModel, Context, DoGenCall,
|
|
DoGenConvOutputAndSucceed,
|
|
CallAndConvOutputLocalVarDefns, CallAndConvOutputFuncDefns,
|
|
CallAndConvOutputStmts, !Info),
|
|
LocalVarDefns = ConvOutputDefns ++ CallAndConvOutputLocalVarDefns,
|
|
FuncDefns = CallAndConvOutputFuncDefns,
|
|
Stmts = CallAndConvOutputStmts
|
|
),
|
|
|
|
% If this is a non-tail call to a procedure in the current TSCC (if any),
|
|
% we need to mark the callee as being an entry point.
|
|
ml_gen_info_get_tail_rec_info(!.Info, TailRecInfo0),
|
|
InSccMap0 = TailRecInfo0 ^ tri_in_scc_map,
|
|
( if map.search(InSccMap0, CalleePredProcId, InSccInfo0) then
|
|
ml_gen_info_get_pred_proc_id(!.Info, CallerPredProcId),
|
|
ml_gen_info_get_disabled_warnings(!.Info, Warnings),
|
|
( if set.contains(Warnings, goal_warning_non_tail_recursive_calls) then
|
|
Status = nontail_rec_call_warn_disabled
|
|
else
|
|
Status = nontail_rec_call_warn_enabled
|
|
),
|
|
( if set.contains(Features, feature_obvious_nontail_rec_call) then
|
|
Obviousness = obvious_nontail_rec
|
|
else
|
|
Obviousness = non_obvious_nontail_rec
|
|
),
|
|
NonTailRecCall = nontail_rec_call(CallerPredProcId, CalleePredProcId,
|
|
Context, NonTailCallReason, Obviousness, Status),
|
|
NonTailRecCalls0 = InSccInfo0 ^ isi_is_target_of_non_tail_rec,
|
|
NonTailRecCalls = [NonTailRecCall | NonTailRecCalls0],
|
|
InSccInfo = InSccInfo0 ^ isi_is_target_of_non_tail_rec
|
|
:= NonTailRecCalls,
|
|
map.det_update(CalleePredProcId, InSccInfo, InSccMap0, InSccMap),
|
|
TailRecInfo = TailRecInfo0 ^ tri_in_scc_map := InSccMap,
|
|
ml_gen_info_set_tail_rec_info(TailRecInfo, !Info)
|
|
else
|
|
true
|
|
).
|
|
|
|
% Generate an rval containing the address of the specified procedure.
|
|
%
|
|
:- pred ml_gen_proc_addr_rval(pred_proc_id::in, mlds_proc_label::out,
|
|
mlds_rval::out, ml_gen_info::in, ml_gen_info::out) is det.
|
|
|
|
ml_gen_proc_addr_rval(PredProcId, ProcLabel, CodeAddrRval, !Info) :-
|
|
ml_gen_info_get_module_info(!.Info, ModuleInfo),
|
|
ml_gen_pred_label(ModuleInfo, PredProcId, PredLabel, PredModule),
|
|
ml_gen_info_proc_params(PredProcId, _ArgTuples, Params,
|
|
_ByRefOutputVars, _CopiedOutputVars, !Info),
|
|
Signature = mlds_get_func_signature(Params),
|
|
PredProcId = proc(_PredId, ProcId),
|
|
ProcLabel = mlds_proc_label(PredLabel, ProcId),
|
|
FuncLabel = mlds_func_label(ProcLabel, proc_func),
|
|
QualFuncLabel = qual_func_label(PredModule, FuncLabel),
|
|
CodeAddrRval = ml_const(mlconst_code_addr(
|
|
mlds_code_addr(QualFuncLabel, Signature))).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% This generates a call in the specified code model.
|
|
% This is a lower-level routine called by both ml_gen_call
|
|
% and ml_gen_generic_call.
|
|
%
|
|
:- pred ml_gen_mlds_call(mlds_func_signature::in,
|
|
mlds_rval::in, list(mlds_rval)::in, assoc_list(mlds_lval, mlds_type)::in,
|
|
determinism::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.
|
|
|
|
ml_gen_mlds_call(Signature, FuncRval, ArgRvals0, RetLvalsTypes0,
|
|
Detism, Context, LocalVarDefns, FuncDefns, Stmts, !Info) :-
|
|
% Append the extra arguments or return val for this code_model.
|
|
determinism_to_code_model(Detism, CodeModel),
|
|
(
|
|
CodeModel = model_non,
|
|
% Create a new success continuation, if necessary.
|
|
ml_gen_success_cont(RetLvalsTypes0, Context, Cont, FuncDefns, !Info),
|
|
% Append the success continuation to the ordinary arguments.
|
|
Cont = success_cont(FuncPtrRval, EnvPtrRval, _),
|
|
ArgRvals = ArgRvals0 ++ [FuncPtrRval, EnvPtrRval],
|
|
% For --nondet-copy-out, the output arguments will be passed to the
|
|
% continuation rather than being returned.
|
|
ml_gen_info_get_nondet_copy_out(!.Info, NondetCopyOut),
|
|
(
|
|
NondetCopyOut = yes,
|
|
RetLvals = []
|
|
;
|
|
NondetCopyOut = no,
|
|
assoc_list.keys(RetLvalsTypes0, RetLvals)
|
|
)
|
|
;
|
|
CodeModel = model_semi,
|
|
% Return a bool indicating whether or not it succeeded.
|
|
ml_success_lval(Success, !Info),
|
|
ArgRvals = ArgRvals0,
|
|
assoc_list.keys(RetLvalsTypes0, RetLvals0),
|
|
RetLvals = [Success | RetLvals0],
|
|
FuncDefns = []
|
|
;
|
|
CodeModel = model_det,
|
|
ArgRvals = ArgRvals0,
|
|
assoc_list.keys(RetLvalsTypes0, RetLvals),
|
|
FuncDefns = []
|
|
),
|
|
LocalVarDefns = [],
|
|
|
|
% Build the MLDS call statement.
|
|
%
|
|
% If the called procedure has determinism `erroneous', then mark it
|
|
% as never returning (this will ensure that it gets treated as a tail
|
|
% call).
|
|
( if Detism = detism_erroneous then
|
|
CallKind = no_return_call
|
|
else
|
|
CallKind = ordinary_call
|
|
),
|
|
Stmt = ml_stmt_call(Signature, FuncRval, ArgRvals, RetLvals,
|
|
CallKind, Context),
|
|
Stmts = [Stmt].
|
|
|
|
:- pred ml_gen_success_cont(assoc_list(mlds_lval, mlds_type)::in,
|
|
prog_context::in, success_cont::out, list(mlds_function_defn)::out,
|
|
ml_gen_info::in, ml_gen_info::out) is det.
|
|
|
|
ml_gen_success_cont(OutputArgLvalsTypes, Context, NewCont, ContDecls, !Info) :-
|
|
ml_gen_info_current_success_cont(!.Info, CurrentCont),
|
|
CurrentCont = success_cont(_FuncPtrRval, _EnvPtrRval,
|
|
CurrentContArgLvalsTypes),
|
|
( if
|
|
% As an optimization, check if the parameters expected by the current
|
|
% continuation are the same as the ones expected by the new
|
|
% continuation that we are generating; if so, we can just use the
|
|
% current continuation rather than creating a new one.
|
|
%
|
|
CurrentContArgLvalsTypes = OutputArgLvalsTypes
|
|
then
|
|
NewCont = CurrentCont,
|
|
ContDecls = []
|
|
else
|
|
% Create a new continuation function that just copies the outputs
|
|
% to locals and then calls the original current continuation.
|
|
%
|
|
% Note that ml_gen_cont_params does not fill in the gc_statement
|
|
% for the parameters. This is OK, because the parameters of the
|
|
% continuation function will not be live across any heap allocations or
|
|
% procedure calls.
|
|
%
|
|
ml_gen_cont_params(OutputArgLvalsTypes, Params, !Info),
|
|
ml_gen_new_func_label(yes(Params),
|
|
ContFuncLabel, ContFuncLabelRval, !Info),
|
|
% Push nesting level.
|
|
ml_gen_copy_args_to_locals(OutputArgLvalsTypes, Context, CopyStmts),
|
|
ml_gen_call_current_success_cont(Context, CallContStmt, !Info),
|
|
CopyStmt = ml_gen_block([], [], CopyStmts ++ [CallContStmt], Context),
|
|
% Pop nesting level.
|
|
ml_gen_label_func(!.Info, ContFuncLabel, Params, Context,
|
|
CopyStmt, ContFuncDefn),
|
|
ContDecls = [ContFuncDefn],
|
|
|
|
ml_get_env_ptr(EnvPtrRval),
|
|
NewCont = success_cont(ContFuncLabelRval, EnvPtrRval,
|
|
OutputArgLvalsTypes)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Generate the appropriate MLDS type for a continuation function
|
|
% for a nondet procedure whose output arguments have the specified types.
|
|
%
|
|
% WARNING: this does not fill in the gc_statement for the function
|
|
% parameters. It is the caller's responsibility to fill these in properly
|
|
% if needed.
|
|
%
|
|
:- pred ml_gen_cont_params(assoc_list(mlds_lval, mlds_type)::in,
|
|
mlds_func_params::out, ml_gen_info::in, ml_gen_info::out) is det.
|
|
|
|
ml_gen_cont_params(OutputArgLvalsTypes, Params, !Info) :-
|
|
ml_gen_cont_params_loop(OutputArgLvalsTypes, 1, Args0),
|
|
ml_declare_env_ptr_arg(EnvPtrArg),
|
|
Args = Args0 ++ [EnvPtrArg],
|
|
Params = mlds_func_params(Args, []).
|
|
|
|
:- pred ml_gen_cont_params_loop(assoc_list(mlds_lval, mlds_type)::in, int::in,
|
|
list(mlds_argument)::out) is det.
|
|
|
|
ml_gen_cont_params_loop([], _, []).
|
|
ml_gen_cont_params_loop([_Lval - Type | LvalsTypes], ArgNum,
|
|
[Argument | Arguments]) :-
|
|
ArgName = lvn_comp_var(lvnc_arg(ArgNum)),
|
|
% Figuring out the correct GC code here is difficult, since doing that
|
|
% requires knowing the HLDS types, but here we only have the MLDS types.
|
|
% So here we just leave it blank. The caller of ml_gen_cont_param has the
|
|
% responsibility of filling this in properly if needed.
|
|
GCStmt = gc_no_stmt,
|
|
Argument = mlds_argument(ArgName, Type, GCStmt),
|
|
ml_gen_cont_params_loop(LvalsTypes, ArgNum + 1, Arguments).
|
|
|
|
:- pred ml_gen_copy_args_to_locals(assoc_list(mlds_lval, mlds_type)::in,
|
|
prog_context::in, list(mlds_stmt)::out) is det.
|
|
|
|
ml_gen_copy_args_to_locals(ArgLvalsTypes, Context, CopyStmts) :-
|
|
ml_gen_copy_args_to_locals_loop(ArgLvalsTypes, 1, Context, CopyStmts).
|
|
|
|
:- pred ml_gen_copy_args_to_locals_loop(assoc_list(mlds_lval, mlds_type)::in,
|
|
int::in, prog_context::in, list(mlds_stmt)::out) is det.
|
|
|
|
ml_gen_copy_args_to_locals_loop([], _, _, []).
|
|
ml_gen_copy_args_to_locals_loop([LocalLvalType | LocalLvalsTypes], ArgNum,
|
|
Context, [Stmt | Stmts]) :-
|
|
LocalLvalType = LocalLval - Type,
|
|
ArgName = lvn_comp_var(lvnc_arg(ArgNum)),
|
|
ArgLval = ml_local_var(ArgName, Type),
|
|
Stmt = ml_gen_assign(LocalLval, ml_lval(ArgLval), Context),
|
|
ml_gen_copy_args_to_locals_loop(LocalLvalsTypes, ArgNum + 1,
|
|
Context, Stmts).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Code for builtins.
|
|
%
|
|
|
|
ml_gen_builtin(PredId, ProcId, ArgVars, CodeModel, Context,
|
|
LocalVarDefns, FuncDefns, Stmts, !Info) :-
|
|
ml_gen_var_list(!.Info, ArgVars, ArgLvals),
|
|
ml_gen_info_get_module_info(!.Info, ModuleInfo),
|
|
ModuleName = predicate_module(ModuleInfo, PredId),
|
|
PredName = predicate_name(ModuleInfo, PredId),
|
|
builtin_ops.translate_builtin(ModuleName, PredName, ProcId, ArgLvals,
|
|
SimpleCode),
|
|
(
|
|
CodeModel = model_det,
|
|
(
|
|
SimpleCode = assign(Lval, SimpleExpr),
|
|
( if
|
|
% We need to avoid generating assignments to dummy variables
|
|
% introduced for types such as io.state.
|
|
Lval = ml_local_var(_VarName, VarType),
|
|
VarType = mercury_nb_type(ProgDataType, _),
|
|
is_type_a_dummy(ModuleInfo, ProgDataType) = is_dummy_type
|
|
then
|
|
Stmts = []
|
|
else
|
|
Rval = ml_gen_simple_expr(SimpleExpr),
|
|
Stmt = ml_gen_assign(Lval, Rval, Context),
|
|
Stmts = [Stmt]
|
|
)
|
|
;
|
|
SimpleCode = ref_assign(AddrLval, ValueLval),
|
|
( if ValueLval = ml_local_var(_ValueVarName, ValueType) then
|
|
Stmt = ml_gen_assign(
|
|
ml_mem_ref(ml_lval(AddrLval), ValueType),
|
|
ml_lval(ValueLval), Context),
|
|
Stmts = [Stmt]
|
|
else
|
|
unexpected($pred, "malformed ref_assign")
|
|
)
|
|
;
|
|
SimpleCode = test(_),
|
|
unexpected($pred, "malformed model_det builtin predicate")
|
|
;
|
|
SimpleCode = noop(_),
|
|
Stmts = []
|
|
)
|
|
;
|
|
CodeModel = model_semi,
|
|
(
|
|
SimpleCode = test(SimpleTest),
|
|
TestRval = ml_gen_simple_expr(SimpleTest),
|
|
ml_gen_set_success(TestRval, Context, Stmt, !Info),
|
|
Stmts = [Stmt]
|
|
;
|
|
( SimpleCode = ref_assign(_, _)
|
|
; SimpleCode = assign(_, _)
|
|
; SimpleCode = noop(_)
|
|
),
|
|
unexpected($pred, "malformed model_semi builtin predicate")
|
|
)
|
|
;
|
|
CodeModel = model_non,
|
|
unexpected($pred, "model_non builtin predicate")
|
|
),
|
|
LocalVarDefns = [],
|
|
FuncDefns = [].
|
|
|
|
:- func ml_gen_simple_expr(simple_expr(mlds_lval)) = mlds_rval.
|
|
|
|
ml_gen_simple_expr(leaf(Lval)) = ml_lval(Lval).
|
|
ml_gen_simple_expr(int_const(Int)) = ml_const(mlconst_int(Int)).
|
|
ml_gen_simple_expr(uint_const(UInt)) = ml_const(mlconst_uint(UInt)).
|
|
ml_gen_simple_expr(int8_const(Int8)) = ml_const(mlconst_int8(Int8)).
|
|
ml_gen_simple_expr(uint8_const(UInt8)) = ml_const(mlconst_uint8(UInt8)).
|
|
ml_gen_simple_expr(int16_const(Int16)) = ml_const(mlconst_int16(Int16)).
|
|
ml_gen_simple_expr(uint16_const(UInt16)) = ml_const(mlconst_uint16(UInt16)).
|
|
ml_gen_simple_expr(int32_const(Int32)) = ml_const(mlconst_int32(Int32)).
|
|
ml_gen_simple_expr(uint32_const(UInt32)) = ml_const(mlconst_uint32(UInt32)).
|
|
ml_gen_simple_expr(int64_const(Int64)) = ml_const(mlconst_int64(Int64)).
|
|
ml_gen_simple_expr(uint64_const(UInt64)) = ml_const(mlconst_uint64(UInt64)).
|
|
ml_gen_simple_expr(float_const(Float)) = ml_const(mlconst_float(Float)).
|
|
ml_gen_simple_expr(unary(Op, Expr)) =
|
|
ml_unop(Op, ml_gen_simple_expr(Expr)).
|
|
ml_gen_simple_expr(binary(Op, ExprA, ExprB)) =
|
|
ml_binop(Op, ml_gen_simple_expr(ExprA), ml_gen_simple_expr(ExprB)).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Find out if the specified lvals/rvals might evaluate to the addresses of
|
|
% local variables (or fields of local variables) or nested functions.
|
|
%
|
|
|
|
:- type may_yield_dangling_stack_ref
|
|
---> may_yield_dangling_stack_ref
|
|
; will_not_yield_dangling_stack_ref.
|
|
|
|
:- func may_rvals_yield_dangling_stack_ref(list(mlds_rval)) =
|
|
may_yield_dangling_stack_ref.
|
|
|
|
may_rvals_yield_dangling_stack_ref([]) = will_not_yield_dangling_stack_ref.
|
|
may_rvals_yield_dangling_stack_ref([Rval | Rvals])
|
|
= MayYieldDanglingStackRef :-
|
|
MayYieldDanglingStackRef0 =
|
|
may_rval_yield_dangling_stack_ref(Rval),
|
|
(
|
|
MayYieldDanglingStackRef0 = may_yield_dangling_stack_ref,
|
|
MayYieldDanglingStackRef = may_yield_dangling_stack_ref
|
|
;
|
|
MayYieldDanglingStackRef0 = will_not_yield_dangling_stack_ref,
|
|
MayYieldDanglingStackRef =
|
|
may_rvals_yield_dangling_stack_ref(Rvals)
|
|
).
|
|
|
|
:- func may_rval_yield_dangling_stack_ref(mlds_rval)
|
|
= may_yield_dangling_stack_ref.
|
|
|
|
may_rval_yield_dangling_stack_ref(Rval) = MayYieldDanglingStackRef :-
|
|
(
|
|
Rval = ml_lval(_Lval),
|
|
% Passing the _value_ of an lval is fine.
|
|
MayYieldDanglingStackRef = will_not_yield_dangling_stack_ref
|
|
;
|
|
Rval = ml_mkword(_Tag, SubRval),
|
|
MayYieldDanglingStackRef =
|
|
may_rval_yield_dangling_stack_ref(SubRval)
|
|
;
|
|
Rval = ml_const(Const),
|
|
MayYieldDanglingStackRef = may_const_yield_dangling_stack_ref(Const)
|
|
;
|
|
( Rval = ml_box(_Type, SubRval)
|
|
; Rval = ml_unbox(_Type, SubRval)
|
|
; Rval = ml_cast(_Type, SubRval)
|
|
; Rval = ml_unop(_Op, SubRval)
|
|
),
|
|
MayYieldDanglingStackRef = may_rval_yield_dangling_stack_ref(SubRval)
|
|
;
|
|
Rval = ml_binop(_Op, SubRvalA, SubRvalB),
|
|
MayYieldDanglingStackRefA =
|
|
may_rval_yield_dangling_stack_ref(SubRvalA),
|
|
(
|
|
MayYieldDanglingStackRefA = may_yield_dangling_stack_ref,
|
|
MayYieldDanglingStackRef = may_yield_dangling_stack_ref
|
|
;
|
|
MayYieldDanglingStackRefA = will_not_yield_dangling_stack_ref,
|
|
MayYieldDanglingStackRef =
|
|
may_rval_yield_dangling_stack_ref(SubRvalB)
|
|
)
|
|
;
|
|
Rval = ml_mem_addr(Lval),
|
|
% Passing the address of an lval is a problem,
|
|
% if that lval names a local variable.
|
|
MayYieldDanglingStackRef =
|
|
may_lval_yield_dangling_stack_ref(Lval)
|
|
;
|
|
Rval = ml_vector_common_row_addr(_VectorCommon, RowRval),
|
|
MayYieldDanglingStackRef =
|
|
may_rval_yield_dangling_stack_ref(RowRval)
|
|
;
|
|
( Rval = ml_scalar_common(_)
|
|
; Rval = ml_scalar_common_addr(_)
|
|
; Rval = ml_self(_)
|
|
),
|
|
MayYieldDanglingStackRef = may_yield_dangling_stack_ref
|
|
).
|
|
|
|
% Find out if the specified lval might be a local variable
|
|
% (or a field of a local variable).
|
|
%
|
|
:- func may_lval_yield_dangling_stack_ref(mlds_lval)
|
|
= may_yield_dangling_stack_ref.
|
|
|
|
may_lval_yield_dangling_stack_ref(Lval) = MayYieldDanglingStackRef :-
|
|
(
|
|
Lval = ml_local_var(_Var0, _),
|
|
MayYieldDanglingStackRef = may_yield_dangling_stack_ref
|
|
;
|
|
Lval = ml_field(_MaybeTag, Rval, _, _, _),
|
|
MayYieldDanglingStackRef = may_rval_yield_dangling_stack_ref(Rval)
|
|
;
|
|
( Lval = ml_mem_ref(_, _)
|
|
; Lval = ml_global_var(_, _)
|
|
; Lval = ml_target_global_var_ref(_)
|
|
),
|
|
% We assume that the addresses of local variables are only ever
|
|
% passed down to other functions, or assigned to, so a mem_ref lval
|
|
% can never refer to a local variable.
|
|
MayYieldDanglingStackRef = will_not_yield_dangling_stack_ref
|
|
).
|
|
|
|
% Find out if the specified const might be the address of a local variable
|
|
% or nested function.
|
|
%
|
|
% The addresses of local variables are probably not consts, at least
|
|
% not unless those variables are declared as static (i.e. `one_copy'),
|
|
% so it might be safe to allow all data_addr_consts here, but currently
|
|
% we just take a conservative approach.
|
|
%
|
|
:- func may_const_yield_dangling_stack_ref(mlds_rval_const)
|
|
= may_yield_dangling_stack_ref.
|
|
|
|
may_const_yield_dangling_stack_ref(Const) = MayYieldDanglingStackRef :-
|
|
(
|
|
Const = mlconst_code_addr(CodeAddr),
|
|
( if function_is_local(CodeAddr) then
|
|
MayYieldDanglingStackRef = may_yield_dangling_stack_ref
|
|
else
|
|
MayYieldDanglingStackRef = will_not_yield_dangling_stack_ref
|
|
)
|
|
;
|
|
Const = mlconst_data_addr_local_var(_VarName),
|
|
MayYieldDanglingStackRef = may_yield_dangling_stack_ref
|
|
;
|
|
( Const = mlconst_true
|
|
; Const = mlconst_false
|
|
; Const = mlconst_int(_)
|
|
; Const = mlconst_uint(_)
|
|
; Const = mlconst_int8(_)
|
|
; Const = mlconst_uint8(_)
|
|
; Const = mlconst_int16(_)
|
|
; Const = mlconst_uint16(_)
|
|
; Const = mlconst_int32(_)
|
|
; Const = mlconst_uint32(_)
|
|
; Const = mlconst_int64(_)
|
|
; Const = mlconst_uint64(_)
|
|
; Const = mlconst_enum(_, _)
|
|
; Const = mlconst_char(_)
|
|
; Const = mlconst_foreign(_, _, _)
|
|
; Const = mlconst_float(_)
|
|
; Const = mlconst_string(_)
|
|
; Const = mlconst_multi_string(_)
|
|
; Const = mlconst_named_const(_, _)
|
|
; Const = mlconst_data_addr_rtti(_, _)
|
|
; Const = mlconst_data_addr_tabling(_, _)
|
|
; Const = mlconst_data_addr_global_var(_, _)
|
|
; Const = mlconst_null(_)
|
|
),
|
|
MayYieldDanglingStackRef = will_not_yield_dangling_stack_ref
|
|
).
|
|
|
|
% Check whether the specified function is defined locally (i.e. as a
|
|
% nested function).
|
|
%
|
|
:- pred function_is_local(mlds_code_addr::in) is semidet.
|
|
|
|
function_is_local(CodeAddr) :-
|
|
CodeAddr = mlds_code_addr(QualFuncLabel, _Signature),
|
|
QualFuncLabel = qual_func_label(_ModuleName, FuncLabel),
|
|
FuncLabel = mlds_func_label(_ProcLabel, MaybeAux),
|
|
require_complete_switch [MaybeAux]
|
|
(
|
|
MaybeAux = proc_func,
|
|
fail
|
|
;
|
|
( MaybeAux = proc_aux_func(_)
|
|
; MaybeAux = gc_trace_for_proc_func
|
|
; MaybeAux = gc_trace_for_proc_aux_func(_)
|
|
)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module ml_backend.ml_call_gen.
|
|
%---------------------------------------------------------------------------%
|