mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-18 19:03:45 +00:00
Since this is the first converted module that dumps out goals when
debugging trace flags are enabled, this required generalizing the code
that does that, to take either varsets or var_tables as a means of
specifying the names of variables. We do this via a new type,
var_name_source, which contains either a varset or a var_table.
Almost all of this diff is there to implement this generalization.
A large part of it affects code in the parse_tree package that we use
to write out the parts of HLDS goals that are defined by types defined
in that package. Since we want to avoid making any part of the parse_tree
package dependent on the hlds package, this required defining the
var_name_source type in the parse_tree package, which in turn requires
var_table.m to be in that same package.
compiler/lco.m:
Convert this module to use var_tables instead of varsets and vartypes.
compiler/var_table.m:
Move this module from the hlds package to the parse_tree package.
To make this, possible, move the parts that required access to the HLDS
to hlds_pred.m, from where it was usually invoked.
Export some utility predicates to allow the moved code to work
in hlds_pred.m without access to the actual definition of the
var_table type.
Define the var_name_source type.
Add some utility functions for use by code writing out variable names.
compiler/hlds_pred.m:
Add the code moved from var_table.m.
compiler/vartypes.m:
Move this module from the hlds package to the parse_tree package,
for symmetry with var_table.m. It did not depend on being in hlds
in any way.
compiler/hlds.m:
compiler/parse_tree.m:
Move vartypes.m and var_table.m from the hlds package
to the parse_tree package.
compiler/hlds_out_goal.m:
Change all the predicates in this module to take a var_name_source
instead of a prog_varset.
Fix some comments.
compiler/hlds_out_util.m:
Change some of the predicates in this module (those called from
hlds_out_goal.m) to take a var_name_source instead of a prog_varset.
compiler/parse_tree_out_term.m:
Provide variants of some existing predicates and functions that take
var_name_sources instead of varsets. The code of the copies
duplicates the logic of the originals, though I hope that this
duplication can be done away with at the end of the transition.
(The best solution would be to use a typeclass with methods
that convert vars to their names, but we would want to ensure
that the compiler can specialize all the affected predicates
and functions to the two instances of this typeclass, which is
something that we cannot do yet. In the meantime, the lack of
any generalization in the old versions preserves their performance.)
tools/sort_imports:
tools/filter_sort_imports:
A new tool that automatically sorts any occurrences of consecutive
":- import_module" declarations in the named files. The sorting is done
in filter_sort_imports; sort_imports loops over the named files.
After automatically replacing all occurrences of hlds.{vartypes,var_table}
in import_module declarations with their parse_tree versions, the updated
import_module declarations were usually out of order with respect to
their neighbours. I used this script to fix that, and some earlier
out-of-order imports.
compiler/accumulator.m:
compiler/add_class.m:
compiler/add_clause.m:
compiler/add_foreign_proc.m:
compiler/add_heap_ops.m:
compiler/add_pragma_type_spec.m:
compiler/add_pred.m:
compiler/add_trail_ops.m:
compiler/analysis.m:
compiler/arg_info.m:
compiler/build_mode_constraints.m:
compiler/bytecode_gen.m:
compiler/call_gen.m:
compiler/check_promise.m:
compiler/closure_analysis.m:
compiler/closure_gen.m:
compiler/code_info.m:
compiler/code_loc_dep.m:
compiler/common.m:
compiler/compile_target_code.m:
compiler/complexity.m:
compiler/const_prop.m:
compiler/constraint.m:
compiler/continuation_info.m:
compiler/convert_parse_tree.m:
compiler/coverage_profiling.m:
compiler/cse_detection.m:
compiler/ctgc.datastruct.m:
compiler/ctgc.util.m:
compiler/dead_proc_elim.m:
compiler/deep_profiling.m:
compiler/deforest.m:
compiler/delay_construct.m:
compiler/delay_partial_inst.m:
compiler/dep_par_conj.m:
compiler/det_analysis.m:
compiler/det_report.m:
compiler/det_util.m:
compiler/direct_arg_in_out.m:
compiler/disj_gen.m:
compiler/distance_granularity.m:
compiler/equiv_type_hlds.m:
compiler/exception_analysis.m:
compiler/file_names.m:
compiler/float_regs.m:
compiler/follow_vars.m:
compiler/format_call.m:
compiler/generate_dep_d_files.m:
compiler/get_dependencies.m:
compiler/goal_expr_to_goal.m:
compiler/goal_mode.m:
compiler/goal_path.m:
compiler/goal_store.m:
compiler/goal_util.m:
compiler/granularity.m:
compiler/hhf.m:
compiler/higher_order.m:
compiler/hlds_clauses.m:
compiler/hlds_code_util.m:
compiler/hlds_error_util.m:
compiler/hlds_goal.m:
compiler/hlds_llds.m:
compiler/hlds_out_pred.m:
compiler/hlds_rtti.m:
compiler/hlds_statistics.m:
compiler/inlining.m:
compiler/inst_check.m:
compiler/inst_test.m:
compiler/inst_user.m:
compiler/instance_method_clauses.m:
compiler/instmap.m:
compiler/intermod.m:
compiler/intermod_analysis.m:
compiler/interval.m:
compiler/introduce_exists_casts.m:
compiler/introduce_parallelism.m:
compiler/item_util.m:
compiler/lambda.m:
compiler/live_vars.m:
compiler/liveness.m:
compiler/llds.m:
compiler/llds_out_data.m:
compiler/llds_out_file.m:
compiler/llds_out_util.m:
compiler/lookup_switch.m:
compiler/loop_inv.m:
compiler/make.module_target.m:
compiler/make.util.m:
compiler/make_goal.m:
compiler/make_hlds_separate_items.m:
compiler/make_hlds_types.m:
compiler/mark_tail_calls.m:
compiler/mercury_compile_mlds_back_end.m:
compiler/middle_rec.m:
compiler/ml_accurate_gc.m:
compiler/ml_args_util.m:
compiler/ml_call_gen.m:
compiler/ml_closure_gen.m:
compiler/ml_code_gen.m:
compiler/ml_code_util.m:
compiler/ml_commit_gen.m:
compiler/ml_disj_gen.m:
compiler/ml_foreign_proc_gen.m:
compiler/ml_gen_info.m:
compiler/ml_lookup_switch.m:
compiler/ml_proc_gen.m:
compiler/ml_simplify_switch.m:
compiler/ml_switch_gen.m:
compiler/ml_tag_switch.m:
compiler/ml_unify_gen.m:
compiler/ml_unify_gen_construct.m:
compiler/ml_unify_gen_deconstruct.m:
compiler/ml_unify_gen_test.m:
compiler/ml_unify_gen_util.m:
compiler/mlds_to_c_data.m:
compiler/mlds_to_c_func.m:
compiler/mlds_to_c_global.m:
compiler/mlds_to_cs_class.m:
compiler/mlds_to_cs_file.m:
compiler/mlds_to_java_data.m:
compiler/mlds_to_java_file.m:
compiler/mlds_to_java_stmt.m:
compiler/mlds_to_java_type.m:
compiler/mmc_analysis.m:
compiler/mode_comparison.m:
compiler/mode_constraints.m:
compiler/mode_debug.m:
compiler/mode_errors.m:
compiler/mode_info.m:
compiler/mode_ordering.m:
compiler/modecheck_call.m:
compiler/modecheck_coerce.m:
compiler/modecheck_goal.m:
compiler/modecheck_unify.m:
compiler/modecheck_util.m:
compiler/modes.m:
compiler/module_cmds.m:
compiler/old_type_constraints.m:
compiler/opt_debug.m:
compiler/optimize.m:
compiler/options_file.m:
compiler/ordering_mode_constraints.m:
compiler/par_loop_control.m:
compiler/parse_item.m:
compiler/parse_string_format.m:
compiler/parse_tree_out_inst.m:
compiler/parse_tree_to_term.m:
compiler/parse_util.m:
compiler/pd_debug.m:
compiler/pd_info.m:
compiler/pd_util.m:
compiler/peephole.m:
compiler/polymorphism.m:
compiler/polymorphism_info.m:
compiler/polymorphism_lambda.m:
compiler/polymorphism_type_class_info.m:
compiler/polymorphism_type_info.m:
compiler/post_typecheck.m:
compiler/pragma_c_gen.m:
compiler/pred_name.m:
compiler/pred_table.m:
compiler/prog_item.m:
compiler/prog_rep.m:
compiler/prop_mode_constraints.m:
compiler/purity.m:
compiler/push_goals_together.m:
compiler/qual_info.m:
compiler/quantification.m:
compiler/rbmm.execution_path.m:
compiler/rbmm.m:
compiler/rbmm.points_to_analysis.m:
compiler/rbmm.points_to_graph.m:
compiler/rbmm.points_to_info.m:
compiler/rbmm.region_resurrection_renaming.m:
compiler/rbmm.region_transformation.m:
compiler/recompilation.used_file.m:
compiler/recompilation.version.m:
compiler/recompute_instmap_deltas.m:
compiler/resolve_unify_functor.m:
compiler/rtti.m:
compiler/rtti_out.m:
compiler/rtti_to_mlds.m:
compiler/saved_vars.m:
compiler/set_of_var.m:
compiler/simplify_goal_call.m:
compiler/simplify_goal_conj.m:
compiler/simplify_goal_disj.m:
compiler/simplify_goal_ite.m:
compiler/simplify_goal_scope.m:
compiler/simplify_goal_switch.m:
compiler/simplify_goal_unify.m:
compiler/simplify_info.m:
compiler/simplify_proc.m:
compiler/size_prof.m:
compiler/smm_common.m:
compiler/ssdebug.m:
compiler/stack_alloc.m:
compiler/stack_layout.m:
compiler/stack_opt.m:
compiler/stm_expand.m:
compiler/store_alloc.m:
compiler/structure_reuse.analysis.m:
compiler/structure_reuse.direct.choose_reuse.m:
compiler/structure_reuse.direct.detect_garbage.m:
compiler/structure_reuse.domain.m:
compiler/structure_reuse.indirect.m:
compiler/structure_reuse.lbu.m:
compiler/structure_reuse.lfu.m:
compiler/structure_sharing.analysis.m:
compiler/structure_sharing.domain.m:
compiler/superhomogeneous.m:
compiler/switch_detection.m:
compiler/switch_gen.m:
compiler/switch_util.m:
compiler/table_gen.m:
compiler/tabling_analysis.m:
compiler/term_constr_build.m:
compiler/term_constr_data.m:
compiler/term_constr_initial.m:
compiler/term_constr_main.m:
compiler/term_constr_main_types.m:
compiler/term_constr_util.m:
compiler/term_pass1.m:
compiler/term_traversal.m:
compiler/term_util.m:
compiler/trace_gen.m:
compiler/trailing_analysis.m:
compiler/transform_llds.m:
compiler/try_expand.m:
compiler/tupling.m:
compiler/type_assign.m:
compiler/type_ctor_info.m:
compiler/type_util.m:
compiler/typecheck.m:
compiler/typecheck_debug.m:
compiler/typecheck_errors.m:
compiler/typecheck_info.m:
compiler/unify_gen_construct.m:
compiler/unify_gen_deconstruct.m:
compiler/unify_proc.m:
compiler/unique_modes.m:
compiler/unneeded_code.m:
compiler/untupling.m:
compiler/unused_args.m:
compiler/unused_imports.m:
compiler/var_locn.m:
compiler/write_deps_file.m:
compiler/write_module_interface_files.m:
Conform to the changes above.
769 lines
26 KiB
Mathematica
769 lines
26 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 1994-2012 The University of Melbourne.
|
|
% This file may only be copied under the terms of the GNU General
|
|
% Public License - see the file COPYING in the Mercury distribution.
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% File: middle_rec.m.
|
|
% Main authors: zs, conway.
|
|
%
|
|
% Code generation - do middle recursion optimization.
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module ll_backend.middle_rec.
|
|
:- interface.
|
|
|
|
:- import_module hlds.
|
|
:- import_module hlds.hlds_goal.
|
|
:- import_module ll_backend.code_info.
|
|
:- import_module ll_backend.code_loc_dep.
|
|
:- import_module ll_backend.llds.
|
|
|
|
:- pred match_and_generate(hlds_goal::in, llds_code::out,
|
|
code_info::in, code_info::out, code_loc_dep::in, code_loc_dep::out)
|
|
is semidet.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module backend_libs.
|
|
:- import_module backend_libs.builtin_ops.
|
|
:- import_module hlds.code_model.
|
|
:- import_module hlds.goal_form.
|
|
:- import_module hlds.hlds_llds.
|
|
:- import_module ll_backend.code_gen.
|
|
:- import_module ll_backend.code_util.
|
|
:- import_module ll_backend.opt_util.
|
|
:- import_module ll_backend.proc_gen.
|
|
:- import_module ll_backend.unify_gen_test.
|
|
:- import_module parse_tree.
|
|
:- import_module parse_tree.prog_data.
|
|
:- import_module parse_tree.var_table.
|
|
|
|
:- import_module assoc_list.
|
|
:- import_module bool.
|
|
:- import_module cord.
|
|
:- import_module int.
|
|
:- import_module list.
|
|
:- import_module maybe.
|
|
:- import_module require.
|
|
:- import_module set.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
match_and_generate(Goal, Instrs, !CI, !CLD) :-
|
|
Goal = hlds_goal(GoalExpr, GoalInfo),
|
|
GoalExpr = switch(Var, cannot_fail, [Case1, Case2]),
|
|
Case1 = case(ConsId1, [], Goal1),
|
|
Case2 = case(ConsId2, [], Goal2),
|
|
( if
|
|
contains_only_builtins(Goal1) = yes,
|
|
contains_simple_recursive_call(Goal2, !.CI)
|
|
then
|
|
middle_rec_generate_switch(Var, ConsId1, Goal1, Goal2,
|
|
GoalInfo, Instrs, !CI, !CLD)
|
|
else if
|
|
contains_only_builtins(Goal2) = yes,
|
|
contains_simple_recursive_call(Goal1, !.CI)
|
|
then
|
|
middle_rec_generate_switch(Var, ConsId2, Goal2, Goal1,
|
|
GoalInfo, Instrs, !CI, !CLD)
|
|
else
|
|
fail
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% contains_simple_recursive_call(G, CI, Last, ContainsTakeAddr)
|
|
% succeeds if G is a conjunction of goals, exactly one of which is a
|
|
% recursive call (CI says what the current procedure is), there are no
|
|
% other goals that cause control to leave this procedure, and there are
|
|
% no unifications that take the addresses of fields.
|
|
%
|
|
:- pred contains_simple_recursive_call(hlds_goal::in, code_info::in)
|
|
is semidet.
|
|
|
|
contains_simple_recursive_call(hlds_goal(GoalExpr, _), CodeInfo) :-
|
|
GoalExpr = conj(plain_conj, Goals),
|
|
contains_simple_recursive_call_conj(Goals, CodeInfo).
|
|
|
|
:- pred contains_simple_recursive_call_conj(list(hlds_goal)::in, code_info::in)
|
|
is semidet.
|
|
|
|
contains_simple_recursive_call_conj([Goal | Goals], CodeInfo) :-
|
|
Goal = hlds_goal(GoalExpr, _),
|
|
OnlyBuiltinsGoalExpr = contains_only_builtins_expr(GoalExpr),
|
|
(
|
|
OnlyBuiltinsGoalExpr = yes,
|
|
contains_simple_recursive_call_conj(Goals, CodeInfo)
|
|
;
|
|
OnlyBuiltinsGoalExpr = no,
|
|
is_recursive_call(GoalExpr, CodeInfo),
|
|
contains_only_builtins_list(Goals) = yes
|
|
).
|
|
|
|
:- pred is_recursive_call(hlds_goal_expr::in, code_info::in) is semidet.
|
|
|
|
is_recursive_call(Goal, CodeInfo) :-
|
|
Goal = plain_call(CallPredId, CallProcId, _, BuiltinState, _, _),
|
|
BuiltinState = not_builtin,
|
|
get_pred_id(CodeInfo, PredId),
|
|
PredId = CallPredId,
|
|
get_proc_id(CodeInfo, ProcId),
|
|
ProcId = CallProcId.
|
|
|
|
% contains_only_builtins(G) returns `yes' if G is a leaf procedure,
|
|
% i.e. control does not leave G to call another procedure, even
|
|
% if that procedure is a complicated unification. It also does not contain
|
|
% unifications that take the addresses of fields.
|
|
%
|
|
:- func contains_only_builtins(hlds_goal) = bool.
|
|
|
|
contains_only_builtins(hlds_goal(GoalExpr, _)) =
|
|
contains_only_builtins_expr(GoalExpr).
|
|
|
|
:- func contains_only_builtins_expr(hlds_goal_expr) = bool.
|
|
|
|
contains_only_builtins_expr(GoalExpr) = OnlyBuiltins :-
|
|
(
|
|
GoalExpr = conj(ConjType, Goals),
|
|
(
|
|
ConjType = plain_conj,
|
|
OnlyBuiltins = contains_only_builtins_list(Goals)
|
|
;
|
|
ConjType = parallel_conj,
|
|
OnlyBuiltins = no
|
|
)
|
|
;
|
|
GoalExpr = disj(Goals),
|
|
OnlyBuiltins = contains_only_builtins_list(Goals)
|
|
;
|
|
GoalExpr = switch(_Var, _CanFail, Cases),
|
|
OnlyBuiltins = contains_only_builtins_cases(Cases)
|
|
;
|
|
GoalExpr = negation(SubGoal),
|
|
OnlyBuiltins = contains_only_builtins(SubGoal)
|
|
;
|
|
GoalExpr = scope(Reason, SubGoal),
|
|
( if
|
|
Reason = from_ground_term(_, FGT),
|
|
( FGT = from_ground_term_construct
|
|
; FGT = from_ground_term_deconstruct
|
|
)
|
|
then
|
|
OnlyBuiltins = yes
|
|
else
|
|
OnlyBuiltins = contains_only_builtins(SubGoal)
|
|
)
|
|
;
|
|
GoalExpr = if_then_else(_Vars, Cond, Then, Else),
|
|
( if
|
|
contains_only_builtins(Cond) = yes,
|
|
contains_only_builtins(Then) = yes,
|
|
contains_only_builtins(Else) = yes
|
|
then
|
|
OnlyBuiltins = yes
|
|
else
|
|
OnlyBuiltins = no
|
|
)
|
|
;
|
|
GoalExpr = plain_call(_, _, _, BuiltinState, _, _),
|
|
(
|
|
BuiltinState = inline_builtin,
|
|
OnlyBuiltins = yes
|
|
;
|
|
BuiltinState = not_builtin,
|
|
OnlyBuiltins = no
|
|
)
|
|
;
|
|
GoalExpr = unify(_, _, _, Uni, _),
|
|
% Complicated unifies are _non_builtin_
|
|
(
|
|
Uni = assign(_, _),
|
|
OnlyBuiltins = yes
|
|
;
|
|
Uni = simple_test(_, _),
|
|
OnlyBuiltins = yes
|
|
;
|
|
Uni = construct(_, _, _, _, _, _, SubInfo),
|
|
(
|
|
SubInfo = no_construct_sub_info,
|
|
OnlyBuiltins = yes
|
|
;
|
|
SubInfo = construct_sub_info(TakeAddressFields, _),
|
|
(
|
|
TakeAddressFields = no,
|
|
OnlyBuiltins = yes
|
|
;
|
|
TakeAddressFields = yes(_),
|
|
OnlyBuiltins = no
|
|
)
|
|
)
|
|
;
|
|
Uni = deconstruct(_, _, _, _, _, _),
|
|
OnlyBuiltins = yes
|
|
;
|
|
Uni = complicated_unify(_, _, _),
|
|
OnlyBuiltins = no
|
|
)
|
|
;
|
|
( GoalExpr = call_foreign_proc(_, _, _, _, _, _, _)
|
|
; GoalExpr = generic_call(_, _, _, _, _)
|
|
),
|
|
OnlyBuiltins = no
|
|
;
|
|
GoalExpr = shorthand(_),
|
|
unexpected($pred, "shorthand")
|
|
).
|
|
|
|
:- func contains_only_builtins_cases(list(case)) = bool.
|
|
|
|
contains_only_builtins_cases([]) = yes.
|
|
contains_only_builtins_cases([case(_, _, Goal) | Cases]) = OnlyBuiltins :-
|
|
OnlyBuiltinsGoal = contains_only_builtins(Goal),
|
|
(
|
|
OnlyBuiltinsGoal = yes,
|
|
OnlyBuiltins = contains_only_builtins_cases(Cases)
|
|
;
|
|
OnlyBuiltinsGoal = no,
|
|
OnlyBuiltins = no
|
|
).
|
|
|
|
:- func contains_only_builtins_list(list(hlds_goal)) = bool.
|
|
|
|
contains_only_builtins_list([]) = yes.
|
|
contains_only_builtins_list([Goal | Goals]) = OnlyBuiltins :-
|
|
OnlyBuiltinsGoal = contains_only_builtins(Goal),
|
|
(
|
|
OnlyBuiltinsGoal = yes,
|
|
OnlyBuiltins = contains_only_builtins_list(Goals)
|
|
;
|
|
OnlyBuiltinsGoal = no,
|
|
OnlyBuiltins = no
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred middle_rec_generate_switch(prog_var::in, cons_id::in,
|
|
hlds_goal::in, hlds_goal::in, hlds_goal_info::in, llds_code::out,
|
|
code_info::in, code_info::out, code_loc_dep::in, code_loc_dep::out)
|
|
is semidet.
|
|
|
|
middle_rec_generate_switch(Var, BaseConsId, Base, Recursive, SwitchGoalInfo,
|
|
Code, !CI, !CLD) :-
|
|
get_stack_slots(!.CI, StackSlots),
|
|
get_var_table(!.CI, VarTable),
|
|
SlotsComment = explain_stack_slots(VarTable, StackSlots),
|
|
get_module_info(!.CI, ModuleInfo),
|
|
get_pred_id(!.CI, PredId),
|
|
get_proc_id(!.CI, ProcId),
|
|
EntryLabel = make_local_entry_label(ModuleInfo, PredId, ProcId,
|
|
for_from_everywhere),
|
|
|
|
pre_goal_update(SwitchGoalInfo, has_subgoals, !CLD),
|
|
produce_variable(Var, VarCode, VarRval, !CLD),
|
|
lookup_var_entry(VarTable, Var, VarEntry),
|
|
VarName = var_entry_name(Var, VarEntry),
|
|
VarType = VarEntry ^ vte_type,
|
|
CheaperTagTest = lookup_cheaper_tag_test(!.CI, VarType),
|
|
generate_test_var_has_cons_id(VarRval, VarName, BaseConsId,
|
|
CheaperTagTest, branch_on_success, BaseLabel, TestCode, !CI),
|
|
EntryTestInstrs = cord.list(VarCode ++ TestCode),
|
|
|
|
goal_info_get_store_map(SwitchGoalInfo, StoreMap),
|
|
remember_position(!.CLD, BranchStart),
|
|
generate_goal(model_det, Base, BaseGoalCode, !CI, !CLD),
|
|
generate_branch_end(StoreMap, no, MaybeEnd1, BaseSaveCode, !.CLD),
|
|
reset_to_position(BranchStart, !.CI, !:CLD),
|
|
generate_goal(model_det, Recursive, RecGoalCode, !CI, !CLD),
|
|
generate_branch_end(StoreMap, MaybeEnd1, MaybeEnd, RecSaveCode, !.CLD),
|
|
|
|
after_all_branches(StoreMap, MaybeEnd, !.CI, !:CLD),
|
|
post_goal_update(SwitchGoalInfo, !.CI, !CLD),
|
|
|
|
ArgModes = get_arginfo(!.CI),
|
|
HeadVars = get_headvars(!.CI),
|
|
assoc_list.from_corresponding_lists(HeadVars, ArgModes, Args),
|
|
setup_return(Args, LiveArgs, EpilogCode, !CLD),
|
|
|
|
BaseCode = BaseGoalCode ++ BaseSaveCode ++ EpilogCode,
|
|
RecCode = RecGoalCode ++ RecSaveCode ++ EpilogCode,
|
|
LiveValCode = singleton(
|
|
llds_instr(livevals(LiveArgs), "")
|
|
),
|
|
|
|
BaseInstrs = cord.list(BaseCode),
|
|
RecInstrs = cord.list(RecCode),
|
|
|
|
% In the code we generate, the base instruction sequence is executed
|
|
% in situations where this procedure has no stack frame. If this
|
|
% sequence refers to the stack frame, it will be to some other procedure's
|
|
% variables, which is obviously incorrect.
|
|
opt_util.block_refers_to_stack(BaseInstrs) = no,
|
|
|
|
AvoidInstrs = BaseInstrs ++ RecInstrs,
|
|
find_unused_register(AvoidInstrs, AuxReg),
|
|
|
|
split_rec_code(RecInstrs, BeforeInstrs0, AfterInstrs),
|
|
add_counter_to_livevals(BeforeInstrs0, AuxReg, BeforeInstrs),
|
|
|
|
get_next_label(Loop1Label, !CI),
|
|
get_next_label(Loop2Label, !CI),
|
|
get_total_stackslot_count(!.CI, FrameSize0),
|
|
FrameSize = round_det_stack_frame_size(!.CI, FrameSize0),
|
|
|
|
generate_downloop_test(EntryTestInstrs, Loop1Label, Loop1Test),
|
|
|
|
( if FrameSize = 0 then
|
|
MaybeIncrSp = empty,
|
|
MaybeDecrSp = empty,
|
|
InitAuxReg = singleton(
|
|
llds_instr(assign(AuxReg, const(llconst_int(0))),
|
|
"initialize counter register")
|
|
),
|
|
IncrAuxReg = singleton(
|
|
llds_instr(
|
|
assign(AuxReg,
|
|
binop(int_add(int_type_int), lval(AuxReg),
|
|
const(llconst_int(1)))),
|
|
"increment loop counter")
|
|
),
|
|
DecrAuxReg = singleton(
|
|
llds_instr(
|
|
assign(AuxReg,
|
|
binop(int_sub(int_type_int), lval(AuxReg),
|
|
const(llconst_int(1)))),
|
|
"decrement loop counter")
|
|
),
|
|
TestAuxReg = singleton(
|
|
llds_instr(
|
|
if_val(binop(
|
|
int_gt(int_type_int), lval(AuxReg), const(llconst_int(0))),
|
|
code_label(Loop2Label)),
|
|
"test on upward loop")
|
|
)
|
|
else
|
|
PushMsg = proc_gen.push_msg(ModuleInfo, PredId, ProcId),
|
|
MaybeIncrSp = singleton(
|
|
llds_instr(incr_sp(FrameSize, PushMsg, stack_incr_nonleaf), "")
|
|
),
|
|
MaybeDecrSp = singleton(
|
|
llds_instr(decr_sp(FrameSize), "")
|
|
),
|
|
InitAuxReg = singleton(
|
|
llds_instr(assign(AuxReg, lval(sp)), "initialize counter register")
|
|
),
|
|
IncrAuxReg = empty,
|
|
DecrAuxReg = empty,
|
|
TestAuxReg = singleton(
|
|
llds_instr(if_val(binop(
|
|
int_gt(int_type_int), lval(sp), lval(AuxReg)),
|
|
code_label(Loop2Label)),
|
|
"test on upward loop")
|
|
)
|
|
),
|
|
|
|
% Even though the recursive call is followed by some goals in the HLDS,
|
|
% these goals may generate no LLDS code, so it is in fact possible for
|
|
% AfterInstrs to be empty. There is no point in testing BeforeInstrs
|
|
% for empty, since if it is, the code is an infinite loop anyway.
|
|
|
|
(
|
|
AfterInstrs = [],
|
|
Code =
|
|
from_list([
|
|
llds_instr(label(EntryLabel), "Procedure entry point"),
|
|
llds_instr(comment(SlotsComment), "")
|
|
]) ++
|
|
from_list(EntryTestInstrs) ++
|
|
singleton(
|
|
llds_instr(label(Loop1Label), "start of the down loop")
|
|
) ++
|
|
from_list(BeforeInstrs) ++
|
|
from_list(Loop1Test) ++
|
|
singleton(
|
|
llds_instr(label(BaseLabel), "start of base case")
|
|
) ++
|
|
from_list(BaseInstrs) ++
|
|
LiveValCode ++
|
|
singleton(
|
|
llds_instr(goto(code_succip), "exit from base case")
|
|
)
|
|
;
|
|
AfterInstrs = [_ | _],
|
|
% The instruction list we are constructing has two copies of BaseList.
|
|
% If this list of instructions defines any labels, we must either not
|
|
% apply this version of the optimization, or we must consistently
|
|
% substitute the labels (which will be referred to only from within the
|
|
% BaseList instructions themselves). We choose the former course.
|
|
find_labels(BaseInstrs, BaseLabels),
|
|
BaseLabels = [],
|
|
Code =
|
|
from_list([
|
|
llds_instr(label(EntryLabel), "Procedure entry point"),
|
|
llds_instr(comment(SlotsComment), "")
|
|
]) ++
|
|
from_list(EntryTestInstrs) ++
|
|
InitAuxReg ++
|
|
singleton(
|
|
llds_instr(label(Loop1Label), "start of the down loop")
|
|
) ++
|
|
MaybeIncrSp ++
|
|
IncrAuxReg ++
|
|
from_list(BeforeInstrs) ++
|
|
from_list(Loop1Test) ++
|
|
from_list(BaseInstrs) ++
|
|
singleton(
|
|
llds_instr(label(Loop2Label), "")
|
|
) ++
|
|
from_list(AfterInstrs) ++
|
|
MaybeDecrSp ++
|
|
DecrAuxReg ++
|
|
TestAuxReg ++
|
|
LiveValCode ++
|
|
from_list([
|
|
llds_instr(goto(code_succip), "exit from recursive case"),
|
|
llds_instr(label(BaseLabel), "start of base case")
|
|
]) ++
|
|
from_list(BaseInstrs) ++
|
|
LiveValCode ++
|
|
singleton(
|
|
llds_instr(goto(code_succip), "exit from base case")
|
|
)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred generate_downloop_test(list(instruction)::in, label::in,
|
|
list(instruction)::out) is det.
|
|
|
|
generate_downloop_test([], _, _) :-
|
|
unexpected($pred, "empty list").
|
|
generate_downloop_test([Instr0 | Instrs0], Target, Instrs) :-
|
|
( if Instr0 = llds_instr(if_val(Test, _OldTarget), _Comment) then
|
|
(
|
|
Instrs0 = []
|
|
;
|
|
Instrs0 = [_ | _],
|
|
unexpected($pred, "if_val followed by other instructions")
|
|
),
|
|
code_util.neg_rval(Test, NewTest),
|
|
Instrs = [
|
|
llds_instr(if_val(NewTest, code_label(Target)),
|
|
"test on downward loop")
|
|
]
|
|
else
|
|
generate_downloop_test(Instrs0, Target, Instrs1),
|
|
Instrs = [Instr0 | Instrs1]
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred split_rec_code(list(instruction)::in,
|
|
list(instruction)::out, list(instruction)::out) is det.
|
|
|
|
split_rec_code([], _, _) :-
|
|
unexpected($pred, "did not find call").
|
|
split_rec_code([Instr0 | Instrs1], Before, After) :-
|
|
( if Instr0 = llds_instr(llcall(_, _, _, _, _, _), _) then
|
|
( if
|
|
opt_util.skip_comments(Instrs1, Instrs2),
|
|
Instrs2 = [Instr2 | Instrs3],
|
|
Instr2 = llds_instr(label(_), _)
|
|
then
|
|
Before = [],
|
|
After = Instrs3
|
|
else
|
|
unexpected($pred, "call not followed by label")
|
|
)
|
|
else
|
|
split_rec_code(Instrs1, Before1, After),
|
|
Before = [Instr0 | Before1]
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred add_counter_to_livevals(list(instruction)::in, lval::in,
|
|
list(instruction)::out) is det.
|
|
|
|
add_counter_to_livevals([], _Lval, []).
|
|
add_counter_to_livevals([Instr0 | Instrs0], Lval, [Instr | Instrs]) :-
|
|
( if Instr0 = llds_instr(livevals(Lives0), Comment) then
|
|
set.insert(Lval, Lives0, Lives),
|
|
Instr = llds_instr(livevals(Lives), Comment)
|
|
else
|
|
Instr = Instr0
|
|
),
|
|
add_counter_to_livevals(Instrs0, Lval, Instrs).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred find_unused_register(list(instruction)::in, lval::out)
|
|
is det.
|
|
|
|
find_unused_register(Instrs, UnusedReg) :-
|
|
set.init(Used0),
|
|
find_used_registers(Instrs, Used0, Used1),
|
|
set.to_sorted_list(Used1, UsedList),
|
|
find_unused_register_acc(UsedList, 1, UnusedReg).
|
|
|
|
:- pred find_unused_register_acc(list(int)::in, int::in, lval::out) is det.
|
|
|
|
find_unused_register_acc([], N, reg(reg_r, N)).
|
|
find_unused_register_acc([H | T], N, Reg) :-
|
|
( if N < H then
|
|
Reg = reg(reg_r, N)
|
|
else
|
|
N1 = N + 1,
|
|
find_unused_register_acc(T, N1, Reg)
|
|
).
|
|
|
|
:- pred find_used_registers(list(instruction)::in,
|
|
set(int)::in, set(int)::out) is det.
|
|
|
|
find_used_registers([], !Used).
|
|
find_used_registers([llds_instr(Uinstr, _) | Instrs], !Used) :-
|
|
find_used_registers_instr(Uinstr, !Used),
|
|
find_used_registers(Instrs, !Used).
|
|
|
|
:- pred find_used_registers_instr(instr::in,
|
|
set(int)::in, set(int)::out) is det.
|
|
|
|
find_used_registers_instr(Uinstr, !Used) :-
|
|
(
|
|
( Uinstr = comment(_)
|
|
; Uinstr = llcall(_, _, _, _, _, _)
|
|
; Uinstr = mkframe(_, _)
|
|
; Uinstr = label(_)
|
|
; Uinstr = goto(_)
|
|
; Uinstr = arbitrary_c_code(_, _, _)
|
|
; Uinstr = push_region_frame(_Id, _EmbeddedStackFrame)
|
|
; Uinstr = use_and_maybe_pop_region_frame(_UseOp, _EmbeddedStackFrame)
|
|
; Uinstr = discard_ticket
|
|
; Uinstr = prune_ticket
|
|
; Uinstr = incr_sp(_, _, _)
|
|
; Uinstr = decr_sp(_)
|
|
; Uinstr = decr_sp_and_return(_)
|
|
)
|
|
;
|
|
Uinstr = livevals(LvalSet),
|
|
set.to_sorted_list(LvalSet, LvalList),
|
|
find_used_registers_lvals(LvalList, !Used)
|
|
;
|
|
Uinstr = block(_, _, Instrs),
|
|
find_used_registers(Instrs, !Used)
|
|
;
|
|
( Uinstr = assign(Lval, Rval)
|
|
; Uinstr = keep_assign(Lval, Rval)
|
|
),
|
|
find_used_registers_lval(Lval, !Used),
|
|
find_used_registers_rval(Rval, !Used)
|
|
;
|
|
Uinstr = incr_hp(Lval, _, _, Rval, _, _, MaybeRegionRval, MaybeReuse),
|
|
find_used_registers_lval(Lval, !Used),
|
|
find_used_registers_rval(Rval, !Used),
|
|
(
|
|
MaybeRegionRval = yes(RegionRval),
|
|
find_used_registers_rval(RegionRval, !Used)
|
|
;
|
|
MaybeRegionRval = no
|
|
),
|
|
(
|
|
MaybeReuse = llds_reuse(ReuseRval, MaybeFlagLval),
|
|
find_used_registers_rval(ReuseRval, !Used),
|
|
(
|
|
MaybeFlagLval = yes(FlagLval),
|
|
find_used_registers_lval(FlagLval, !Used)
|
|
;
|
|
MaybeFlagLval = no
|
|
)
|
|
;
|
|
MaybeReuse = no_llds_reuse
|
|
)
|
|
;
|
|
Uinstr = region_fill_frame(_FillOp, _EmbeddedStackFrame,
|
|
IdRval, NumLval, AddrLval),
|
|
find_used_registers_rval(IdRval, !Used),
|
|
find_used_registers_lval(NumLval, !Used),
|
|
find_used_registers_lval(AddrLval, !Used)
|
|
;
|
|
Uinstr = region_set_fixed_slot(_SetOp, _EmbeddedStackFrame, ValueRval),
|
|
find_used_registers_rval(ValueRval, !Used)
|
|
;
|
|
Uinstr = foreign_proc_code(_, Components, _, _, _, _, _, _, _, _),
|
|
find_used_registers_components(Components, !Used)
|
|
;
|
|
( Uinstr = computed_goto(Rval, _)
|
|
; Uinstr = if_val(Rval, _)
|
|
; Uinstr = restore_hp(Rval)
|
|
; Uinstr = free_heap(Rval)
|
|
; Uinstr = reset_ticket(Rval, _Rsn)
|
|
; Uinstr = prune_tickets_to(Rval)
|
|
),
|
|
find_used_registers_rval(Rval, !Used)
|
|
;
|
|
( Uinstr = save_maxfr(Lval)
|
|
; Uinstr = restore_maxfr(Lval)
|
|
; Uinstr = mark_hp(Lval)
|
|
; Uinstr = store_ticket(Lval)
|
|
; Uinstr = mark_ticket_stack(Lval)
|
|
; Uinstr = init_sync_term(Lval, _, _)
|
|
; Uinstr = fork_new_child(Lval, _)
|
|
; Uinstr = join_and_continue(Lval, _)
|
|
),
|
|
find_used_registers_lval(Lval, !Used)
|
|
;
|
|
Uinstr = lc_create_loop_control(_, LCLval),
|
|
find_used_registers_lval(LCLval, !Used)
|
|
;
|
|
Uinstr = lc_wait_free_slot(LCRval, LCSLval, _),
|
|
find_used_registers_rval(LCRval, !Used),
|
|
find_used_registers_lval(LCSLval, !Used)
|
|
;
|
|
Uinstr = lc_spawn_off(LCRval, LCSRval, _),
|
|
find_used_registers_rval(LCRval, !Used),
|
|
find_used_registers_rval(LCSRval, !Used)
|
|
;
|
|
Uinstr = lc_join_and_terminate(LCRval, LCSRval),
|
|
find_used_registers_rval(LCRval, !Used),
|
|
find_used_registers_rval(LCSRval, !Used)
|
|
).
|
|
|
|
:- pred find_used_registers_components(
|
|
list(foreign_proc_component)::in,
|
|
set(int)::in, set(int)::out) is det.
|
|
|
|
find_used_registers_components([], !Used).
|
|
find_used_registers_components([Comp | Comps], !Used) :-
|
|
find_used_registers_component(Comp, !Used),
|
|
find_used_registers_components(Comps, !Used).
|
|
|
|
:- pred find_used_registers_component(foreign_proc_component::in,
|
|
set(int)::in, set(int)::out) is det.
|
|
|
|
find_used_registers_component(foreign_proc_inputs(In), !Used) :-
|
|
insert_foreign_proc_input_registers(In, !Used).
|
|
find_used_registers_component(foreign_proc_outputs(Out), !Used) :-
|
|
insert_foreign_proc_output_registers(Out, !Used).
|
|
find_used_registers_component(foreign_proc_user_code(_, _, _), !Used).
|
|
find_used_registers_component(foreign_proc_raw_code(_, _, _, _), !Used).
|
|
find_used_registers_component(foreign_proc_fail_to(_), !Used).
|
|
find_used_registers_component(foreign_proc_alloc_id(_), !Used).
|
|
find_used_registers_component(foreign_proc_noop, !Used).
|
|
|
|
:- pred find_used_registers_lvals(list(lval)::in,
|
|
set(int)::in, set(int)::out) is det.
|
|
|
|
find_used_registers_lvals([], !Used).
|
|
find_used_registers_lvals([Lval | Lvals], !Used) :-
|
|
find_used_registers_lval(Lval, !Used),
|
|
find_used_registers_lvals(Lvals, !Used).
|
|
|
|
:- pred find_used_registers_lval(lval::in,
|
|
set(int)::in, set(int)::out) is det.
|
|
|
|
find_used_registers_lval(Lval, !Used) :-
|
|
( if Lval = reg(reg_r, N) then
|
|
copy(N, N1),
|
|
set.insert(N1, !Used)
|
|
else if Lval = field(_, Rval, FieldNum) then
|
|
find_used_registers_rval(Rval, !Used),
|
|
find_used_registers_rval(FieldNum, !Used)
|
|
else if Lval = lvar(_) then
|
|
unexpected($pred, "lvar")
|
|
else
|
|
true
|
|
).
|
|
|
|
:- pred find_used_registers_rval(rval::in, set(int)::in, set(int)::out) is det.
|
|
|
|
find_used_registers_rval(Rval, !Used) :-
|
|
(
|
|
Rval = lval(Lval),
|
|
find_used_registers_lval(Lval, !Used)
|
|
;
|
|
Rval = var(_),
|
|
unexpected($pred, "var")
|
|
;
|
|
Rval = mkword_hole(_)
|
|
;
|
|
Rval = const(_)
|
|
;
|
|
( Rval = mkword(_, SubRval)
|
|
; Rval = cast(_, SubRval)
|
|
; Rval = unop(_, SubRval)
|
|
),
|
|
find_used_registers_rval(SubRval, !Used)
|
|
;
|
|
Rval = binop(_, SubRvalA, SubRvalB),
|
|
find_used_registers_rval(SubRvalA, !Used),
|
|
find_used_registers_rval(SubRvalB, !Used)
|
|
;
|
|
Rval = mem_addr(MemRef),
|
|
find_used_registers_mem_ref(MemRef, !Used)
|
|
).
|
|
|
|
:- pred find_used_registers_mem_ref(mem_ref::in,
|
|
set(int)::in, set(int)::out) is det.
|
|
|
|
find_used_registers_mem_ref(stackvar_ref(Rval), !Used) :-
|
|
find_used_registers_rval(Rval, !Used).
|
|
find_used_registers_mem_ref(framevar_ref(Rval), !Used) :-
|
|
find_used_registers_rval(Rval, !Used).
|
|
find_used_registers_mem_ref(heap_ref(Rval1, _, Rval2), !Used) :-
|
|
find_used_registers_rval(Rval1, !Used),
|
|
find_used_registers_rval(Rval2, !Used).
|
|
|
|
:- pred insert_foreign_proc_input_registers(list(foreign_proc_input)::in,
|
|
set(int)::in, set(int)::out) is det.
|
|
|
|
insert_foreign_proc_input_registers([], !Used).
|
|
insert_foreign_proc_input_registers([Input | Inputs], !Used) :-
|
|
Input = foreign_proc_input(_, _, _, _, Rval, _, _),
|
|
find_used_registers_rval(Rval, !Used),
|
|
insert_foreign_proc_input_registers(Inputs, !Used).
|
|
|
|
:- pred insert_foreign_proc_output_registers(list(foreign_proc_output)::in,
|
|
set(int)::in, set(int)::out) is det.
|
|
|
|
insert_foreign_proc_output_registers([], !Used).
|
|
insert_foreign_proc_output_registers([Output | Outputs], !Used) :-
|
|
Output = foreign_proc_output(Lval, _, _, _, _, _, _),
|
|
find_used_registers_lval(Lval, !Used),
|
|
insert_foreign_proc_output_registers(Outputs, !Used).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Find all the labels defined in an instruction sequence.
|
|
%
|
|
:- pred find_labels(list(instruction)::in, list(label)::out) is det.
|
|
|
|
find_labels(Instrs, Labels) :-
|
|
find_labels_acc(Instrs, [], Labels).
|
|
|
|
:- pred find_labels_acc(list(instruction)::in,
|
|
list(label)::in, list(label)::out) is det.
|
|
|
|
find_labels_acc([], !Labels).
|
|
find_labels_acc([Instr | Instrs], !Labels) :-
|
|
Instr = llds_instr(Uinstr, _),
|
|
( if Uinstr = label(Label) then
|
|
!:Labels = [Label | !.Labels]
|
|
else if Uinstr = block(_, _, Block) then
|
|
find_labels_acc(Block, !Labels)
|
|
else
|
|
true
|
|
),
|
|
find_labels_acc(Instrs, !Labels).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module ll_backend.middle_rec.
|
|
%---------------------------------------------------------------------------%
|
|
|