Files
mercury/compiler/call_gen.m
Zoltan Somogyi 18817d62d0 Record more than a pred_proc_id for each method.
Class and instance definitions both contain lists of methods,
predicates and/or functions, that each have one or more procedures.
Until now, we represented the methods in class and instance definitions
as lists of nothing more than pred_proc_ids. This fact complicated
several operations,

- partly because there was no simple way to tell which procedures
  were part of the same predicate or function, and

- partly because the order of the list is important (we identify
  each method procedure in our equivalent of vtables with a number,
  which is simply the procedure's position in this list), but there was
  absolutely no information about recorded about this.

This diff therefore replaces the lists of pred_proc_ids with lists of
method_infos. Each method_info contains

- the method procedure number, i.e. the vtable index,

- the pred_or_func, sym_name and user arity of the predicate or function
  that the method procedure is a part of, to make it simple to test
  whether two method_infos represent different modes of the same predicate
  or function, or not,

- the original pred_proc_id of the method procedure, which never changes,
  and

- the current pred_proc_id, which program transformations *can* change.

compiler/hlds_class.m:
    Make the change above in the representations of class and instance
    definitions.

    Put the fields of both types into a better order, by putting
    related fields next to each other.

    Put a notag wrapper around method procedure numbers to prevent
    accidentally mixing them up with plain integers.

    Add some utility functions.

compiler/prog_data.m:
    Replace three fields containing pred_or_func, sym_name and arity
    in the parse tree representation of instance methods with just one,
    which contains all three pieces of info. This makes it easier to operate
    on them as a unit.

    Change the representation of methods defined by clauses from a list
    of clauses to a cord of clauses, since this supports constant-time
    append.

compiler/hlds_goal.m:
    Switch from plain ints to the new notag representation of method
    procedure numbers in method call goals.

compiler/add_class.m:
    Simplify the code for adding new classes to the HLDS.

    Give some predicates better names.

compiler/check_typeclass.m:
    Significantly simplify the code for that generates the pred_infos and
    proc_infos implementing all the methods of an instances definition,
    and construct lists of method_infos instead of lists of pred_proc_ids.

    Give some predicates better names.

    Some error messages about problems in instance definitions started with

        In instance declaration for class/arity:

    while others started with

        In instance declaration for class(module_a.foo, module_b.bar):

    Replace both with

        In instance declaration for class(foo, bar):

    because it contains more useful information than the first, and less
    non-useful information than the second. Improve the wording of some
    error messages.

    Factor out some common code.

compiler/prog_mode.m:
compiler/prog_type.m:
compiler/prog_util.m:
    Generalize the existing predicates for stripping "builtin.m" module
    qualifiers from sym_names, cons_ids, insts, types and modes
    to allow also the stripping of *all* module qualifiers. This capability
    is now used when we print an instance's type vector as a context
    for diagnostics about problems inside instance definitions.

compiler/add_pred.m:
    Add a mechanism for returning the pred_id of a newly created pred_info,
    whether or not it was declared using a predmode declaration. This
    capability is now needed by add_class.m.

    Move the code creating an error message into its own function, and export
    that function for add_class.m.

compiler/polymorphism_type_info.m:
    Fix some comment rot.

compiler/base_typeclass_info.m:
compiler/call_gen.m:
compiler/dead_proc_elim.m:
compiler/deep_profiling.m:
compiler/direct_arg_in_out.m:
compiler/error_msg_inst.m:
compiler/float_regs.m:
compiler/get_dependencies.m:
compiler/higher_order.m:
compiler/hlds_error_util.m:
compiler/hlds_out_goal.m:
compiler/hlds_out_typeclass_table.m:
compiler/instance_method_clauses.m:
compiler/intermod.m:
compiler/make_hlds_error.m:
compiler/ml_call_gen.m:
compiler/mode_errors.m:
compiler/modes.m:
compiler/module_qual.qualify_items.m:
compiler/old_type_constraints.m:
compiler/parse_class.m:
compiler/parse_tree_out.m:
compiler/parse_tree_out_inst.m:
compiler/polymorphism_post_copy.m:
compiler/polymorphism_type_class_info.m:
compiler/prog_item.m:
compiler/prog_rep.m:
compiler/recompilation.usage.m:
compiler/state_var.m:
compiler/type_class_info.m:
compiler/typecheck_debug.m:
compiler/typecheck_error_type_assign.m:
compiler/typecheck_errors.m:
compiler/typecheck_msgs.m:
compiler/unused_imports.m:
compiler/xml_documentation.m:
    Conform to the changes above.

tests/invalid/bug476.err_exp:
tests/invalid/tc_err1.err_exp:
tests/invalid/tc_err2.err_exp:
tests/invalid/typeclass_bogus_method.err_exp:
tests/invalid/typeclass_missing_mode.err_exp:
tests/invalid/typeclass_missing_mode_2.err_exp:
tests/invalid/typeclass_mode.err_exp:
tests/invalid/typeclass_mode_2.err_exp:
tests/invalid/typeclass_mode_3.err_exp:
tests/invalid/typeclass_mode_4.err_exp:
tests/invalid/typeclass_test_10.err_exp:
tests/invalid/typeclass_test_3.err_exp:
tests/invalid/typeclass_test_4.err_exp:
tests/invalid/typeclass_test_5.err_exp:
tests/invalid/typeclass_test_9.err_exp:
    Expect the updated wording of some error messages.
2022-11-22 02:27:33 +11:00

853 lines
32 KiB
Mathematica

%---------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%---------------------------------------------------------------------------%
% Copyright (C) 1994-2012 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: call_gen.m.
% Authors: conway, zs.
%
% This module provides predicates for generating procedure calls,
% including calls to higher-order pred variables.
%
%---------------------------------------------------------------------------%
:- module ll_backend.call_gen.
:- interface.
:- import_module hlds.
:- import_module hlds.code_model.
:- import_module hlds.hlds_goal.
:- import_module hlds.hlds_pred.
:- import_module libs.
:- import_module libs.globals.
:- import_module ll_backend.code_info.
:- import_module ll_backend.code_loc_dep.
:- import_module ll_backend.llds.
:- import_module parse_tree.
:- import_module parse_tree.prog_data.
:- import_module assoc_list.
:- import_module list.
%---------------------------------------------------------------------------%
:- pred generate_call(code_model::in, pred_id::in, proc_id::in,
list(prog_var)::in, hlds_goal_info::in, llds_code::out,
code_info::in, code_info::out, code_loc_dep::in, code_loc_dep::out) is det.
:- pred generate_generic_call(code_model::in, generic_call::in,
list(prog_var)::in, list(mer_mode)::in, arg_reg_type_info::in,
determinism::in, hlds_goal_info::in, llds_code::out,
code_info::in, code_info::out, code_loc_dep::in, code_loc_dep::out) is det.
:- type known_call_variant
---> ho_call_known_num
; ho_call_unknown.
% generic_call_info(Globals, GenericCall, NumImmediateInputArgsR,
% NumImmediateInputArgsF, CodeAddr, SpecifierArgInfos,
% FirstImmediateInputReg, HoCallVariant).
%
:- pred generic_call_info(globals::in, generic_call::in, int::in, int::in,
code_addr::out, assoc_list(prog_var, arg_info)::out, int::out,
known_call_variant::out) is det.
:- pred generate_builtin(code_model::in, pred_id::in, proc_id::in,
list(prog_var)::in, llds_code::out,
code_info::in, code_info::out, code_loc_dep::in, code_loc_dep::out) is det.
:- pred input_arg_locs(assoc_list(prog_var, arg_info)::in,
assoc_list(prog_var, arg_loc)::out) is det.
:- pred output_arg_locs(assoc_list(prog_var, arg_info)::in,
assoc_list(prog_var, arg_loc)::out) is det.
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
:- implementation.
:- import_module backend_libs.
:- import_module backend_libs.builtin_ops.
:- import_module hlds.arg_info.
:- import_module hlds.hlds_class.
:- import_module hlds.hlds_llds.
:- import_module hlds.hlds_module.
:- import_module hlds.instmap.
:- import_module libs.options.
:- import_module ll_backend.code_util.
:- import_module ll_backend.continuation_info.
:- import_module ll_backend.trace_gen.
:- import_module mdbcomp.
:- import_module mdbcomp.goal_path.
:- import_module parse_tree.prog_data_event.
:- import_module parse_tree.prog_event.
:- import_module parse_tree.prog_type.
:- import_module parse_tree.set_of_var.
:- import_module parse_tree.var_table.
:- import_module bool.
:- import_module cord.
:- import_module int.
:- import_module maybe.
:- import_module pair.
:- import_module require.
:- import_module set.
:- import_module string.
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
generate_call(CodeModel, PredId, ProcId, ArgVars, GoalInfo, Code, !CI, !CLD) :-
% Find out which arguments are input and which are output.
ArgInfo = get_pred_proc_arginfo(!.CI, PredId, ProcId),
assoc_list.from_corresponding_lists(ArgVars, ArgInfo, ArgsInfos),
% Save the necessary vars on the stack and move the input args
% to their registers.
setup_call(GoalInfo, ArgsInfos, LiveVals, SetupCode, !.CI, !CLD),
kill_dead_input_vars(ArgsInfos, GoalInfo, NonLiveOutputs, !CLD),
% Figure out what the call model is.
prepare_for_call(CodeModel, GoalInfo, CallModel, TraceResetCode,
!CI, !CLD),
% Make the call. Note that the construction of CallCode will be moved
% *after* the code that computes ReturnLiveLvalues.
get_module_info(!.CI, ModuleInfo),
Address = make_proc_entry_label(!.CI, ModuleInfo, PredId, ProcId,
for_immediate_call),
get_next_label(ReturnLabel, !CI),
call_gen.call_comment(!.CI, PredId, CodeModel, CallComment),
Context = goal_info_get_context(GoalInfo),
GoalId = goal_info_get_goal_id(GoalInfo),
get_maybe_containing_goal_map(!.CI, MaybeContainingGoalMap),
(
MaybeContainingGoalMap = yes(ContainingGoalMap),
GoalPath = goal_id_to_forward_path(ContainingGoalMap, GoalId),
MaybeGoalPath = yes(GoalPath)
;
MaybeContainingGoalMap = no,
MaybeGoalPath = no
),
CallCode = from_list([
llds_instr(livevals(LiveVals), ""),
llds_instr(llcall(Address, code_label(ReturnLabel), ReturnLiveLvalues,
Context, MaybeGoalPath, CallModel), CallComment),
llds_instr(label(ReturnLabel), "continuation label")
]),
% Figure out what variables will be live at the return point, and where,
% for use in the accurate garbage collector, and in the debugger.
get_instmap(!.CLD, InstMap),
InstMapDelta = goal_info_get_instmap_delta(GoalInfo),
apply_instmap_delta(InstMapDelta, InstMap, ReturnInstMap),
% Update the code generator state to reflect the situation after the call.
handle_return(ArgsInfos, GoalInfo, NonLiveOutputs,
ReturnInstMap, ReturnLiveLvalues, !.CI, !CLD),
% If the call can fail, generate code to check for and handle the failure.
remember_position(!.CLD, AfterReturn),
handle_call_failure(CodeModel, GoalInfo, FailHandlingCode, !CI, !.CLD),
reset_to_position(AfterReturn, !.CI, !:CLD),
( if
goal_info_has_feature(GoalInfo, feature_debug_self_tail_rec_call),
get_maybe_trace_info(!.CI, MaybeTraceInfo),
MaybeTraceInfo = yes(TraceInfo)
then
generate_tailrec_event_code(TraceInfo, ArgsInfos, GoalId, Context,
TraceTailRecResetAndEventCode, TailRecLabel, !CI, !CLD),
JumpCode = from_list([
llds_instr(livevals(LiveVals), ""),
llds_instr(goto(code_label(TailRecLabel)),
"tail recursive jump")
]),
Code = SetupCode ++ TraceTailRecResetAndEventCode ++ JumpCode
else
Code = SetupCode ++ TraceResetCode ++ CallCode ++ FailHandlingCode
).
%---------------------------------------------------------------------------%
generate_generic_call(OuterCodeModel, GenericCall, ArgVars, Modes,
MaybeRegTypes, Det, GoalInfo, Code, !CI, !CLD) :-
% For a generic_call, we split the arguments into inputs and outputs,
% put the inputs in the locations expected by mercury.do_call_closure in
% runtime/mercury_ho_call.c, generate the call to that code, and pick up
% the outputs from the locations that we know the runtime system leaves
% them in.
% `cast' differs from the other generic call types in that there is no
% address. Also, live_vars.m assumes that casts do not require live
% variables to be saved to the stack.
(
( GenericCall = higher_order(_, _, _, _)
; GenericCall = class_method(_, _, _, _)
),
generate_main_generic_call(OuterCodeModel, GenericCall, ArgVars, Modes,
MaybeRegTypes, Det, GoalInfo, Code, !CI, !CLD)
;
GenericCall = event_call(EventName),
generate_event_call(EventName, ArgVars, GoalInfo, Code, !CI, !CLD)
;
GenericCall = cast(_),
( if ArgVars = [InputArgVar, OutputArgVar] then
get_var_table(!.CI, VarTable),
lookup_var_entry(VarTable, InputArgVar, InputArgEntry),
InputArgVarIsDummy = InputArgEntry ^ vte_is_dummy,
(
InputArgVarIsDummy = is_dummy_type,
% Dummy types don't actually have values, which is
% normally harmless. However, using the constant zero means
% that we don't need to allocate space for an existentially
% typed version of a dummy type. Using the constant zero
% also avoids keeping pointers to memory that could be freed.
InputArgRval = int_const(0)
;
InputArgVarIsDummy = is_not_dummy_type,
InputArgRval = leaf(InputArgVar)
),
generate_assign_builtin(OutputArgVar, InputArgRval, Code, !CLD)
else
unexpected($pred, "invalid type/inst cast call")
)
).
:- pred generate_main_generic_call(code_model::in, generic_call::in,
list(prog_var)::in, list(mer_mode)::in, arg_reg_type_info::in,
determinism::in, hlds_goal_info::in, llds_code::out,
code_info::in, code_info::out, code_loc_dep::in, code_loc_dep::out) is det.
generate_main_generic_call(_OuterCodeModel, GenericCall, ArgVars, Modes,
MaybeRegTypes, Det, GoalInfo, Code, !CI, !CLD) :-
get_module_info(!.CI, ModuleInfo),
arg_info.generic_call_arg_reg_types(ModuleInfo, GenericCall,
ArgVars, MaybeRegTypes, ArgRegTypes),
get_var_table(!.CI, VarTable),
arg_info.compute_in_and_out_vars_sep_regs(ModuleInfo, VarTable,
ArgVars, Modes, ArgRegTypes, InVarsR, InVarsF, OutVarsR, OutVarsF),
module_info_get_globals(ModuleInfo, Globals),
generic_call_info(Globals, GenericCall, length(InVarsR), length(InVarsF),
CodeAddr, SpecifierArgInfos, FirstImmInputR, HoCallVariant),
FirstImmInputF = 1,
determinism_to_code_model(Det, CodeModel),
(
CodeModel = model_semi,
FirstOutputR = 2
;
( CodeModel = model_det
; CodeModel = model_non
),
FirstOutputR = 1
),
FirstOutputF = 1,
give_vars_consecutive_arg_infos(InVarsR, reg_r, FirstImmInputR, top_in,
InVarArgInfosR),
give_vars_consecutive_arg_infos(InVarsF, reg_f, FirstImmInputF, top_in,
InVarArgInfosF),
give_vars_consecutive_arg_infos(OutVarsR, reg_r, FirstOutputR, top_out,
OutArgsInfosR),
give_vars_consecutive_arg_infos(OutVarsF, reg_f, FirstOutputF, top_out,
OutArgsInfosF),
ArgInfos = list.condense([SpecifierArgInfos,
InVarArgInfosR, InVarArgInfosF, OutArgsInfosR, OutArgsInfosF]),
% Save the necessary vars on the stack and move the input args defined
% by variables to their registers.
setup_call(GoalInfo, ArgInfos, LiveVals0, SetupCode, !.CI, !CLD),
kill_dead_input_vars(ArgInfos, GoalInfo, NonLiveOutputs, !CLD),
% Move the input args not defined by variables to their registers.
% Setting up these arguments last results in slightly more efficient code,
% since we can use their registers when placing the variables.
generic_call_nonvar_setup(GenericCall, HoCallVariant, InVarsR, InVarsF,
NonVarCode, !CLD),
extra_livevals(FirstImmInputR, ExtraLiveVals),
set.insert_list(ExtraLiveVals, LiveVals0, LiveVals),
prepare_for_call(CodeModel, GoalInfo, CallModel, TraceCode, !CI, !CLD),
% Make the call.
get_next_label(ReturnLabel, !CI),
Context = goal_info_get_context(GoalInfo),
GoalId = goal_info_get_goal_id(GoalInfo),
% Figure out what variables will be live at the return point, and where,
% for use in the accurate garbage collector, and in the debugger.
get_instmap(!.CLD, InstMap),
InstMapDelta = goal_info_get_instmap_delta(GoalInfo),
apply_instmap_delta(InstMapDelta, InstMap, ReturnInstMap),
% Update the code generator state to reflect the situation after the call.
OutArgsInfos = OutArgsInfosR ++ OutArgsInfosF,
handle_return(OutArgsInfos, GoalInfo, NonLiveOutputs,
ReturnInstMap, ReturnLiveLvalues, !.CI, !CLD),
get_maybe_containing_goal_map(!.CI, MaybeContainingGoalMap),
(
MaybeContainingGoalMap = yes(ContainingGoalMap),
GoalPath = goal_id_to_forward_path(ContainingGoalMap, GoalId),
MaybeGoalPath = yes(GoalPath)
;
MaybeContainingGoalMap = no,
MaybeGoalPath = no
),
CallCode = from_list([
llds_instr(livevals(LiveVals), ""),
llds_instr(llcall(CodeAddr, code_label(ReturnLabel), ReturnLiveLvalues,
Context, MaybeGoalPath, CallModel), "Setup and call"),
llds_instr(label(ReturnLabel), "Continuation label")
]),
% If the call can fail, generate code to check for and handle the failure.
remember_position(!.CLD, AfterReturn),
handle_call_failure(CodeModel, GoalInfo, FailHandlingCode, !CI, !.CLD),
reset_to_position(AfterReturn, !.CI, !:CLD),
Code = SetupCode ++ NonVarCode ++ TraceCode ++ CallCode ++
FailHandlingCode.
%---------------------------------------------------------------------------%
:- pred generate_event_call(string::in, list(prog_var)::in, hlds_goal_info::in,
llds_code::out,
code_info::in, code_info::out, code_loc_dep::in, code_loc_dep::out) is det.
generate_event_call(EventName, Args, GoalInfo, Code, !CI, !CLD) :-
get_module_info(!.CI, ModuleInfo),
module_info_get_event_set(ModuleInfo, EventSet),
EventSpecMap = EventSet ^ event_set_spec_map,
( if
event_attributes(EventSpecMap, EventName, Attributes),
event_number(EventSpecMap, EventName, EventNumber)
then
generate_event_attributes(Attributes, Args, MaybeUserAttributes,
AttrCodes, !.CI, !CLD),
UserEventInfo = user_event_info(EventNumber, MaybeUserAttributes),
generate_user_event_code(UserEventInfo, GoalInfo, EventCode,
!CI, !CLD),
Code = cord_list_to_cord(AttrCodes) ++ EventCode
else
unexpected($pred, "bad event name")
).
:- pred generate_event_attributes(list(event_attribute)::in,
list(prog_var)::in, list(maybe(user_attribute))::out, list(llds_code)::out,
code_info::in, code_loc_dep::in, code_loc_dep::out) is det.
generate_event_attributes([], !.Vars, [], [], _CI, !CLD) :-
(
!.Vars = [_ | _],
unexpected($pred, "var")
;
!.Vars = []
).
generate_event_attributes([Attribute | Attributes], !.Vars,
[MaybeUserAttr | MaybeUserAttrs], [Code | Codes], CI, !CLD) :-
SynthCall = Attribute ^ attr_maybe_synth_call,
(
SynthCall = no,
(
!.Vars = [Var | !:Vars],
produce_variable(Var, Code, Rval, !CLD),
UserAttr = user_attribute(Rval, Var),
MaybeUserAttr = yes(UserAttr)
;
!.Vars = [],
unexpected($pred, "no var")
)
;
SynthCall = yes(_),
MaybeUserAttr = no,
Code = empty
),
generate_event_attributes(Attributes, !.Vars, MaybeUserAttrs, Codes,
CI, !CLD).
%---------------------------------------------------------------------------%
% The registers before the first reg_r input argument are all live.
%
:- pred extra_livevals(int::in, list(lval)::out) is det.
extra_livevals(FirstInput, ExtraLiveVals) :-
extra_livevals_from(1, FirstInput, ExtraLiveVals).
:- pred extra_livevals_from(int::in, int::in, list(lval)::out) is det.
extra_livevals_from(Reg, FirstInput, ExtraLiveVals) :-
( if Reg < FirstInput then
ExtraLiveVals = [reg(reg_r, Reg) | ExtraLiveVals1],
NextReg = Reg + 1,
extra_livevals_from(NextReg, FirstInput, ExtraLiveVals1)
else
ExtraLiveVals = []
).
generic_call_info(Globals, GenericCall, NumInputArgsR, NumInputArgsF,
CodeAddr, SpecifierArgInfos, FirstImmediateInputReg, HoCallVariant) :-
(
GenericCall = higher_order(PredVar, _, _, _),
Reg = reg(reg_r, 1),
SpecifierArgInfos = [PredVar - arg_info(Reg, top_in)],
globals.lookup_int_option(Globals,
max_specialized_do_call_closure, MaxSpec),
( if
MaxSpec >= 0,
NumInputArgsR =< MaxSpec,
NumInputArgsF = 0
then
CodeAddr = do_call_closure(specialized_known(NumInputArgsR)),
HoCallVariant = ho_call_known_num,
FirstImmediateInputReg = 2
else
CodeAddr = do_call_closure(generic),
HoCallVariant = ho_call_unknown,
FirstImmediateInputReg = 3
)
;
GenericCall = class_method(TCVar, _, _, _),
Reg = reg(reg_r, 1),
SpecifierArgInfos = [TCVar - arg_info(Reg, top_in)],
% XXX we do not use float registers for method calls yet
( if NumInputArgsF = 0 then
globals.lookup_int_option(Globals,
max_specialized_do_call_class_method, MaxSpec),
( if
MaxSpec >= 0,
NumInputArgsR =< MaxSpec
then
CodeAddr = do_call_class_method(
specialized_known(NumInputArgsR)),
HoCallVariant = ho_call_known_num,
FirstImmediateInputReg = 3
else
CodeAddr = do_call_class_method(generic),
HoCallVariant = ho_call_unknown,
FirstImmediateInputReg = 4
)
else
sorry($pred, "float reg inputs")
)
;
% Events and casts are generated inline.
( GenericCall = event_call(_)
; GenericCall = cast(_)
),
CodeAddr = do_not_reached,
SpecifierArgInfos = [],
FirstImmediateInputReg = 1,
HoCallVariant = ho_call_unknown % dummy; not used
).
% Some of the values that generic call passes to the dispatch routine
% to specify what code is being indirectly called come from HLDS
% variables, while the others come from constants. The ones that come
% from variables (the closure for a higher order call, the
% typeclass_info for a method call) are set up together with the
% arguments being passed the indirectly called code, since with eager
% code generation this ensures that each target register is reserved
% for the variable destined for it. This is set up by generic_call_info.
% generic_call_nonvar_setup generates code to pass to the dispatch routine
% the parts of the indirectly called code's identifier that come from
% constants.
%
:- pred generic_call_nonvar_setup(generic_call::in, known_call_variant::in,
list(prog_var)::in, list(prog_var)::in,
llds_code::out, code_loc_dep::in, code_loc_dep::out) is det.
generic_call_nonvar_setup(higher_order(_, _, _, _), HoCallVariant,
InVarsR, InVarsF, Code, !CLD) :-
(
HoCallVariant = ho_call_known_num,
Code = empty
;
HoCallVariant = ho_call_unknown,
clobber_reg(reg(reg_r, 2), !CLD),
list.length(InVarsR, NumInVarsR),
list.length(InVarsF, NumInVarsF),
NumInVars = encode_num_generic_call_vars(NumInVarsR, NumInVarsF),
Code = singleton(
llds_instr(assign(reg(reg_r, 2), const(llconst_int(NumInVars))),
"Assign number of immediate input arguments")
)
).
generic_call_nonvar_setup(class_method(_, Method, _, _), HoCallVariant,
InVarsR, InVarsF, Code, !CLD) :-
(
InVarsF = []
;
InVarsF = [_ | _],
sorry($pred, "float input reg")
),
Method = method_proc_num(MethodNum),
MethodNumConst = const(llconst_int(MethodNum)),
(
HoCallVariant = ho_call_known_num,
clobber_reg(reg(reg_r, 2), !CLD),
Code = singleton(
llds_instr(assign(reg(reg_r, 2), MethodNumConst),
"Index of class method in typeclass info")
)
;
HoCallVariant = ho_call_unknown,
clobber_regs([reg(reg_r, 2), reg(reg_r, 3)], !CLD),
list.length(InVarsR, NumInVarsR),
% Currently we do not use float registers for method calls.
NumInVarsF = 0,
NumInVars = encode_num_generic_call_vars(NumInVarsR, NumInVarsF),
Code = from_list([
llds_instr(assign(reg(reg_r, 2), MethodNumConst),
"Index of class method in typeclass info"),
llds_instr(assign(reg(reg_r, 3), const(llconst_int(NumInVars))),
"Assign number of immediate regular input arguments")
])
).
generic_call_nonvar_setup(event_call(_), _, _, _, _, !CLD) :-
unexpected($pred, "event_call").
generic_call_nonvar_setup(cast(_), _, _, _, _, !CLD) :-
unexpected($pred, "cast").
%---------------------------------------------------------------------------%
:- pred prepare_for_call(code_model::in, hlds_goal_info::in, call_model::out,
llds_code::out,
code_info::in, code_info::out, code_loc_dep::in, code_loc_dep::out) is det.
prepare_for_call(CodeModel, GoalInfo, CallModel, TraceCode, !CI, !CLD) :-
succip_is_used(!CI),
( if goal_info_has_feature(GoalInfo, feature_do_not_tailcall) then
AllowLCO = do_not_allow_lco
else
AllowLCO = allow_lco
),
(
CodeModel = model_det,
CallModel = call_model_det(AllowLCO)
;
CodeModel = model_semi,
CallModel = call_model_semidet(AllowLCO)
;
CodeModel = model_non,
may_use_nondet_tailcall(!.CLD, TailCallStatus),
CallModel = call_model_nondet(TailCallStatus),
set_resume_point_and_frame_to_unknown(!CLD)
),
trace_prepare_for_call(!.CI, TraceCode).
:- pred handle_call_failure(code_model::in, hlds_goal_info::in, llds_code::out,
code_info::in, code_info::out, code_loc_dep::in) is det.
handle_call_failure(CodeModel, GoalInfo, FailHandlingCode, !CI, !.CLD) :-
(
CodeModel = model_semi,
Detism = goal_info_get_determinism(GoalInfo),
( if Detism = detism_failure then
generate_failure(FailHandlingCode, !CI, !.CLD)
else
get_next_label(ContLab, !CI),
FailTestCode = singleton(
llds_instr(if_val(lval(reg(reg_r, 1)), code_label(ContLab)),
"test for success")
),
generate_failure(FailCode, !CI, !.CLD),
ContLabelCode = singleton(
llds_instr(label(ContLab), "")
),
FailHandlingCode = FailTestCode ++ FailCode ++ ContLabelCode
)
;
( CodeModel = model_det
; CodeModel = model_non
),
FailHandlingCode = empty
).
:- pred call_comment(code_info::in, pred_id::in, code_model::in, string::out)
is det.
call_comment(CI, PredId, CodeModel, Msg) :-
(
CodeModel = model_det,
BaseMsg = "branch to det procedure"
;
CodeModel = model_semi,
BaseMsg = "branch to semidet procedure"
;
CodeModel = model_non,
BaseMsg = "branch to nondet procedure"
),
get_auto_comments(CI, AutoComments),
(
AutoComments = yes,
get_module_info(CI, ModuleInfo),
module_info_pred_info(ModuleInfo, PredId, PredInfo),
PredName = pred_info_name(PredInfo),
Msg = BaseMsg ++ " " ++ PredName
;
AutoComments = no,
Msg = BaseMsg
).
%---------------------------------------------------------------------------%
% After we have placed all the input variables in their registers,
% we will want to clear all the registers so we can start updating
% the code generator state to reflect their contents after the call.
% (In the case of higher order calls, we may place some constant
% input arguments in registers before clearing them.) The register
% clearing code complains if it is asked to dispose of the last copy
% of a still live variable, so before we clear the registers, we must
% make forward-dead all the variables that are in this goal's
% post-death set. However, a variable may be in this set even if it
% is not live before the call, if it is bound by the call. (This can
% happen when the caller ignores some of the output arguments of the
% called procedure.) We handle such variables not by making them
% forward-dead but by simply never making them forward-live in the
% first place.
%
% ArgsInfos should list all the output arguments of the call.
% It may contain the input arguments as well; kill_dead_input_vars
% and handle_return ignore them.
%
:- pred kill_dead_input_vars(assoc_list(prog_var, arg_info)::in,
hlds_goal_info::in, set_of_progvar::out,
code_loc_dep::in, code_loc_dep::out) is det.
kill_dead_input_vars(ArgsInfos, GoalInfo, NonLiveOutputs, !CLD) :-
get_forward_live_vars(!.CLD, Liveness),
find_nonlive_outputs(ArgsInfos, Liveness, set_of_var.init, NonLiveOutputs),
goal_info_get_post_deaths(GoalInfo, PostDeaths),
set_of_var.difference(PostDeaths, NonLiveOutputs, ImmediatePostDeaths),
make_vars_forward_dead(ImmediatePostDeaths, !CLD).
:- pred handle_return(assoc_list(prog_var, arg_info)::in,
hlds_goal_info::in, set_of_progvar::in, instmap::in, list(liveinfo)::out,
code_info::in, code_loc_dep::in, code_loc_dep::out) is det.
handle_return(ArgsInfos, GoalInfo, _NonLiveOutputs, ReturnInstMap,
ReturnLiveLvalues, CI, !CLD) :-
InstMapDelta = goal_info_get_instmap_delta(GoalInfo),
( if instmap_delta_is_reachable(InstMapDelta) then
OkToDeleteAny = no
else
OkToDeleteAny = yes
),
clear_all_registers(OkToDeleteAny, !CLD),
get_forward_live_vars(!.CLD, Liveness),
rebuild_registers(ArgsInfos, Liveness, OutputArgLocs, !CLD),
generate_return_live_lvalues(CI, !.CLD, OutputArgLocs,
ReturnInstMap, OkToDeleteAny, ReturnLiveLvalues).
:- pred find_nonlive_outputs(assoc_list(prog_var, arg_info)::in,
set_of_progvar::in, set_of_progvar::in, set_of_progvar::out) is det.
find_nonlive_outputs([], _, !NonLiveOutputs).
find_nonlive_outputs([Var - arg_info(_ArgLoc, Mode) | Args],
Liveness, !NonLiveOutputs) :-
(
Mode = top_out,
( if set_of_var.member(Liveness, Var) then
true
else
set_of_var.insert(Var, !NonLiveOutputs)
)
;
( Mode = top_in
; Mode = top_unused
)
),
find_nonlive_outputs(Args, Liveness, !NonLiveOutputs).
:- pred rebuild_registers(assoc_list(prog_var, arg_info)::in,
set_of_progvar::in, assoc_list(prog_var, arg_loc)::out,
code_loc_dep::in, code_loc_dep::out) is det.
rebuild_registers([], _, [], !CLD).
rebuild_registers([Var - arg_info(ArgLoc, Mode) | Args], Liveness,
OutputArgLocs, !CLD) :-
rebuild_registers(Args, Liveness, OutputArgLocs1, !CLD),
( if
Mode = top_out,
set_of_var.member(Liveness, Var)
then
code_util.arg_loc_to_register(ArgLoc, Register),
set_var_location(Var, Register, !CLD),
OutputArgLocs = [Var - ArgLoc | OutputArgLocs1]
else
OutputArgLocs = OutputArgLocs1
).
%---------------------------------------------------------------------------%
generate_builtin(CodeModel, PredId, ProcId, Args, Code, !CI, !CLD) :-
get_module_info(!.CI, ModuleInfo),
ModuleName = predicate_module(ModuleInfo, PredId),
PredName = predicate_name(ModuleInfo, PredId),
builtin_ops.translate_builtin(ModuleName, PredName, ProcId, Args,
SimpleCode),
(
CodeModel = model_det,
(
SimpleCode = assign(Var, AssignExpr),
generate_assign_builtin(Var, AssignExpr, Code, !CLD)
;
SimpleCode = ref_assign(AddrVar, ValueVar),
produce_variable(AddrVar, AddrVarCode, AddrRval, !CLD),
produce_variable(ValueVar, ValueVarCode, ValueRval, !CLD),
StoreInstr = llds_instr(assign(mem_ref(AddrRval), ValueRval), ""),
StoreCode = singleton(StoreInstr),
Code = AddrVarCode ++ ValueVarCode ++ StoreCode
;
SimpleCode = test(_),
unexpected($pred, "malformed model_det builtin predicate")
;
SimpleCode = noop(DefinedVars),
list.foldl(magically_put_var_in_unused_reg, DefinedVars, !CLD),
Code = empty
)
;
CodeModel = model_semi,
(
SimpleCode = test(TestExpr),
generate_simple_test(TestExpr, Rval, ArgCode, !CLD),
fail_if_rval_is_false(Rval, TestCode, !CI, !CLD),
Code = ArgCode ++ TestCode
;
SimpleCode = assign(_, _),
unexpected($pred, "malformed model_semi builtin predicate")
;
SimpleCode = ref_assign(_, _),
unexpected($pred, "malformed model_semi builtin predicate")
;
SimpleCode = noop(_),
unexpected($pred, "malformed model_semi builtin predicate")
)
;
CodeModel = model_non,
unexpected($pred, "model_non builtin predicate")
).
:- pred generate_assign_builtin(prog_var::in, simple_expr(prog_var)::in,
llds_code::out, code_loc_dep::in, code_loc_dep::out) is det.
generate_assign_builtin(Var, AssignExpr, Code, !CLD) :-
( if variable_is_forward_live(!.CLD, Var) then
Rval = convert_simple_expr(AssignExpr),
assign_expr_to_var(Var, Rval, Code, !CLD)
else
Code = empty
).
:- func convert_simple_expr(simple_expr(prog_var)) = rval.
convert_simple_expr(leaf(Var)) = var(Var).
convert_simple_expr(int_const(Int)) = const(llconst_int(Int)).
convert_simple_expr(uint_const(UInt)) = const(llconst_uint(UInt)).
convert_simple_expr(int8_const(Int8)) = const(llconst_int8(Int8)).
convert_simple_expr(uint8_const(UInt8)) = const(llconst_uint8(UInt8)).
convert_simple_expr(int16_const(Int16)) = const(llconst_int16(Int16)).
convert_simple_expr(uint16_const(UInt16)) = const(llconst_uint16(UInt16)).
convert_simple_expr(int32_const(Int32)) = const(llconst_int32(Int32)).
convert_simple_expr(uint32_const(UInt32)) = const(llconst_uint32(UInt32)).
convert_simple_expr(int64_const(Int64)) = const(llconst_int64(Int64)).
convert_simple_expr(uint64_const(UInt64)) = const(llconst_uint64(UInt64)).
convert_simple_expr(float_const(Float)) = const(llconst_float(Float)).
convert_simple_expr(unary(UnOp, Expr)) =
unop(UnOp, convert_simple_expr(Expr)).
convert_simple_expr(binary(BinOp, Expr1, Expr2)) =
binop(BinOp, convert_simple_expr(Expr1), convert_simple_expr(Expr2)).
:- pred generate_simple_test(simple_expr(prog_var)::in(simple_test_expr),
rval::out, llds_code::out, code_loc_dep::in, code_loc_dep::out) is det.
generate_simple_test(TestExpr, Rval, ArgCode, !CLD) :-
(
TestExpr = binary(BinOp, X0, Y0),
X1 = convert_simple_expr(X0),
Y1 = convert_simple_expr(Y0),
generate_builtin_arg(X1, X, CodeX, !CLD),
generate_builtin_arg(Y1, Y, CodeY, !CLD),
Rval = binop(BinOp, X, Y),
ArgCode = CodeX ++ CodeY
;
TestExpr = unary(UnOp, X0),
X1 = convert_simple_expr(X0),
generate_builtin_arg(X1, X, ArgCode, !CLD),
Rval = unop(UnOp, X)
).
:- pred generate_builtin_arg(rval::in, rval::out, llds_code::out,
code_loc_dep::in, code_loc_dep::out) is det.
generate_builtin_arg(Rval0, Rval, Code, !CLD) :-
(
Rval0 = var(Var),
produce_variable(Var, Code, Rval, !CLD)
;
( Rval0 = const(_)
; Rval0 = cast(_, _)
; Rval0 = unop(_, _)
; Rval0 = binop(_, _, _)
; Rval0 = mkword(_, _)
; Rval0 = mkword_hole(_)
; Rval0 = mem_addr(_)
; Rval0 = lval(_)
),
Rval = Rval0,
Code = empty
).
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
input_arg_locs([], []).
input_arg_locs([Var - arg_info(Loc, Mode) | Args], Vs) :-
input_arg_locs(Args, Vs0),
(
Mode = top_in,
Vs = [Var - Loc | Vs0]
;
( Mode = top_out
; Mode = top_unused
),
Vs = Vs0
).
output_arg_locs([], []).
output_arg_locs([Var - arg_info(Loc, Mode) | Args], Vs) :-
output_arg_locs(Args, Vs0),
(
Mode = top_out,
Vs = [Var - Loc | Vs0]
;
( Mode = top_in
; Mode = top_unused
),
Vs = Vs0
).
%---------------------------------------------------------------------------%
:- pred give_vars_consecutive_arg_infos(list(prog_var)::in,
reg_type::in, int::in, top_functor_mode::in,
assoc_list(prog_var, arg_info)::out) is det.
give_vars_consecutive_arg_infos([], _RegType, _N, _TopFunctorMode, []).
give_vars_consecutive_arg_infos([Var | Vars], RegType, N, TopFunctorMode,
[Var - ArgInfo | ArgInfos]) :-
ArgInfo = arg_info(reg(RegType, N), TopFunctorMode),
give_vars_consecutive_arg_infos(Vars, RegType, N + 1, TopFunctorMode,
ArgInfos).
%---------------------------------------------------------------------------%
:- end_module ll_backend.call_gen.
%---------------------------------------------------------------------------%