mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-14 21:35:49 +00:00
Estimated hours taken: 12 (mostly in diagnosis) Branches: main A temporary fix Mantis bug #87, which caused all programs compiled with deep profiling to get a sanity check violation when writing out the deep profiling data. My detective work tracked the first reported call site that got this violation, which is in integer.m in the library, where the predicate integer.to_string calls the function string.++. (a) String.++ is marked (correctly) as opt_imported. (b) The test predicate pred_info_is_imported FAILS for opt_imported predicates. (c) This causes make_rtti_proc_label to put "no" into the pred_is_imported field of the rtti_proc_label for string.++. (d) This rtti_proc_label is put into the element of the call_site_static_data list for integer.to_string that corresponds to the call to string.++ as the rtti_proc_label of the callee. (e) When the time comes to write out the call_site_static structures, layout_out.m invokes make_proc_label_from_rtti to convert this rtti_proc_label in the call_site_static_data to a plain, non-RTTI proc_label. However, since the current module name ("integer") does not match the name of the module that defines the function ("string") AND the function is not marked as imported, make_user_proc_label (invoked from make_proc_label_from_rtti) believes that this means that this module generates code for the callee (as we do for some opt_imported procedures). (f) For procedures for which we generate target code in a different module than their defining module, we generate different labels for them than the label for the code for the procedure in its own module. We derive the name of a procedure's proc_static structure directly from its label, so this difference holds for the names of proc_static structures as well as for labels. (g) This lead to the problem, which was that the call_site_static for this call site referred to the proc_static for string.++ defined in integer.c (as opposed to string.++ defined in string.c), since integer.c did not actually generate code for string.++, there was no such proc_static. This should be a link error, but it isn't, due to the requirement on unix linkers of being compatible with Fortran's ancient semantics (specifically, Fortran's common areas). Instead, the C compiler happily allocates memory for the undefined symbol and fills it with zeros as if it were a proc_static structure. (h) The assertion violation in mercury_deep_profiling.c occurs because the call_site_static refers to this bogus proc_static, but of course no module writes out this proc_static. It turns out that deep_profiling.m and the code generator both use the same algorithm to turn the pred_proc_id of string.++ into a label, but the relevant input to this algorithm, the import_status of string.++, changes between the invocation of the deep_profiling transformation and the code generator. The import_status is changed by the dead procedure elimination pass; it shouldn't be, but it is. The reason why the bug started showing up recently is my recent diff that moved the dead_procedure elimination pass after the deep profiling pass. The temporary fix is to make sure that the deep profiling pass sees the same status for each procedure as the code generator, by making it invoke the dead procedure elimination pass itself. The right fix is to change the representation of import_status, designing it properly for the first time, but that will take more time. This redesign should eliminate the need for the dead procedure eliminate pass to change any statuses. compiler/deep_profiling.m: Make the fix described above. Print progress message only with -V, not -v, since this is what all the other passes do. compiler/hlds_out.m: When writing out a procedure's deep profiling information, include the data structures from which we later construct the procedure's proc_static and call_site_static structures. compiler/code_util.m: Replace some if-then-elses with switches. compiler/proc_label.m: Don't export a function that is not used anywhere else. compiler/type_ctor_info.m: Delete a predicate to avoid an ambiguity.
424 lines
15 KiB
Mathematica
424 lines
15 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 1994-2008 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 hlds.hlds_goal.
|
|
:- import_module hlds.hlds_llds.
|
|
:- import_module hlds.hlds_module.
|
|
:- import_module hlds.hlds_pred.
|
|
:- import_module hlds.hlds_rtti.
|
|
:- import_module ll_backend.llds.
|
|
:- import_module mdbcomp.prim_data.
|
|
:- import_module parse_tree.prog_data.
|
|
|
|
:- import_module assoc_list.
|
|
:- import_module bool.
|
|
:- import_module list.
|
|
:- import_module maybe.
|
|
:- import_module pair.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% 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)).
|
|
:- func make_entry_label(module_info, pred_id, proc_id, immed) = code_addr.
|
|
|
|
:- func make_entry_label_from_rtti(rtti_proc_label, immed) = code_addr.
|
|
|
|
% 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 make_entry_label.
|
|
%
|
|
:- func make_local_entry_label(module_info, pred_id, proc_id, immed) = label.
|
|
|
|
% Create a label internal to a Mercury procedure.
|
|
%
|
|
:- func make_internal_label(module_info, pred_id, proc_id, int) = label.
|
|
|
|
:- func extract_proc_label_from_code_addr(code_addr) = proc_label.
|
|
|
|
:- pred arg_loc_to_register(arg_loc::in, lval::out) is det.
|
|
|
|
:- pred max_mentioned_reg(list(lval)::in, int::out) is det.
|
|
:- pred max_mentioned_abs_reg(list(abs_locn)::in, int::out) is det.
|
|
|
|
:- pred goal_may_alloc_temp_frame(hlds_goal::in, bool::out) is det.
|
|
|
|
% Negate a condition.
|
|
% This is used mostly just to make the generated code more readable.
|
|
%
|
|
:- pred neg_rval(rval::in, rval::out) is det.
|
|
|
|
:- pred negate_the_test(list(instruction)::in, list(instruction)::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.
|
|
%
|
|
:- func lvals_in_rval(rval) = list(lval).
|
|
:- func lvals_in_lval(lval) = list(lval).
|
|
:- func lvals_in_lvals(list(lval)) = list(lval).
|
|
|
|
% Given a procedure that already has its arg_info field filled in,
|
|
% return a list giving its input variables and their initial locations.
|
|
%
|
|
:- pred build_input_arg_list(proc_info::in, assoc_list(prog_var, lval)::out)
|
|
is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module backend_libs.builtin_ops.
|
|
:- import_module backend_libs.proc_label.
|
|
:- import_module backend_libs.rtti.
|
|
:- import_module hlds.code_model.
|
|
:- import_module libs.compiler_util.
|
|
|
|
:- import_module int.
|
|
:- import_module term.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
make_entry_label(ModuleInfo, PredId, ProcId, Immed) = ProcAddr :-
|
|
RttiProcLabel = make_rtti_proc_label(ModuleInfo, PredId, ProcId),
|
|
ProcAddr = make_entry_label_from_rtti(RttiProcLabel, Immed).
|
|
|
|
make_entry_label_from_rtti(RttiProcLabel, Immed) = ProcAddr :-
|
|
ProcIsImported = RttiProcLabel ^ proc_is_imported,
|
|
(
|
|
ProcIsImported = yes,
|
|
ProcLabel = make_proc_label_from_rtti(RttiProcLabel),
|
|
ProcAddr = code_imported_proc(ProcLabel)
|
|
;
|
|
ProcIsImported = no,
|
|
Label = make_local_entry_label_from_rtti(RttiProcLabel, Immed),
|
|
ProcAddr = code_label(Label)
|
|
).
|
|
|
|
make_local_entry_label(ModuleInfo, PredId, ProcId, Immed) = Label :-
|
|
RttiProcLabel = make_rtti_proc_label(ModuleInfo, PredId, ProcId),
|
|
Label = make_local_entry_label_from_rtti(RttiProcLabel, Immed).
|
|
|
|
:- func make_local_entry_label_from_rtti(rtti_proc_label, immed) = label.
|
|
|
|
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.
|
|
ProcIsExported = RttiProcLabel ^ proc_is_exported,
|
|
(
|
|
ProcIsExported = yes,
|
|
EntryType = entry_label_exported
|
|
;
|
|
ProcIsExported = no,
|
|
EntryType = entry_label_local
|
|
),
|
|
Label = entry_label(EntryType, ProcLabel)
|
|
;
|
|
Immed = yes(ProcsPerFunc - proc(CurPredId, CurProcId)),
|
|
Label = choose_local_label_type(ProcsPerFunc, CurPredId, CurProcId,
|
|
RttiProcLabel ^ pred_id, RttiProcLabel ^ proc_id, ProcLabel)
|
|
).
|
|
|
|
:- func choose_local_label_type(int, pred_id, proc_id, pred_id, proc_id,
|
|
proc_label) = label.
|
|
|
|
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 is
|
|
% usable only within the current C module, since it is likely to be
|
|
% faster.
|
|
(
|
|
ProcsPerFunc = 0
|
|
;
|
|
PredId = CurPredId,
|
|
ProcId = CurProcId
|
|
)
|
|
->
|
|
EntryType = entry_label_c_local
|
|
;
|
|
EntryType = entry_label_local
|
|
),
|
|
Label = entry_label(EntryType, ProcLabel).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
make_internal_label(ModuleInfo, PredId, ProcId, LabelNum) = Label :-
|
|
ProcLabel = make_proc_label(ModuleInfo, PredId, ProcId),
|
|
Label = internal_label(LabelNum, ProcLabel).
|
|
|
|
extract_proc_label_from_code_addr(CodeAddr) = ProcLabel :-
|
|
( CodeAddr = code_label(Label) ->
|
|
ProcLabel = get_proc_label(Label)
|
|
; CodeAddr = code_imported_proc(ProcLabelPrime) ->
|
|
ProcLabel = ProcLabelPrime
|
|
;
|
|
unexpected(this_file, "extract_label_from_code_addr failed")
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
arg_loc_to_register(ArgLoc, reg(reg_r, ArgLoc)).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
max_mentioned_reg(Lvals, MaxRegNum) :-
|
|
max_mentioned_reg_2(Lvals, 0, MaxRegNum).
|
|
|
|
:- pred max_mentioned_reg_2(list(lval)::in, int::in, int::out) is det.
|
|
|
|
max_mentioned_reg_2([], !MaxRegNum).
|
|
max_mentioned_reg_2([Lval | Lvals], !MaxRegNum) :-
|
|
( Lval = reg(reg_r, N) ->
|
|
int.max(N, !MaxRegNum)
|
|
;
|
|
true
|
|
),
|
|
max_mentioned_reg_2(Lvals, !MaxRegNum).
|
|
|
|
max_mentioned_abs_reg(Lvals, MaxRegNum) :-
|
|
max_mentioned_abs_reg_2(Lvals, 0, MaxRegNum).
|
|
|
|
:- pred max_mentioned_abs_reg_2(list(abs_locn)::in, int::in, int::out) is det.
|
|
|
|
max_mentioned_abs_reg_2([], !MaxRegNum).
|
|
max_mentioned_abs_reg_2([Lval | Lvals], !MaxRegNum) :-
|
|
( Lval = abs_reg(N) ->
|
|
int.max(N, !MaxRegNum)
|
|
;
|
|
true
|
|
),
|
|
max_mentioned_abs_reg_2(Lvals, !MaxRegNum).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
goal_may_alloc_temp_frame(hlds_goal(GoalExpr, _GoalInfo), May) :-
|
|
goal_may_alloc_temp_frame_2(GoalExpr, May).
|
|
|
|
:- pred goal_may_alloc_temp_frame_2(hlds_goal_expr::in, bool::out)
|
|
is det.
|
|
|
|
goal_may_alloc_temp_frame_2(generic_call(_, _, _, _), no).
|
|
goal_may_alloc_temp_frame_2(plain_call(_, _, _, _, _, _), no).
|
|
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.
|
|
goal_may_alloc_temp_frame_2(call_foreign_proc(_, _, _, _, _, _, _), yes).
|
|
goal_may_alloc_temp_frame_2(scope(_, Goal), May) :-
|
|
Goal = hlds_goal(_, GoalInfo),
|
|
CodeModel = goal_info_get_code_model(GoalInfo),
|
|
(
|
|
CodeModel = model_non,
|
|
May = yes
|
|
;
|
|
( CodeModel = model_det
|
|
; CodeModel = model_semi
|
|
),
|
|
goal_may_alloc_temp_frame(Goal, May)
|
|
).
|
|
goal_may_alloc_temp_frame_2(negation(Goal), May) :-
|
|
goal_may_alloc_temp_frame(Goal, May).
|
|
goal_may_alloc_temp_frame_2(conj(_ConjType, Goals), May) :-
|
|
goal_list_may_alloc_temp_frame(Goals, May).
|
|
goal_may_alloc_temp_frame_2(disj(Goals), May) :-
|
|
goal_list_may_alloc_temp_frame(Goals, May).
|
|
goal_may_alloc_temp_frame_2(switch(_Var, _Det, Cases), May) :-
|
|
cases_may_alloc_temp_frame(Cases, May).
|
|
goal_may_alloc_temp_frame_2(if_then_else(_Vars, C, T, E), May) :-
|
|
( goal_may_alloc_temp_frame(C, yes) ->
|
|
May = yes
|
|
; goal_may_alloc_temp_frame(T, yes) ->
|
|
May = yes
|
|
;
|
|
goal_may_alloc_temp_frame(E, May)
|
|
).
|
|
goal_may_alloc_temp_frame_2(shorthand(_), _) :-
|
|
% These should have been expanded out by now.
|
|
unexpected(this_file, "goal_may_alloc_temp_frame_2: shorthand").
|
|
|
|
:- pred goal_list_may_alloc_temp_frame(list(hlds_goal)::in, bool::out) is det.
|
|
|
|
goal_list_may_alloc_temp_frame([], no).
|
|
goal_list_may_alloc_temp_frame([Goal | Goals], May) :-
|
|
( goal_may_alloc_temp_frame(Goal, yes) ->
|
|
May = yes
|
|
;
|
|
goal_list_may_alloc_temp_frame(Goals, May)
|
|
).
|
|
|
|
:- pred cases_may_alloc_temp_frame(list(case)::in, bool::out) is det.
|
|
|
|
cases_may_alloc_temp_frame([], no).
|
|
cases_may_alloc_temp_frame([case(_, _, Goal) | Cases], May) :-
|
|
( goal_may_alloc_temp_frame(Goal, yes) ->
|
|
May = yes
|
|
;
|
|
cases_may_alloc_temp_frame(Cases, May)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
neg_rval(Rval, NegRval) :-
|
|
( neg_rval_2(Rval, NegRval0) ->
|
|
NegRval = NegRval0
|
|
;
|
|
NegRval = unop(logical_not, Rval)
|
|
).
|
|
|
|
:- pred neg_rval_2(rval::in, rval::out) is semidet.
|
|
|
|
neg_rval_2(const(Const), const(NegConst)) :-
|
|
(
|
|
Const = llconst_true,
|
|
NegConst = llconst_false
|
|
;
|
|
Const = llconst_false,
|
|
NegConst = llconst_true
|
|
).
|
|
neg_rval_2(unop(logical_not, Rval), Rval).
|
|
neg_rval_2(binop(Op, X, Y), binop(NegOp, X, Y)) :-
|
|
neg_op(Op, NegOp).
|
|
|
|
:- pred neg_op(binary_op::in, binary_op::out) is semidet.
|
|
|
|
neg_op(eq, ne).
|
|
neg_op(ne, eq).
|
|
neg_op(int_lt, int_ge).
|
|
neg_op(int_le, int_gt).
|
|
neg_op(int_gt, int_le).
|
|
neg_op(int_ge, int_lt).
|
|
neg_op(str_eq, str_ne).
|
|
neg_op(str_ne, str_eq).
|
|
neg_op(str_lt, str_ge).
|
|
neg_op(str_le, str_gt).
|
|
neg_op(str_gt, str_le).
|
|
neg_op(str_ge, str_lt).
|
|
neg_op(float_eq, float_ne).
|
|
neg_op(float_ne, float_eq).
|
|
neg_op(float_lt, float_ge).
|
|
neg_op(float_le, float_gt).
|
|
neg_op(float_gt, float_le).
|
|
neg_op(float_ge, float_lt).
|
|
|
|
negate_the_test([], _) :-
|
|
unexpected(this_file, "negate_the_test on empty list").
|
|
negate_the_test([Instr0 | Instrs0], Instrs) :-
|
|
( Instr0 = llds_instr(if_val(Test, Target), Comment) ->
|
|
neg_rval(Test, NewTest),
|
|
Instrs = [llds_instr(if_val(NewTest, Target), Comment)]
|
|
;
|
|
negate_the_test(Instrs0, Instrs1),
|
|
Instrs = [Instr0 | Instrs1]
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
lvals_in_lvals([]) = [].
|
|
lvals_in_lvals([First | Rest]) = FirstLvals ++ RestLvals :-
|
|
FirstLvals = lvals_in_lval(First),
|
|
RestLvals = lvals_in_lvals(Rest).
|
|
|
|
lvals_in_rval(lval(Lval)) = [Lval | lvals_in_lval(Lval)].
|
|
lvals_in_rval(var(_)) = [].
|
|
lvals_in_rval(mkword(_, Rval)) = lvals_in_rval(Rval).
|
|
lvals_in_rval(const(_)) = [].
|
|
lvals_in_rval(unop(_, Rval)) = lvals_in_rval(Rval).
|
|
lvals_in_rval(binop(_, Rval1, Rval2)) =
|
|
lvals_in_rval(Rval1) ++ lvals_in_rval(Rval2).
|
|
lvals_in_rval(mem_addr(MemRef)) = lvals_in_mem_ref(MemRef).
|
|
|
|
lvals_in_lval(reg(_, _)) = [].
|
|
lvals_in_lval(stackvar(_)) = [].
|
|
lvals_in_lval(parent_stackvar(_)) = [].
|
|
lvals_in_lval(framevar(_)) = [].
|
|
lvals_in_lval(succip) = [].
|
|
lvals_in_lval(maxfr) = [].
|
|
lvals_in_lval(curfr) = [].
|
|
lvals_in_lval(succip_slot(Rval)) = lvals_in_rval(Rval).
|
|
lvals_in_lval(redofr_slot(Rval)) = lvals_in_rval(Rval).
|
|
lvals_in_lval(redoip_slot(Rval)) = lvals_in_rval(Rval).
|
|
lvals_in_lval(succfr_slot(Rval)) = lvals_in_rval(Rval).
|
|
lvals_in_lval(prevfr_slot(Rval)) = lvals_in_rval(Rval).
|
|
lvals_in_lval(hp) = [].
|
|
lvals_in_lval(sp) = [].
|
|
lvals_in_lval(parent_sp) = [].
|
|
lvals_in_lval(field(_, Rval1, Rval2)) =
|
|
lvals_in_rval(Rval1) ++ lvals_in_rval(Rval2).
|
|
lvals_in_lval(lvar(_)) = [].
|
|
lvals_in_lval(temp(_, _)) = [].
|
|
lvals_in_lval(mem_ref(Rval)) = lvals_in_rval(Rval).
|
|
lvals_in_lval(global_var_ref(_)) = [].
|
|
|
|
:- func lvals_in_mem_ref(mem_ref) = list(lval).
|
|
|
|
lvals_in_mem_ref(stackvar_ref(Rval)) = lvals_in_rval(Rval).
|
|
lvals_in_mem_ref(framevar_ref(Rval)) = lvals_in_rval(Rval).
|
|
lvals_in_mem_ref(heap_ref(Rval1, _, Rval2)) =
|
|
lvals_in_rval(Rval1) ++ lvals_in_rval(Rval2).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
build_input_arg_list(ProcInfo, VarLvals) :-
|
|
proc_info_get_headvars(ProcInfo, HeadVars),
|
|
proc_info_arg_info(ProcInfo, ArgInfos),
|
|
assoc_list.from_corresponding_lists(HeadVars, ArgInfos, VarArgInfos),
|
|
build_input_arg_list_2(VarArgInfos, VarLvals).
|
|
|
|
:- pred build_input_arg_list_2(assoc_list(prog_var, arg_info)::in,
|
|
assoc_list(prog_var, lval)::out) is det.
|
|
|
|
build_input_arg_list_2([], []).
|
|
build_input_arg_list_2([V - Arg | Rest0], VarArgs) :-
|
|
Arg = arg_info(Loc, Mode),
|
|
(
|
|
Mode = top_in,
|
|
arg_loc_to_register(Loc, Reg),
|
|
VarArgs = [V - Reg | VarArgs0]
|
|
;
|
|
( Mode = top_out
|
|
; Mode = top_unused
|
|
),
|
|
VarArgs = VarArgs0
|
|
),
|
|
build_input_arg_list_2(Rest0, VarArgs0).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- func this_file = string.
|
|
|
|
this_file = "code_util.m".
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
:- end_module code_util.
|
|
%-----------------------------------------------------------------------------%
|