mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 01:13:30 +00:00
compiler/ml_code_util.m:
Move the part of the ml_gen_box_or_unbox_rval predicate that handles
the bp_native_if_possible box policy (the box policy that almost all
of its callers statically specify) into a predicate of its own,
and export this predicate.
Change the moved code from a long if-then-else chain to a shorter
if-then-else chain with embedded switches, whose structure clarifies
the relationships between the special cases that the predicate handles.
compiler/ml_args_util.m:
compiler/ml_call_gen.m:
compiler/ml_unify_gen_construct.m:
compiler/ml_unify_gen_deconstruct.m:
Call the newly exported predicate when appropriate.
1191 lines
50 KiB
Mathematica
1191 lines
50 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 1999-2011 The University of Melbourne.
|
|
% Copyright (C) 2014-2018, 2020-2025 The Mercury team.
|
|
% 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_markers.
|
|
:- 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 hlds.hlds_class.
|
|
:- import_module hlds.hlds_module.
|
|
:- import_module hlds.hlds_proc_util.
|
|
:- import_module hlds.type_util.
|
|
:- 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_type.
|
|
:- import_module parse_tree.var_table.
|
|
|
|
:- 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, 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_var_table(!.Info, VarTable),
|
|
ArgNames = ml_gen_local_var_names(VarTable, ArgVars),
|
|
PredOrFunc = generic_call_pred_or_func(GenericCall),
|
|
determinism_to_code_model(Determinism, CodeModel),
|
|
% XXX PARAMS We could call a specialized version of
|
|
% ml_gen_params_no_gc_stmts that
|
|
%
|
|
% - allocates each ArgName from VarTable as it goes along, and then
|
|
% returns them, saving a nil/cons switch on ArgNames, and
|
|
%
|
|
% - knows that every BoxedArgType is a free type var
|
|
% (the fact that they are different type vars should not matter).
|
|
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,
|
|
_Syntax),
|
|
ml_gen_var_direct(!.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,
|
|
method_proc_num(MethodNum), _ClassId, _PredName),
|
|
|
|
% Create the lval for the typeclass_info, which is also the closure
|
|
% in this case.
|
|
ml_gen_var_direct(!.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 (including coerce). 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) :-
|
|
(
|
|
ArgVars = [SrcVar, DstVar],
|
|
ml_gen_info_get_var_table(!.Info, VarTable),
|
|
lookup_var_entry(VarTable, SrcVar, SrcVarEntry),
|
|
lookup_var_entry(VarTable, DstVar, DstVarEntry),
|
|
ml_gen_var(!.Info, SrcVar, SrcVarEntry, SrcLval),
|
|
ml_gen_var(!.Info, DstVar, DstVarEntry, DstLval),
|
|
SrcVarEntry = vte(_, SrcType, _),
|
|
DstVarEntry = vte(_, DstType, DstIsDummy),
|
|
(
|
|
DstIsDummy = is_dummy_type,
|
|
Stmts = []
|
|
;
|
|
DstIsDummy = is_not_dummy_type,
|
|
ml_gen_info_get_module_info(!.Info, ModuleInfo),
|
|
ml_gen_box_or_unbox_rval_native(ModuleInfo, SrcType, DstType,
|
|
ml_lval(SrcLval), CastRval),
|
|
Assign = ml_gen_assign(DstLval, 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(DstVar, GroundTerm, !Info)
|
|
else
|
|
true
|
|
)
|
|
;
|
|
( ArgVars = []
|
|
; ArgVars = [_]
|
|
; ArgVars = [_, _, _ | _]
|
|
),
|
|
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),
|
|
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(!.Info, Context, CallContStmt),
|
|
CopyStmt = ml_gen_block([], [], CopyStmts ++ [CallContStmt], Context),
|
|
% Pop nesting level.
|
|
ml_gen_label_func(!.Info, ContFuncLabel, mlds_func_source_continuation,
|
|
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) is det.
|
|
|
|
ml_gen_cont_params(OutputArgLvalsTypes, Params) :-
|
|
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_direct_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, SimpleAssignedExpr),
|
|
( 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_assigned_expr(SimpleAssignedExpr),
|
|
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),
|
|
SimpleTest = binary_test(BinOp, LvalX, LvalY),
|
|
TestRval = ml_binop(BinOp, ml_lval(LvalX), ml_lval(LvalY)),
|
|
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_assigned_expr(simple_assigned_expr(mlds_lval))
|
|
= mlds_rval.
|
|
|
|
ml_gen_simple_assigned_expr(AssignedExpr) = Rval :-
|
|
(
|
|
AssignedExpr = assign_copy(Lval),
|
|
Rval = ml_lval(Lval)
|
|
;
|
|
AssignedExpr = assign_const(Const),
|
|
Rval = convert_simple_const(Const)
|
|
;
|
|
AssignedExpr = assign_binary(BinOp, LvalX, LvalY),
|
|
Rval = ml_binop(BinOp, ml_lval(LvalX), ml_lval(LvalY))
|
|
;
|
|
AssignedExpr = assign_binary_lc(BinOp, Const, LvalY),
|
|
Rval = ml_binop(BinOp, convert_simple_const(Const), ml_lval(LvalY))
|
|
;
|
|
AssignedExpr = assign_unary(UnOp, LvalX),
|
|
Rval = ml_unop(UnOp, ml_lval(LvalX))
|
|
).
|
|
|
|
% This function is needed because simple_const is defined in
|
|
% backend_libs.builtin_ops.m, which is not supposed to know about
|
|
% backend-specific types such as mlds_rval.
|
|
%
|
|
:- func convert_simple_const(simple_const) = mlds_rval.
|
|
|
|
convert_simple_const(int_const(Int)) = ml_const(mlconst_int(Int)).
|
|
convert_simple_const(int8_const(Int8)) = ml_const(mlconst_int8(Int8)).
|
|
convert_simple_const(int16_const(Int16)) = ml_const(mlconst_int16(Int16)).
|
|
convert_simple_const(int32_const(Int32)) = ml_const(mlconst_int32(Int32)).
|
|
convert_simple_const(int64_const(Int64)) = ml_const(mlconst_int64(Int64)).
|
|
convert_simple_const(uint_const(UInt)) = ml_const(mlconst_uint(UInt)).
|
|
convert_simple_const(uint8_const(UInt8)) = ml_const(mlconst_uint8(UInt8)).
|
|
convert_simple_const(uint16_const(UInt16)) = ml_const(mlconst_uint16(UInt16)).
|
|
convert_simple_const(uint32_const(UInt32)) = ml_const(mlconst_uint32(UInt32)).
|
|
convert_simple_const(uint64_const(UInt64)) = ml_const(mlconst_uint64(UInt64)).
|
|
convert_simple_const(float_const(Float)) = ml_const(mlconst_float(Float)).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% 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.
|
|
%---------------------------------------------------------------------------%
|