mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-17 06:47:17 +00:00
Estimated hours taken: 1 Branches: main Avoid dependencies on ll_backend.code_util in *hlds.*. compiler/code_util.m: compiler/hlds_pred.m: Move the predicates to identify builtins into hlds_pred.m. compiler/*.m: Update calls and remove unnecessary imports.
957 lines
34 KiB
Mathematica
957 lines
34 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 1994-2003 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 ll_backend__code_util.
|
|
|
|
:- interface.
|
|
|
|
:- import_module parse_tree__prog_data, hlds__hlds_module, hlds__hlds_pred.
|
|
:- import_module hlds__hlds_goal, hlds__hlds_data.
|
|
:- import_module backend_libs__rtti, ll_backend__llds.
|
|
|
|
:- import_module bool, list, 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.
|
|
|
|
:- func code_util__make_proc_label_from_rtti(rtti_proc_label) = proc_label.
|
|
|
|
% 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_ctor, 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.
|
|
|
|
:- pred code_util__goal_may_alloc_temp_frame(hlds_goal).
|
|
:- mode code_util__goal_may_alloc_temp_frame(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.
|
|
|
|
% 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 execution of 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 execution of the given goal cannot encounter a context
|
|
% that causes any variable to be flushed to its stack slot or to a
|
|
% register.
|
|
|
|
:- pred code_util__cannot_flush(hlds_goal).
|
|
:- mode code_util__cannot_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.
|
|
|
|
% 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 parse_tree__prog_util, check_hlds__type_util.
|
|
:- import_module hlds__special_pred, backend_libs__builtin_ops.
|
|
:- import_module backend_libs__code_model.
|
|
:- import_module libs__options, libs__globals.
|
|
|
|
:- 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(LabelNum, ProcLabel).
|
|
|
|
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.
|
|
|
|
code_util__make_proc_label_from_rtti(RttiProcLabel) = ProcLabel :-
|
|
RttiProcLabel = rtti_proc_label(PredOrFunc, ThisModule,
|
|
PredModule, PredName, PredArity, ArgTypes, _PredId, ProcId,
|
|
_HeadVarsWithNames, _ArgModes, _CodeModel,
|
|
IsImported, _IsPseudoImported, _IsExported,
|
|
IsSpecialPredInstance),
|
|
(
|
|
IsSpecialPredInstance = yes
|
|
->
|
|
(
|
|
special_pred_get_type(PredName, ArgTypes, Type),
|
|
type_to_ctor_and_args(Type, TypeCtor, _),
|
|
% All type_ctors other than tuples here should be
|
|
% module qualified, since builtin types are
|
|
% handled separately in polymorphism.m.
|
|
(
|
|
TypeCtor = unqualified(TypeName) - _,
|
|
type_ctor_is_tuple(TypeCtor),
|
|
mercury_public_builtin_module(TypeModule)
|
|
;
|
|
TypeCtor = qualified(TypeModule, TypeName) - _
|
|
)
|
|
->
|
|
TypeCtor = _ - 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, TypeCtor, UniModeNum, ProcLabel) :-
|
|
module_info_name(ModuleInfo, ModuleName),
|
|
( TypeCtor = 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_ctor")
|
|
).
|
|
|
|
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__goal_may_allocate_heap(Goal) :-
|
|
code_util__goal_may_allocate_heap(Goal, yes).
|
|
|
|
code_util__goal_list_may_allocate_heap(Goals) :-
|
|
code_util__goal_list_may_allocate_heap(Goals, yes).
|
|
|
|
:- pred code_util__goal_may_allocate_heap(hlds_goal::in, bool::out) is det.
|
|
|
|
code_util__goal_may_allocate_heap(Goal - _GoalInfo, May) :-
|
|
code_util__goal_may_allocate_heap_2(Goal, May).
|
|
|
|
:- pred code_util__goal_may_allocate_heap_2(hlds_goal_expr::in, bool::out)
|
|
is det.
|
|
|
|
code_util__goal_may_allocate_heap_2(generic_call(_, _, _, _), yes).
|
|
code_util__goal_may_allocate_heap_2(call(_, _, _, Builtin, _, _), May) :-
|
|
( Builtin = inline_builtin ->
|
|
May = no
|
|
;
|
|
May = yes
|
|
).
|
|
code_util__goal_may_allocate_heap_2(unify(_, _, _, Unification, _), May) :-
|
|
( Unification = construct(_,_,Args,_,_,_,_), Args = [_|_] ->
|
|
May = yes
|
|
;
|
|
May = no
|
|
).
|
|
% We cannot safely say that a foreign code fragment does not
|
|
% allocate memory without knowing all the #defined macros that
|
|
% expand to incr_hp and variants thereof.
|
|
% XXX although you could make it an attribute of the foreign code and
|
|
% trust the programmer
|
|
code_util__goal_may_allocate_heap_2(foreign_proc(_,_,_,_,_,_,_), yes).
|
|
code_util__goal_may_allocate_heap_2(some(_Vars, _, Goal), May) :-
|
|
code_util__goal_may_allocate_heap(Goal, May).
|
|
code_util__goal_may_allocate_heap_2(not(Goal), May) :-
|
|
code_util__goal_may_allocate_heap(Goal, May).
|
|
code_util__goal_may_allocate_heap_2(conj(Goals), May) :-
|
|
code_util__goal_list_may_allocate_heap(Goals, May).
|
|
code_util__goal_may_allocate_heap_2(par_conj(_), yes).
|
|
code_util__goal_may_allocate_heap_2(disj(Goals), May) :-
|
|
code_util__goal_list_may_allocate_heap(Goals, May).
|
|
code_util__goal_may_allocate_heap_2(switch(_Var, _Det, Cases), May) :-
|
|
code_util__cases_may_allocate_heap(Cases, May).
|
|
code_util__goal_may_allocate_heap_2(if_then_else(_Vars, C, T, E), May) :-
|
|
( code_util__goal_may_allocate_heap(C, yes) ->
|
|
May = yes
|
|
; code_util__goal_may_allocate_heap(T, yes) ->
|
|
May = yes
|
|
;
|
|
code_util__goal_may_allocate_heap(E, May)
|
|
).
|
|
code_util__goal_may_allocate_heap_2(shorthand(ShorthandGoal), May) :-
|
|
code_util__goal_may_allocate_heap_2_shorthand(ShorthandGoal, May).
|
|
|
|
:- pred code_util__goal_may_allocate_heap_2_shorthand(shorthand_goal_expr::in,
|
|
bool::out) is det.
|
|
|
|
code_util__goal_may_allocate_heap_2_shorthand(bi_implication(G1, G2), May) :-
|
|
( code_util__goal_may_allocate_heap(G1, yes) ->
|
|
May = yes
|
|
;
|
|
code_util__goal_may_allocate_heap(G2, May)
|
|
).
|
|
|
|
|
|
|
|
:- pred code_util__goal_list_may_allocate_heap(list(hlds_goal)::in, bool::out)
|
|
is det.
|
|
|
|
code_util__goal_list_may_allocate_heap([], no).
|
|
code_util__goal_list_may_allocate_heap([Goal | Goals], May) :-
|
|
( code_util__goal_may_allocate_heap(Goal, yes) ->
|
|
May = yes
|
|
;
|
|
code_util__goal_list_may_allocate_heap(Goals, May)
|
|
).
|
|
|
|
:- pred code_util__cases_may_allocate_heap(list(case)::in, bool::out) is det.
|
|
|
|
code_util__cases_may_allocate_heap([], no).
|
|
code_util__cases_may_allocate_heap([case(_, Goal) | Cases], May) :-
|
|
( code_util__goal_may_allocate_heap(Goal, yes) ->
|
|
May = yes
|
|
;
|
|
code_util__cases_may_allocate_heap(Cases, May)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
code_util__goal_may_alloc_temp_frame(Goal) :-
|
|
code_util__goal_may_alloc_temp_frame(Goal, yes).
|
|
|
|
:- pred code_util__goal_may_alloc_temp_frame(hlds_goal::in, bool::out) is det.
|
|
|
|
code_util__goal_may_alloc_temp_frame(Goal - _GoalInfo, May) :-
|
|
code_util__goal_may_alloc_temp_frame_2(Goal, May).
|
|
|
|
:- pred code_util__goal_may_alloc_temp_frame_2(hlds_goal_expr::in, bool::out)
|
|
is det.
|
|
|
|
code_util__goal_may_alloc_temp_frame_2(generic_call(_, _, _, _), no).
|
|
code_util__goal_may_alloc_temp_frame_2(call(_, _, _, _, _, _), no).
|
|
code_util__goal_may_alloc_temp_frame_2(unify(_, _, _, _, _), no).
|
|
% We cannot safely say that a foreign code fragment does not allocate
|
|
% temporary nondet frames without knowing all the #defined macros
|
|
% that expand to mktempframe and variants thereof. The performance
|
|
% impact of being too conservative is probably not too bad.
|
|
code_util__goal_may_alloc_temp_frame_2(foreign_proc(_,_,_,_,_,_,_),
|
|
yes).
|
|
code_util__goal_may_alloc_temp_frame_2(some(_Vars, _, Goal), May) :-
|
|
Goal = _ - GoalInfo,
|
|
goal_info_get_code_model(GoalInfo, CodeModel),
|
|
( CodeModel = model_non ->
|
|
May = yes
|
|
;
|
|
code_util__goal_may_alloc_temp_frame(Goal, May)
|
|
).
|
|
code_util__goal_may_alloc_temp_frame_2(not(Goal), May) :-
|
|
code_util__goal_may_alloc_temp_frame(Goal, May).
|
|
code_util__goal_may_alloc_temp_frame_2(conj(Goals), May) :-
|
|
code_util__goal_list_may_alloc_temp_frame(Goals, May).
|
|
code_util__goal_may_alloc_temp_frame_2(par_conj(Goals), May) :-
|
|
code_util__goal_list_may_alloc_temp_frame(Goals, May).
|
|
code_util__goal_may_alloc_temp_frame_2(disj(Goals), May) :-
|
|
code_util__goal_list_may_alloc_temp_frame(Goals, May).
|
|
code_util__goal_may_alloc_temp_frame_2(switch(_Var, _Det, Cases), May) :-
|
|
code_util__cases_may_alloc_temp_frame(Cases, May).
|
|
code_util__goal_may_alloc_temp_frame_2(if_then_else(_Vars, C, T, E), May) :-
|
|
( code_util__goal_may_alloc_temp_frame(C, yes) ->
|
|
May = yes
|
|
; code_util__goal_may_alloc_temp_frame(T, yes) ->
|
|
May = yes
|
|
;
|
|
code_util__goal_may_alloc_temp_frame(E, May)
|
|
).
|
|
code_util__goal_may_alloc_temp_frame_2(shorthand(ShorthandGoal), May) :-
|
|
code_util__goal_may_alloc_temp_frame_2_shorthand(ShorthandGoal,May).
|
|
|
|
:- pred code_util__goal_may_alloc_temp_frame_2_shorthand(
|
|
shorthand_goal_expr::in, bool::out) is det.
|
|
|
|
code_util__goal_may_alloc_temp_frame_2_shorthand(bi_implication(G1, G2),
|
|
May) :-
|
|
( code_util__goal_may_alloc_temp_frame(G1, yes) ->
|
|
May = yes
|
|
;
|
|
code_util__goal_may_alloc_temp_frame(G2, May)
|
|
).
|
|
|
|
:- pred code_util__goal_list_may_alloc_temp_frame(list(hlds_goal)::in,
|
|
bool::out) is det.
|
|
|
|
code_util__goal_list_may_alloc_temp_frame([], no).
|
|
code_util__goal_list_may_alloc_temp_frame([Goal | Goals], May) :-
|
|
( code_util__goal_may_alloc_temp_frame(Goal, yes) ->
|
|
May = yes
|
|
;
|
|
code_util__goal_list_may_alloc_temp_frame(Goals, May)
|
|
).
|
|
|
|
:- pred code_util__cases_may_alloc_temp_frame(list(case)::in, bool::out)
|
|
is det.
|
|
|
|
code_util__cases_may_alloc_temp_frame([], no).
|
|
code_util__cases_may_alloc_temp_frame([case(_, Goal) | Cases], May) :-
|
|
( code_util__goal_may_alloc_temp_frame(Goal, yes) ->
|
|
May = yes
|
|
;
|
|
code_util__cases_may_alloc_temp_frame(Cases, May)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% 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(deep_profiling_proc_static(PPId), _, _,
|
|
deep_profiling_proc_static_tag(PPId)).
|
|
code_util__cons_id_to_tag(table_io_decl(PPId), _, _, table_io_decl_tag(PPId)).
|
|
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)
|
|
;
|
|
% Tuples do not need a tag. Note that unary tuples are not
|
|
% treated as no_tag types. There's no reason why they
|
|
% couldn't be, it's just not worth the effort.
|
|
type_is_tuple(Type, _)
|
|
->
|
|
Tag = single_functor
|
|
;
|
|
% Use the type to determine the type_ctor
|
|
( type_to_ctor_and_args(Type, TypeCtor0, _) ->
|
|
TypeCtor = TypeCtor0
|
|
;
|
|
% the type-checker should ensure that this never happens
|
|
error("code_util__cons_id_to_tag: invalid type")
|
|
),
|
|
|
|
% Given the type_ctor, lookup up the constructor tag
|
|
% table for that type
|
|
module_info_types(ModuleInfo, TypeTable),
|
|
map__lookup(TypeTable, TypeCtor, 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).
|
|
code_util__cannot_stack_flush_2(not(unify(_, _, _, Unify, _) - _)) :-
|
|
Unify \= complicated_unify(_, _, _).
|
|
|
|
:- 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_flush(GoalExpr - _) :-
|
|
code_util__cannot_flush_2(GoalExpr).
|
|
|
|
:- pred code_util__cannot_flush_2(hlds_goal_expr).
|
|
:- mode code_util__cannot_flush_2(in) is semidet.
|
|
|
|
code_util__cannot_flush_2(unify(_, _, _, Unify, _)) :-
|
|
Unify \= complicated_unify(_, _, _).
|
|
code_util__cannot_flush_2(call(_, _, _, BuiltinState, _, _)) :-
|
|
BuiltinState = inline_builtin.
|
|
code_util__cannot_flush_2(conj(Goals)) :-
|
|
code_util__cannot_flush_goals(Goals).
|
|
|
|
:- pred code_util__cannot_flush_goals(list(hlds_goal)).
|
|
:- mode code_util__cannot_flush_goals(in) is semidet.
|
|
|
|
code_util__cannot_flush_goals([]).
|
|
code_util__cannot_flush_goals([Goal | Goals]) :-
|
|
code_util__cannot_flush(Goal),
|
|
code_util__cannot_flush_goals(Goals).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
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(foreign_proc(_, _, _, _, _, _, _),
|
|
_, _, 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(shorthand(_),
|
|
_, _, _, _) :-
|
|
% these should have been expanded out by now
|
|
error("code_util__count_recursive_calls_2: unexpected shorthand").
|
|
|
|
:- 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__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).
|
|
|
|
%-----------------------------------------------------------------------------%
|