mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-17 23:05:21 +00:00
Estimated hours taken: 140 Add an alternative to code_exprn that does eager code generation (code_exprn always does lazy code generation). Its main advantages are that the new code is significantly simpler, and that it does not generate unnecessary shuffling code. Its main disadvantage, which is that it does not eliminate the creation of unneeded cells, can be eliminated by switching on --unneeded-code. For now, you can select the use of the new code generator with the --no-lazy-code option (which was previously present but unused). This will be made the default later, after I do more performance tests. Var_locn contains stricter self-checks than code_exprn does. This required modifications to some other parts of the code generator to ensure that the self-checks do not fail unnecessarily. (This mostly took the form of explicitly killing off dead variables before calling code_info__clear_all_registers, which would complain about losing the last record of the value of a variable that was alive as far as it knew.) To make my changes simpler, also took the opportunity to simplify parts of the code generator which were handing around rvals that in fact had to be wrappers around lvals, by handing around the lvals directly. Testing this change also required fixing an old bug which prevented compiling the library with -O1 --trace deep, together with the usual intermodule optimization. The bug is that a library module reads predicates from builtin.opt or private_builtin.opt, does not eliminate them because of the -O1, and then tries to generate traced code for them. However, this fails because the builtin modules contain some predicates that cannot be made to conform to typeinfo-liveness, which is required by tracing. compiler/var_locn.m: The new module that implements eager code generation. compiler/follow_vars.m: Improve the follow_vars pass, since eager code generation requires better follow_vars information. We now generate correct information for generic calls, and record not only where some vars (e.g. those which appear as input arguments of following calls) should be put, but also which registers are not reserved for those variables and are thus available for other variables. compiler/hlds_goal.m: Modify the follow_vars field of the goal_info to record the number of the first non-reserved register. compiler/code_info.m: Replace the general-purpose predicate code_info__cache_exprn, which associated a variable with an rval without generating code, with a set of special-purpose predicates such as code_info__assign_const_to_var and code_info__assign_cell_to_var, some of which can generate code. These new predicates and some older ones (e.g. code_info__setup_call) now choose at runtime whether to call code_exprn or var_locn. The basis for the decision is checking whether the code_info structure contains an exprn_info or a var_locn_info. This is decided in code_info__init based on the value of the lazy_code option, and maintained unchanged from then on. Rename some predicates to better reflect their current possible behaviors. compiler/unify_gen.m: Call the new special-purpose predicates in code_info instead of code_info__cache_exprn. Replace an incorrect clause with a call to error, since that clause could never be invoked. compiler/call_gen.m: Hand over the task of generating the args of generic calls to code_info, since it already has code to do the right thing, which includes reserving the registers to be used for the input args. Notify the rest of the code generator after the last use of non-forward-live variables, in order to avoid spurious calls to error (it is an error to clobber the last location of a live variable). Notify the rest of the code generator when generic calls overwrite registers, to allow the proper consistency checks to be made. If an output variable is singleton, then do not make it known to the code generator. It never will never become dead, and may thus cause a spurious compiler abort if its storage is ever clobbered. Export a predicate for use by follow_vars. Factor out some common code. Call the new preds in code_info where necessary. compiler/pragma_c_gen.m: Notify the rest of the code generator after the last use of non-forward-live variables, in order to avoid spurious calls to error (it is an error to clobber the last location of a live variable). If an output variable is singleton, then do not make it known to the code generator. It never will never become dead, and may thus cause a spurious compiler abort if its storage is ever clobbered. When using var_locn, ensure that none of the input arguments of a model_semi pragma_c_code is assigned to r1. If we did, and the last reference to the value of that argument was after an assignment to SUCCESS_INDICATOR, the C compiler would be forced to generate code to shuffle the value of the argument out of the way. compiler/code_exprn.m: Minor changes to return lvals directly instead of lvals wrapped inside rvals and to conform the new format of follow_vars. Do not include the registers reserved by follow_vars in the search for a spare register. compiler/lookup_switch.m: compiler/switch_gen.m: Fix an old bug that did not matter with code_exprn but does matter with var_locn: the branch end structure was being computed in the wrong place. compiler/disj_gen.m: At the ends of non-last disjuncts, kill off the variables that we needed to know inside the disjunct but won't need to know after the disjunct, in order to avoid error messages about throwing away their state. The variables affected are those which are needed only by the resumption point of the next disjunct, not by enclosing resumption points or forward execution. compiler/arg_info.m: Associate an lval, not an rval, with each argument. compiler/*.m: Minor changes to conform to (a) the new format of follow_vars, (b) the replacement of rvals containing lvals by lvals. compiler/code_util.m: Add some utility predicates for var_locn.m. compiler/exprn_aux.m: Add some utility functions for var_locn.m. Export a predicate for var_locn.m. compiler/handle_options.m: If --no-lazy-code is set, switch on the "optimizations" on whose presence it depends. compiler/mercury_compile.m: compiler/code_gen.m: Turn off tracing for predicates that don't obey typeinfo liveness for backend_by_preds and backend_by_phases respectively. Look up options in the globals structure in the module_info, not in the globals structure in the I/O state, since this is where we turn off tracing. (We should later make sure that other parts of the compiler are also consistent on this issue.) compiler/stack_layout.m: Throw away any continuation_info structures that belong to predicates that don't obey typeinfo liveness.
854 lines
30 KiB
Mathematica
854 lines
30 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 1994-2000 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: code_util.m.
|
|
%
|
|
% various utilities routines for code generation and recognition
|
|
% of builtins.
|
|
%
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- module code_util.
|
|
|
|
:- interface.
|
|
|
|
:- import_module prog_data, hlds_module, hlds_pred, hlds_goal, hlds_data.
|
|
:- import_module rtti, llds.
|
|
|
|
:- import_module bool, list, assoc_list, set, std_util.
|
|
|
|
% Create a code address which holds the address of the specified
|
|
% procedure.
|
|
% The `immed' argument should be `no' if the the caller wants the
|
|
% returned address to be valid from everywhere in the program.
|
|
% If being valid from within the current procedure is enough,
|
|
% this argument should be `yes' wrapped around the value of the
|
|
% --procs-per-c-function option and the current procedure id.
|
|
% Using an address that is only valid from within the current
|
|
% procedure may make jumps more efficient.
|
|
|
|
:- type immed == maybe(pair(int, pred_proc_id)).
|
|
|
|
:- pred code_util__make_entry_label(module_info, pred_id, proc_id,
|
|
immed, code_addr).
|
|
:- mode code_util__make_entry_label(in, in, in, in, out) is det.
|
|
|
|
:- pred code_util__make_entry_label_from_rtti(rtti_proc_label, immed,
|
|
code_addr).
|
|
:- mode code_util__make_entry_label_from_rtti(in, in, out) is det.
|
|
|
|
% Create a label which holds the address of the specified procedure,
|
|
% which must be defined in the current module (procedures that are
|
|
% imported from other modules have representations only as code_addrs,
|
|
% not as labels, since their address is not known at C compilation
|
|
% time).
|
|
% The fourth argument has the same meaning as for
|
|
% code_util__make_entry_label.
|
|
|
|
:- pred code_util__make_local_entry_label(module_info, pred_id, proc_id,
|
|
immed, label).
|
|
:- mode code_util__make_local_entry_label(in, in, in, in, out) is det.
|
|
|
|
% Create a label internal to a Mercury procedure.
|
|
:- pred code_util__make_internal_label(module_info, pred_id, proc_id, int,
|
|
label).
|
|
:- mode code_util__make_internal_label(in, in, in, in, out) is det.
|
|
|
|
:- pred code_util__make_proc_label(module_info, pred_id, proc_id, proc_label).
|
|
:- mode code_util__make_proc_label(in, in, in, out) is det.
|
|
|
|
% code_util__make_user_proc_label(ModuleName, PredIsImported,
|
|
% PredOrFunc, ModuleName, PredName, Arity, ProcId, Label):
|
|
% Make a proc_label for a user-defined procedure.
|
|
%
|
|
% The PredIsImported argument should be the result of
|
|
% calling pred_info_is_imported.
|
|
|
|
:- pred code_util__make_user_proc_label(module_name, bool,
|
|
pred_or_func, module_name, string, arity, proc_id, proc_label).
|
|
:- mode code_util__make_user_proc_label(in, in,
|
|
in, in, in, in, in, out) is det.
|
|
|
|
:- pred code_util__make_uni_label(module_info, type_id, proc_id, proc_label).
|
|
:- mode code_util__make_uni_label(in, in, in, out) is det.
|
|
|
|
:- pred code_util__extract_proc_label_from_code_addr(code_addr, proc_label).
|
|
:- mode code_util__extract_proc_label_from_code_addr(in, out) is det.
|
|
|
|
:- pred code_util__extract_proc_label_from_label(label, proc_label).
|
|
:- mode code_util__extract_proc_label_from_label(in, out) is det.
|
|
|
|
:- pred code_util__arg_loc_to_register(arg_loc, lval).
|
|
:- mode code_util__arg_loc_to_register(in, out) is det.
|
|
|
|
:- pred code_util__max_mentioned_reg(list(lval), int).
|
|
:- mode code_util__max_mentioned_reg(in, out) is det.
|
|
|
|
% Determine whether a goal might allocate some heap space,
|
|
% i.e. whether it contains any construction unifications
|
|
% or predicate calls. BEWARE that this predicate is only
|
|
% an approximation, used to decide whether or not to try to
|
|
% reclaim the heap space; currently it fails even for some
|
|
% goals which do allocate heap space, such as construction
|
|
% of boxed constants.
|
|
|
|
:- pred code_util__goal_may_allocate_heap(hlds_goal).
|
|
:- mode code_util__goal_may_allocate_heap(in) is semidet.
|
|
|
|
:- pred code_util__goal_list_may_allocate_heap(list(hlds_goal)).
|
|
:- mode code_util__goal_list_may_allocate_heap(in) is semidet.
|
|
|
|
% Negate a condition.
|
|
% This is used mostly just to make the generated code more readable.
|
|
|
|
:- pred code_util__neg_rval(rval, rval).
|
|
:- mode code_util__neg_rval(in, out) is det.
|
|
|
|
:- pred code_util__negate_the_test(list(instruction), list(instruction)).
|
|
:- mode code_util__negate_the_test(in, out) is det.
|
|
|
|
% code_util__compiler_generated(PredInfo) succeeds iff
|
|
% the PredInfo is for a compiler generated instance of a
|
|
% type-specific special_pred (i.e. one of the __Unify__,
|
|
% __Index__, or __Compare__ predicates generated as a
|
|
% type-specific instance of unify/2, index/2, or compare/3).
|
|
%
|
|
% XXX The name of this predicate is misleading, because there
|
|
% are other kinds of compiler-generated predicates, e.g. those
|
|
% for lambda expressions, those generated by higher-order
|
|
% specialization, ordinary type specialization, deforestation,
|
|
% etc., for which this predicate does not succeed.
|
|
|
|
:- pred code_util__compiler_generated(pred_info).
|
|
:- mode code_util__compiler_generated(in) is semidet.
|
|
|
|
:- pred code_util__predinfo_is_builtin(pred_info).
|
|
:- mode code_util__predinfo_is_builtin(in) is semidet.
|
|
|
|
:- pred code_util__builtin_state(module_info, pred_id, proc_id, builtin_state).
|
|
:- mode code_util__builtin_state(in, in, in, out) is det.
|
|
|
|
% Find out how a function symbol (constructor) is represented
|
|
% in the given type.
|
|
|
|
:- pred code_util__cons_id_to_tag(cons_id, type, module_info, cons_tag).
|
|
:- mode code_util__cons_id_to_tag(in, in, in, out) is det.
|
|
|
|
% Succeed if the given goal cannot encounter a context
|
|
% that causes any variable to be flushed to its stack slot.
|
|
% If such a goal needs a resume point, and that resume point cannot
|
|
% be backtracked to once control leaves the goal, then the only entry
|
|
% point we need for the resume point is the one with the resume
|
|
% variables in their original locations.
|
|
|
|
:- pred code_util__cannot_stack_flush(hlds_goal).
|
|
:- mode code_util__cannot_stack_flush(in) is semidet.
|
|
|
|
% Succeed if the given goal cannot fail before encountering a context
|
|
% that forces all variables to be flushed to their stack slots.
|
|
% If such a goal needs a resume point, the only entry point we need
|
|
% is the stack entry point.
|
|
|
|
:- pred code_util__cannot_fail_before_stack_flush(hlds_goal).
|
|
:- mode code_util__cannot_fail_before_stack_flush(in) is semidet.
|
|
|
|
% code_util__count_recursive_calls(Goal, PredId, ProcId, Min, Max)
|
|
% Given that we are in predicate PredId and procedure ProcId,
|
|
% return the minimum and maximum number of recursive calls that
|
|
% an execution of Goal may encounter.
|
|
|
|
:- pred code_util__count_recursive_calls(hlds_goal, pred_id, proc_id,
|
|
int, int).
|
|
:- mode code_util__count_recursive_calls(in, in, in, out, out) is det.
|
|
|
|
% Return the set of locations occupied by output arguments.
|
|
|
|
:- pred code_util__output_args(assoc_list(prog_var, arg_info), set(lval)).
|
|
:- mode code_util__output_args(in, out) is det.
|
|
|
|
% These predicates return the set of lvals referenced in an rval
|
|
% and an lval respectively. Lvals referenced indirectly through
|
|
% lvals of the form var(_) are not counted.
|
|
|
|
:- pred code_util__lvals_in_rval(rval, list(lval)).
|
|
:- mode code_util__lvals_in_rval(in, out) is det.
|
|
|
|
:- pred code_util__lvals_in_lval(lval, list(lval)).
|
|
:- mode code_util__lvals_in_lval(in, out) is det.
|
|
|
|
:- pred code_util__lvals_in_lvals(list(lval), list(lval)).
|
|
:- mode code_util__lvals_in_lvals(in, out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module builtin_ops, type_util, special_pred.
|
|
|
|
:- import_module char, int, string, set, map, term, varset.
|
|
:- import_module require, std_util, assoc_list.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
code_util__make_entry_label(ModuleInfo, PredId, ProcId, Immed, ProcAddr) :-
|
|
RttiProcLabel = rtti__make_proc_label(ModuleInfo, PredId, ProcId),
|
|
code_util__make_entry_label_from_rtti(RttiProcLabel, Immed, ProcAddr).
|
|
|
|
code_util__make_entry_label_from_rtti(RttiProcLabel, Immed, ProcAddr) :-
|
|
(
|
|
(
|
|
RttiProcLabel^is_imported = yes
|
|
;
|
|
RttiProcLabel^is_pseudo_imported = yes,
|
|
% only the (in, in) mode of unification is imported
|
|
hlds_pred__in_in_unification_proc_id(
|
|
RttiProcLabel^proc_id)
|
|
)
|
|
->
|
|
code_util__make_proc_label_from_rtti(RttiProcLabel, ProcLabel),
|
|
ProcAddr = imported(ProcLabel)
|
|
;
|
|
code_util__make_local_entry_label_from_rtti(RttiProcLabel,
|
|
Immed, Label),
|
|
ProcAddr = label(Label)
|
|
).
|
|
|
|
code_util__make_local_entry_label(ModuleInfo, PredId, ProcId, Immed, Label) :-
|
|
RttiProcLabel = rtti__make_proc_label(ModuleInfo, PredId, ProcId),
|
|
code_util__make_local_entry_label_from_rtti(RttiProcLabel,
|
|
Immed, Label).
|
|
|
|
:- pred code_util__make_local_entry_label_from_rtti(rtti_proc_label, immed,
|
|
label).
|
|
:- mode code_util__make_local_entry_label_from_rtti(in, in, out) is det.
|
|
|
|
code_util__make_local_entry_label_from_rtti(RttiProcLabel, Immed, Label) :-
|
|
code_util__make_proc_label_from_rtti(RttiProcLabel, ProcLabel),
|
|
(
|
|
Immed = no,
|
|
% If we want to define the label or use it to put it
|
|
% into a data structure, a label that is usable only
|
|
% within the current C module won't do.
|
|
( RttiProcLabel^is_exported = yes ->
|
|
Label = exported(ProcLabel)
|
|
;
|
|
Label = local(ProcLabel)
|
|
)
|
|
;
|
|
Immed = yes(ProcsPerFunc - proc(CurPredId, CurProcId)),
|
|
choose_local_label_type(ProcsPerFunc, CurPredId, CurProcId,
|
|
RttiProcLabel^pred_id, RttiProcLabel^proc_id,
|
|
ProcLabel, Label)
|
|
).
|
|
|
|
|
|
:- pred choose_local_label_type(int, pred_id, proc_id,
|
|
pred_id, proc_id, proc_label, label).
|
|
:- mode choose_local_label_type(in, in, in, in, in, in, out) is det.
|
|
|
|
choose_local_label_type(ProcsPerFunc, CurPredId, CurProcId,
|
|
PredId, ProcId, ProcLabel, Label) :-
|
|
(
|
|
% If we want to branch to the label now,
|
|
% we prefer a form that are usable only within
|
|
% the current C module, since it is likely
|
|
% to be faster.
|
|
(
|
|
ProcsPerFunc = 0
|
|
;
|
|
PredId = CurPredId,
|
|
ProcId = CurProcId
|
|
)
|
|
->
|
|
Label = c_local(ProcLabel)
|
|
;
|
|
Label = local(ProcLabel)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
code_util__make_internal_label(ModuleInfo, PredId, ProcId, LabelNum, Label) :-
|
|
code_util__make_proc_label(ModuleInfo, PredId, ProcId, ProcLabel),
|
|
Label = local(ProcLabel, LabelNum).
|
|
|
|
code_util__make_proc_label(ModuleInfo, PredId, ProcId, ProcLabel) :-
|
|
RttiProcLabel = rtti__make_proc_label(ModuleInfo, PredId, ProcId),
|
|
code_util__make_proc_label_from_rtti(RttiProcLabel, ProcLabel).
|
|
|
|
:- pred code_util__make_proc_label_from_rtti(rtti_proc_label, proc_label).
|
|
:- mode code_util__make_proc_label_from_rtti(in, out) is det.
|
|
|
|
code_util__make_proc_label_from_rtti(RttiProcLabel, ProcLabel) :-
|
|
RttiProcLabel = rtti_proc_label(PredOrFunc, ThisModule,
|
|
PredModule, PredName, PredArity, ArgTypes, _PredId, ProcId,
|
|
_VarSet, _HeadVars, _ArgModes, _CodeModel,
|
|
IsImported, _IsPseudoImported, _IsExported,
|
|
IsSpecialPredInstance),
|
|
(
|
|
IsSpecialPredInstance = yes
|
|
->
|
|
(
|
|
special_pred_get_type(PredName, ArgTypes, Type),
|
|
type_to_type_id(Type, TypeId, _),
|
|
% All type_ids here should be module qualified,
|
|
% since builtin types are handled separately in
|
|
% polymorphism.m.
|
|
TypeId = qualified(TypeModule, TypeName) - TypeArity
|
|
->
|
|
(
|
|
ThisModule \= TypeModule,
|
|
PredName = "__Unify__",
|
|
\+ hlds_pred__in_in_unification_proc_id(ProcId)
|
|
->
|
|
DefiningModule = ThisModule
|
|
;
|
|
DefiningModule = TypeModule
|
|
),
|
|
ProcLabel = special_proc(DefiningModule, PredName,
|
|
TypeModule, TypeName, TypeArity, ProcId)
|
|
;
|
|
string__append_list(["code_util__make_proc_label:\n",
|
|
"cannot make label for special pred `",
|
|
PredName, "'"], ErrorMessage),
|
|
error(ErrorMessage)
|
|
)
|
|
;
|
|
code_util__make_user_proc_label(ThisModule, IsImported,
|
|
PredOrFunc, PredModule, PredName, PredArity,
|
|
ProcId, ProcLabel)
|
|
).
|
|
|
|
code_util__make_user_proc_label(ThisModule, PredIsImported,
|
|
PredOrFunc, PredModule, PredName, PredArity,
|
|
ProcId, ProcLabel) :-
|
|
|
|
(
|
|
% Work out which module supplies the code for
|
|
% the predicate.
|
|
ThisModule \= PredModule,
|
|
PredIsImported = no
|
|
->
|
|
% This predicate is a specialized version of
|
|
% a pred from a `.opt' file.
|
|
DefiningModule = ThisModule
|
|
;
|
|
DefiningModule = PredModule
|
|
),
|
|
ProcLabel = proc(DefiningModule, PredOrFunc,
|
|
PredModule, PredName, PredArity, ProcId).
|
|
|
|
code_util__make_uni_label(ModuleInfo, TypeId, UniModeNum, ProcLabel) :-
|
|
module_info_name(ModuleInfo, ModuleName),
|
|
( TypeId = qualified(TypeModule, TypeName) - Arity ->
|
|
( hlds_pred__in_in_unification_proc_id(UniModeNum) ->
|
|
Module = TypeModule
|
|
;
|
|
Module = ModuleName
|
|
),
|
|
ProcLabel = special_proc(Module, "__Unify__", TypeModule,
|
|
TypeName, Arity, UniModeNum)
|
|
;
|
|
error("code_util__make_uni_label: unqualified type_id")
|
|
).
|
|
|
|
code_util__extract_proc_label_from_code_addr(CodeAddr, ProcLabel) :-
|
|
( code_util__proc_label_from_code_addr(CodeAddr, ProcLabelPrime) ->
|
|
ProcLabel = ProcLabelPrime
|
|
;
|
|
error("code_util__extract_label_from_code_addr failed")
|
|
).
|
|
|
|
:- pred code_util__proc_label_from_code_addr(code_addr::in,
|
|
proc_label::out) is semidet.
|
|
|
|
code_util__proc_label_from_code_addr(CodeAddr, ProcLabel) :-
|
|
(
|
|
CodeAddr = label(Label),
|
|
code_util__extract_proc_label_from_label(Label, ProcLabel)
|
|
;
|
|
CodeAddr = imported(ProcLabel)
|
|
).
|
|
|
|
code_util__extract_proc_label_from_label(local(ProcLabel, _), ProcLabel).
|
|
code_util__extract_proc_label_from_label(c_local(ProcLabel), ProcLabel).
|
|
code_util__extract_proc_label_from_label(local(ProcLabel), ProcLabel).
|
|
code_util__extract_proc_label_from_label(exported(ProcLabel), ProcLabel).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
code_util__arg_loc_to_register(ArgLoc, reg(r, ArgLoc)).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
code_util__max_mentioned_reg(Lvals, MaxRegNum) :-
|
|
code_util__max_mentioned_reg_2(Lvals, 0, MaxRegNum).
|
|
|
|
:- pred code_util__max_mentioned_reg_2(list(lval)::in, int::in, int::out)
|
|
is det.
|
|
|
|
code_util__max_mentioned_reg_2([], MaxRegNum, MaxRegNum).
|
|
code_util__max_mentioned_reg_2([Lval | Lvals], MaxRegNum0, MaxRegNum) :-
|
|
( Lval = reg(r, N) ->
|
|
int__max(MaxRegNum0, N, MaxRegNum1)
|
|
;
|
|
MaxRegNum1 = MaxRegNum0
|
|
),
|
|
code_util__max_mentioned_reg_2(Lvals, MaxRegNum1, MaxRegNum).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
code_util__predinfo_is_builtin(PredInfo) :-
|
|
pred_info_module(PredInfo, ModuleName),
|
|
pred_info_name(PredInfo, PredName),
|
|
pred_info_arity(PredInfo, Arity),
|
|
hlds_pred__initial_proc_id(ProcId),
|
|
code_util__is_inline_builtin(ModuleName, PredName, ProcId, Arity).
|
|
|
|
code_util__builtin_state(ModuleInfo, PredId, ProcId, BuiltinState) :-
|
|
module_info_pred_info(ModuleInfo, PredId, PredInfo),
|
|
pred_info_module(PredInfo, ModuleName),
|
|
pred_info_name(PredInfo, PredName),
|
|
pred_info_arity(PredInfo, Arity),
|
|
( code_util__is_inline_builtin(ModuleName, PredName, ProcId, Arity) ->
|
|
BuiltinState = inline_builtin
|
|
;
|
|
BuiltinState = not_builtin
|
|
).
|
|
|
|
:- pred code_util__is_inline_builtin(module_name, string, proc_id, arity).
|
|
:- mode code_util__is_inline_builtin(in, in, in, in) is semidet.
|
|
|
|
code_util__is_inline_builtin(ModuleName, PredName, ProcId, Arity) :-
|
|
Arity =< 3,
|
|
prog_varset_init(VarSet),
|
|
varset__new_vars(VarSet, Arity, Args, _),
|
|
builtin_ops__translate_builtin(ModuleName, PredName, ProcId, Args, _).
|
|
|
|
:- pred prog_varset_init(prog_varset::out) is det.
|
|
prog_varset_init(VarSet) :- varset__init(VarSet).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% XXX The name of this predicate is misleading -- see the comment
|
|
% in the declaration.
|
|
code_util__compiler_generated(PredInfo) :-
|
|
pred_info_name(PredInfo, PredName),
|
|
pred_info_arity(PredInfo, PredArity),
|
|
special_pred_name_arity(_, _, PredName, PredArity).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% This code may _look_ nondeterministic, but it's really semidet,
|
|
% and Mercury is smart enough to know this.
|
|
|
|
code_util__goal_may_allocate_heap(Goal - _GoalInfo) :-
|
|
code_util__goal_may_allocate_heap_2(Goal).
|
|
|
|
:- pred code_util__goal_may_allocate_heap_2(hlds_goal_expr).
|
|
:- mode code_util__goal_may_allocate_heap_2(in) is semidet.
|
|
|
|
code_util__goal_may_allocate_heap_2(generic_call(_, _, _, _)).
|
|
code_util__goal_may_allocate_heap_2(call(_, _, _, Builtin, _, _)) :-
|
|
Builtin \= inline_builtin.
|
|
code_util__goal_may_allocate_heap_2(
|
|
unify(_, _, _, construct(_,_,Args,_,_,_,_), _)) :-
|
|
Args = [_|_].
|
|
code_util__goal_may_allocate_heap_2(some(_Vars, _, Goal)) :-
|
|
code_util__goal_may_allocate_heap(Goal).
|
|
code_util__goal_may_allocate_heap_2(not(Goal)) :-
|
|
code_util__goal_may_allocate_heap(Goal).
|
|
code_util__goal_may_allocate_heap_2(conj(Goals)) :-
|
|
code_util__goal_list_may_allocate_heap(Goals).
|
|
code_util__goal_may_allocate_heap_2(disj(Goals, _)) :-
|
|
code_util__goal_list_may_allocate_heap(Goals).
|
|
code_util__goal_may_allocate_heap_2(switch(_Var, _Det, Cases, _)) :-
|
|
code_util__cases_may_allocate_heap(Cases).
|
|
code_util__goal_may_allocate_heap_2(if_then_else(_Vars, A, B, C, _)) :-
|
|
(
|
|
code_util__goal_may_allocate_heap(A)
|
|
;
|
|
code_util__goal_may_allocate_heap(B)
|
|
;
|
|
code_util__goal_may_allocate_heap(C)
|
|
).
|
|
|
|
:- pred code_util__cases_may_allocate_heap(list(case)).
|
|
:- mode code_util__cases_may_allocate_heap(in) is semidet.
|
|
|
|
code_util__cases_may_allocate_heap([case(_, Goal) | _]) :-
|
|
code_util__goal_may_allocate_heap(Goal).
|
|
code_util__cases_may_allocate_heap([_ | Cases]) :-
|
|
code_util__cases_may_allocate_heap(Cases).
|
|
|
|
code_util__goal_list_may_allocate_heap([Goal | _]) :-
|
|
code_util__goal_may_allocate_heap(Goal).
|
|
code_util__goal_list_may_allocate_heap([_ | Goals]) :-
|
|
code_util__goal_list_may_allocate_heap(Goals).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Negate a condition.
|
|
% This is used mostly just to make the generated code more readable.
|
|
|
|
code_util__neg_rval(Rval, NegRval) :-
|
|
( code_util__neg_rval_2(Rval, NegRval0) ->
|
|
NegRval = NegRval0
|
|
;
|
|
NegRval = unop(not, Rval)
|
|
).
|
|
|
|
:- pred code_util__neg_rval_2(rval, rval).
|
|
:- mode code_util__neg_rval_2(in, out) is semidet.
|
|
|
|
code_util__neg_rval_2(const(Const), const(NegConst)) :-
|
|
(
|
|
Const = true, NegConst = false
|
|
;
|
|
Const = false, NegConst = true
|
|
).
|
|
code_util__neg_rval_2(unop(not, Rval), Rval).
|
|
code_util__neg_rval_2(binop(Op, X, Y), binop(NegOp, X, Y)) :-
|
|
code_util__neg_op(Op, NegOp).
|
|
|
|
:- pred code_util__neg_op(binary_op, binary_op).
|
|
:- mode code_util__neg_op(in, out) is semidet.
|
|
|
|
code_util__neg_op(eq, ne).
|
|
code_util__neg_op(ne, eq).
|
|
code_util__neg_op(<, >=).
|
|
code_util__neg_op(<=, >).
|
|
code_util__neg_op(>, <=).
|
|
code_util__neg_op(>=, <).
|
|
code_util__neg_op(str_eq, str_ne).
|
|
code_util__neg_op(str_ne, str_eq).
|
|
code_util__neg_op(str_lt, str_ge).
|
|
code_util__neg_op(str_le, str_gt).
|
|
code_util__neg_op(str_gt, str_le).
|
|
code_util__neg_op(str_ge, str_lt).
|
|
code_util__neg_op(float_eq, float_ne).
|
|
code_util__neg_op(float_ne, float_eq).
|
|
code_util__neg_op(float_lt, float_ge).
|
|
code_util__neg_op(float_le, float_gt).
|
|
code_util__neg_op(float_gt, float_le).
|
|
code_util__neg_op(float_ge, float_lt).
|
|
|
|
code_util__negate_the_test([], _) :-
|
|
error("code_util__negate_the_test on empty list").
|
|
code_util__negate_the_test([Instr0 | Instrs0], Instrs) :-
|
|
( Instr0 = if_val(Test, Target) - Comment ->
|
|
code_util__neg_rval(Test, NewTest),
|
|
Instrs = [if_val(NewTest, Target) - Comment]
|
|
;
|
|
code_util__negate_the_test(Instrs0, Instrs1),
|
|
Instrs = [Instr0 | Instrs1]
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
code_util__cons_id_to_tag(int_const(X), _, _, int_constant(X)).
|
|
code_util__cons_id_to_tag(float_const(X), _, _, float_constant(X)).
|
|
code_util__cons_id_to_tag(string_const(X), _, _, string_constant(X)).
|
|
code_util__cons_id_to_tag(code_addr_const(P,M), _, _, code_addr_constant(P,M)).
|
|
code_util__cons_id_to_tag(pred_const(P,M,E), _, _, pred_closure_tag(P,M,E)).
|
|
code_util__cons_id_to_tag(type_ctor_info_const(M,T,A), _, _,
|
|
type_ctor_info_constant(M,T,A)).
|
|
code_util__cons_id_to_tag(base_typeclass_info_const(M,C,_,N), _, _,
|
|
base_typeclass_info_constant(M,C,N)).
|
|
code_util__cons_id_to_tag(tabling_pointer_const(PredId,ProcId), _, _,
|
|
tabling_pointer_constant(PredId,ProcId)).
|
|
code_util__cons_id_to_tag(cons(Name, Arity), Type, ModuleInfo, Tag) :-
|
|
(
|
|
% handle the `character' type specially
|
|
Type = term__functor(term__atom("character"), [], _),
|
|
Name = unqualified(ConsName),
|
|
string__char_to_string(Char, ConsName)
|
|
->
|
|
char__to_int(Char, CharCode),
|
|
Tag = int_constant(CharCode)
|
|
;
|
|
% Use the type to determine the type_id
|
|
( type_to_type_id(Type, TypeId0, _) ->
|
|
TypeId = TypeId0
|
|
;
|
|
% the type-checker should ensure that this never happens
|
|
error("code_util__cons_id_to_tag: invalid type")
|
|
),
|
|
|
|
% Given the type_id, lookup up the constructor tag
|
|
% table for that type
|
|
module_info_types(ModuleInfo, TypeTable),
|
|
map__lookup(TypeTable, TypeId, TypeDefn),
|
|
hlds_data__get_type_defn_body(TypeDefn, TypeBody),
|
|
(
|
|
TypeBody = du_type(_, ConsTable0, _, _)
|
|
->
|
|
ConsTable = ConsTable0
|
|
;
|
|
% this should never happen
|
|
error(
|
|
"code_util__cons_id_to_tag: type is not d.u. type?"
|
|
)
|
|
),
|
|
% Finally look up the cons_id in the table
|
|
map__lookup(ConsTable, cons(Name, Arity), Tag)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
code_util__cannot_stack_flush(GoalExpr - _) :-
|
|
code_util__cannot_stack_flush_2(GoalExpr).
|
|
|
|
:- pred code_util__cannot_stack_flush_2(hlds_goal_expr).
|
|
:- mode code_util__cannot_stack_flush_2(in) is semidet.
|
|
|
|
code_util__cannot_stack_flush_2(unify(_, _, _, Unify, _)) :-
|
|
Unify \= complicated_unify(_, _, _).
|
|
code_util__cannot_stack_flush_2(call(_, _, _, BuiltinState, _, _)) :-
|
|
BuiltinState = inline_builtin.
|
|
code_util__cannot_stack_flush_2(conj(Goals)) :-
|
|
code_util__cannot_stack_flush_goals(Goals).
|
|
code_util__cannot_stack_flush_2(switch(_, _, Cases, _)) :-
|
|
code_util__cannot_stack_flush_cases(Cases).
|
|
|
|
:- pred code_util__cannot_stack_flush_goals(list(hlds_goal)).
|
|
:- mode code_util__cannot_stack_flush_goals(in) is semidet.
|
|
|
|
code_util__cannot_stack_flush_goals([]).
|
|
code_util__cannot_stack_flush_goals([Goal | Goals]) :-
|
|
code_util__cannot_stack_flush(Goal),
|
|
code_util__cannot_stack_flush_goals(Goals).
|
|
|
|
:- pred code_util__cannot_stack_flush_cases(list(case)).
|
|
:- mode code_util__cannot_stack_flush_cases(in) is semidet.
|
|
|
|
code_util__cannot_stack_flush_cases([]).
|
|
code_util__cannot_stack_flush_cases([case(_, Goal) | Cases]) :-
|
|
code_util__cannot_stack_flush(Goal),
|
|
code_util__cannot_stack_flush_cases(Cases).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
code_util__cannot_fail_before_stack_flush(GoalExpr - GoalInfo) :-
|
|
goal_info_get_determinism(GoalInfo, Detism),
|
|
determinism_components(Detism, CanFail, _),
|
|
( CanFail = cannot_fail ->
|
|
true
|
|
;
|
|
code_util__cannot_fail_before_stack_flush_2(GoalExpr)
|
|
).
|
|
|
|
:- pred code_util__cannot_fail_before_stack_flush_2(hlds_goal_expr).
|
|
:- mode code_util__cannot_fail_before_stack_flush_2(in) is semidet.
|
|
|
|
code_util__cannot_fail_before_stack_flush_2(conj(Goals)) :-
|
|
code_util__cannot_fail_before_stack_flush_conj(Goals).
|
|
|
|
:- pred code_util__cannot_fail_before_stack_flush_conj(list(hlds_goal)).
|
|
:- mode code_util__cannot_fail_before_stack_flush_conj(in) is semidet.
|
|
|
|
code_util__cannot_fail_before_stack_flush_conj([]).
|
|
code_util__cannot_fail_before_stack_flush_conj([Goal | Goals]) :-
|
|
Goal = GoalExpr - GoalInfo,
|
|
(
|
|
(
|
|
GoalExpr = call(_, _, _, BuiltinState, _, _),
|
|
BuiltinState \= inline_builtin
|
|
;
|
|
GoalExpr = generic_call(_, _, _, _)
|
|
)
|
|
->
|
|
true
|
|
;
|
|
goal_info_get_determinism(GoalInfo, Detism),
|
|
determinism_components(Detism, cannot_fail, _)
|
|
->
|
|
code_util__cannot_fail_before_stack_flush_conj(Goals)
|
|
;
|
|
fail
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
code_util__count_recursive_calls(Goal - _, PredId, ProcId, Min, Max) :-
|
|
code_util__count_recursive_calls_2(Goal, PredId, ProcId, Min, Max).
|
|
|
|
:- pred code_util__count_recursive_calls_2(hlds_goal_expr, pred_id, proc_id,
|
|
int, int).
|
|
:- mode code_util__count_recursive_calls_2(in, in, in, out, out) is det.
|
|
|
|
code_util__count_recursive_calls_2(not(Goal), PredId, ProcId, Min, Max) :-
|
|
code_util__count_recursive_calls(Goal, PredId, ProcId, Min, Max).
|
|
code_util__count_recursive_calls_2(some(_, _, Goal),
|
|
PredId, ProcId, Min, Max) :-
|
|
code_util__count_recursive_calls(Goal, PredId, ProcId, Min, Max).
|
|
code_util__count_recursive_calls_2(unify(_, _, _, _, _), _, _, 0, 0).
|
|
code_util__count_recursive_calls_2(generic_call(_, _, _, _), _, _,
|
|
0, 0).
|
|
code_util__count_recursive_calls_2(pragma_foreign_code(_, _, _, _, _, _, _, _),
|
|
_, _, 0, 0).
|
|
code_util__count_recursive_calls_2(call(CallPredId, CallProcId, _, _, _, _),
|
|
PredId, ProcId, Count, Count) :-
|
|
(
|
|
PredId = CallPredId,
|
|
ProcId = CallProcId
|
|
->
|
|
Count = 1
|
|
;
|
|
Count = 0
|
|
).
|
|
code_util__count_recursive_calls_2(conj(Goals), PredId, ProcId, Min, Max) :-
|
|
code_util__count_recursive_calls_conj(Goals, PredId, ProcId, 0, 0,
|
|
Min, Max).
|
|
code_util__count_recursive_calls_2(par_conj(Goals, _), PredId, ProcId, Min, Max) :-
|
|
code_util__count_recursive_calls_conj(Goals, PredId, ProcId, 0, 0, Min, Max).
|
|
code_util__count_recursive_calls_2(disj(Goals, _), PredId, ProcId, Min, Max) :-
|
|
code_util__count_recursive_calls_disj(Goals, PredId, ProcId, Min, Max).
|
|
code_util__count_recursive_calls_2(switch(_, _, Cases, _), PredId, ProcId,
|
|
Min, Max) :-
|
|
code_util__count_recursive_calls_cases(Cases, PredId, ProcId, Min, Max).
|
|
code_util__count_recursive_calls_2(if_then_else(_, Cond, Then, Else, _),
|
|
PredId, ProcId, Min, Max) :-
|
|
code_util__count_recursive_calls(Cond, PredId, ProcId, CMin, CMax),
|
|
code_util__count_recursive_calls(Then, PredId, ProcId, TMin, TMax),
|
|
code_util__count_recursive_calls(Else, PredId, ProcId, EMin, EMax),
|
|
CTMin is CMin + TMin,
|
|
CTMax is CMax + TMax,
|
|
int__min(CTMin, EMin, Min),
|
|
int__max(CTMax, EMax, Max).
|
|
code_util__count_recursive_calls_2(bi_implication(_, _),
|
|
_, _, _, _) :-
|
|
% these should have been expanded out by now
|
|
error("code_util__count_recursive_calls_2: unexpected bi_implication").
|
|
|
|
:- pred code_util__count_recursive_calls_conj(list(hlds_goal),
|
|
pred_id, proc_id, int, int, int, int).
|
|
:- mode code_util__count_recursive_calls_conj(in, in, in, in, in, out, out)
|
|
is det.
|
|
|
|
code_util__count_recursive_calls_conj([], _, _, Min, Max, Min, Max).
|
|
code_util__count_recursive_calls_conj([Goal | Goals], PredId, ProcId,
|
|
Min0, Max0, Min, Max) :-
|
|
code_util__count_recursive_calls(Goal, PredId, ProcId, Min1, Max1),
|
|
Min2 is Min0 + Min1,
|
|
Max2 is Max0 + Max1,
|
|
code_util__count_recursive_calls_conj(Goals, PredId, ProcId,
|
|
Min2, Max2, Min, Max).
|
|
|
|
:- pred code_util__count_recursive_calls_disj(list(hlds_goal),
|
|
pred_id, proc_id, int, int).
|
|
:- mode code_util__count_recursive_calls_disj(in, in, in, out, out) is det.
|
|
|
|
code_util__count_recursive_calls_disj([], _, _, 0, 0).
|
|
code_util__count_recursive_calls_disj([Goal | Goals], PredId, ProcId,
|
|
Min, Max) :-
|
|
( Goals = [] ->
|
|
code_util__count_recursive_calls(Goal, PredId, ProcId,
|
|
Min, Max)
|
|
;
|
|
code_util__count_recursive_calls(Goal, PredId, ProcId,
|
|
Min0, Max0),
|
|
code_util__count_recursive_calls_disj(Goals, PredId, ProcId,
|
|
Min1, Max1),
|
|
int__min(Min0, Min1, Min),
|
|
int__max(Max0, Max1, Max)
|
|
).
|
|
|
|
:- pred code_util__count_recursive_calls_cases(list(case),
|
|
pred_id, proc_id, int, int).
|
|
:- mode code_util__count_recursive_calls_cases(in, in, in, out, out) is det.
|
|
|
|
code_util__count_recursive_calls_cases([], _, _, _, _) :-
|
|
error("empty cases in code_util__count_recursive_calls_cases").
|
|
code_util__count_recursive_calls_cases([case(_, Goal) | Cases], PredId, ProcId,
|
|
Min, Max) :-
|
|
( Cases = [] ->
|
|
code_util__count_recursive_calls(Goal, PredId, ProcId,
|
|
Min, Max)
|
|
;
|
|
code_util__count_recursive_calls(Goal, PredId, ProcId,
|
|
Min0, Max0),
|
|
code_util__count_recursive_calls_cases(Cases, PredId, ProcId,
|
|
Min1, Max1),
|
|
int__min(Min0, Min1, Min),
|
|
int__max(Max0, Max1, Max)
|
|
).
|
|
|
|
code_util__output_args([], LiveVals) :-
|
|
set__init(LiveVals).
|
|
code_util__output_args([_V - arg_info(Loc, Mode) | Args], Vs) :-
|
|
code_util__output_args(Args, Vs0),
|
|
(
|
|
Mode = top_out
|
|
->
|
|
code_util__arg_loc_to_register(Loc, Reg),
|
|
set__insert(Vs0, Reg, Vs)
|
|
;
|
|
Vs = Vs0
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
code_util__lvals_in_lvals([], []).
|
|
code_util__lvals_in_lvals([First | Rest], Lvals) :-
|
|
code_util__lvals_in_lval(First, FirstLvals),
|
|
code_util__lvals_in_lvals(Rest, RestLvals),
|
|
list__append(FirstLvals, RestLvals, Lvals).
|
|
|
|
code_util__lvals_in_rval(lval(Lval), [Lval | Lvals]) :-
|
|
code_util__lvals_in_lval(Lval, Lvals).
|
|
code_util__lvals_in_rval(var(_), []).
|
|
code_util__lvals_in_rval(create(_, _, _, _, _, _, _), []).
|
|
code_util__lvals_in_rval(mkword(_, Rval), Lvals) :-
|
|
code_util__lvals_in_rval(Rval, Lvals).
|
|
code_util__lvals_in_rval(const(_), []).
|
|
code_util__lvals_in_rval(unop(_, Rval), Lvals) :-
|
|
code_util__lvals_in_rval(Rval, Lvals).
|
|
code_util__lvals_in_rval(binop(_, Rval1, Rval2), Lvals) :-
|
|
code_util__lvals_in_rval(Rval1, Lvals1),
|
|
code_util__lvals_in_rval(Rval2, Lvals2),
|
|
list__append(Lvals1, Lvals2, Lvals).
|
|
code_util__lvals_in_rval(mem_addr(MemRef), Lvals) :-
|
|
code_util__lvals_in_mem_ref(MemRef, Lvals).
|
|
|
|
code_util__lvals_in_lval(reg(_, _), []).
|
|
code_util__lvals_in_lval(stackvar(_), []).
|
|
code_util__lvals_in_lval(framevar(_), []).
|
|
code_util__lvals_in_lval(succip, []).
|
|
code_util__lvals_in_lval(maxfr, []).
|
|
code_util__lvals_in_lval(curfr, []).
|
|
code_util__lvals_in_lval(succip(Rval), Lvals) :-
|
|
code_util__lvals_in_rval(Rval, Lvals).
|
|
code_util__lvals_in_lval(redofr(Rval), Lvals) :-
|
|
code_util__lvals_in_rval(Rval, Lvals).
|
|
code_util__lvals_in_lval(redoip(Rval), Lvals) :-
|
|
code_util__lvals_in_rval(Rval, Lvals).
|
|
code_util__lvals_in_lval(succfr(Rval), Lvals) :-
|
|
code_util__lvals_in_rval(Rval, Lvals).
|
|
code_util__lvals_in_lval(prevfr(Rval), Lvals) :-
|
|
code_util__lvals_in_rval(Rval, Lvals).
|
|
code_util__lvals_in_lval(hp, []).
|
|
code_util__lvals_in_lval(sp, []).
|
|
code_util__lvals_in_lval(field(_, Rval1, Rval2), Lvals) :-
|
|
code_util__lvals_in_rval(Rval1, Lvals1),
|
|
code_util__lvals_in_rval(Rval2, Lvals2),
|
|
list__append(Lvals1, Lvals2, Lvals).
|
|
code_util__lvals_in_lval(lvar(_), []).
|
|
code_util__lvals_in_lval(temp(_, _), []).
|
|
code_util__lvals_in_lval(mem_ref(Rval), Lvals) :-
|
|
code_util__lvals_in_rval(Rval, Lvals).
|
|
|
|
:- pred code_util__lvals_in_mem_ref(mem_ref, list(lval)).
|
|
:- mode code_util__lvals_in_mem_ref(in, out) is det.
|
|
|
|
code_util__lvals_in_mem_ref(stackvar_ref(_), []).
|
|
code_util__lvals_in_mem_ref(framevar_ref(_), []).
|
|
code_util__lvals_in_mem_ref(heap_ref(Rval, _, _), Lvals) :-
|
|
code_util__lvals_in_rval(Rval, Lvals).
|
|
|
|
%-----------------------------------------------------------------------------%
|