mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-17 10:23:46 +00:00
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.
853 lines
32 KiB
Mathematica
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.
|
|
%---------------------------------------------------------------------------%
|