mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-14 21:35:49 +00:00
Estimated hours taken: 1 Branches: main compiler/code_util.m: compiler/goal_form.m: compiler/hlds_code_util.m: Move functionality that is not part of the LLDS backend from ll_backend__code_util to hlds__goal_form and to hlds__hlds_code_util. Turn some predicates into functions. compiler/*.m: Conform to the changes above.
445 lines
16 KiB
Mathematica
445 lines
16 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 backend_libs__proc_label.
|
|
:- import_module backend_libs__rtti.
|
|
:- import_module hlds__hlds_goal.
|
|
:- import_module hlds__hlds_module.
|
|
:- import_module hlds__hlds_pred.
|
|
:- import_module ll_backend__llds.
|
|
|
|
:- import_module 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__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.
|
|
|
|
:- 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.
|
|
|
|
% 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 backend_libs__builtin_ops.
|
|
:- import_module backend_libs__code_model.
|
|
:- import_module hlds__special_pred.
|
|
:- import_module libs__globals.
|
|
:- import_module libs__options.
|
|
:- import_module parse_tree__prog_util.
|
|
|
|
:- import_module bool, 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_rtti_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)
|
|
)
|
|
->
|
|
ProcLabel = make_proc_label_from_rtti(RttiProcLabel),
|
|
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_rtti_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) :-
|
|
ProcLabel = make_proc_label_from_rtti(RttiProcLabel),
|
|
(
|
|
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) :-
|
|
ProcLabel = make_proc_label(ModuleInfo, PredId, ProcId),
|
|
Label = local(LabelNum, ProcLabel).
|
|
|
|
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_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__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).
|
|
|
|
%-----------------------------------------------------------------------------%
|