mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-14 21:35:49 +00:00
Estimated hours taken: 20 These changes make `var' and `term' polymorphic. This allows us to make variables and terms representing types of a different type to those representing program terms and those representing insts. These changes do not *fix* any existing problems (for instance there was a messy conflation of program variables and inst variables, and where necessary I've just called varset__init(InstVarSet) with an XXX comment). NEWS: Mention the changes to the standard library. library/term.m: Make term, var and var_supply polymorphic. Add new predicates: term__generic_term/1 term__coerce/2 term__coerce_var/2 term__coerce_var_supply/2 library/varset.m: Make varset polymorphic. Add the new predicate: varset__coerce/2 compiler/prog_data.m: Introduce type equivalences for the different kinds of vars, terms, and varsets that we use (tvar and tvarset were already there but have been changed to use the polymorphic var and term). Also change the various kinds of items to use the appropriate kinds of var/varset. compiler/*.m: Thousands of boring changes to make the compiler type correct with the different types for type, program and inst vars and varsets.
440 lines
16 KiB
Mathematica
440 lines
16 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 1994-1998 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.
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Original author: conway.
|
|
% Extensive modification by zs.
|
|
|
|
% Allocates the storage location for each live variable at the end of
|
|
% each branched structure, so that the code generator will generate code
|
|
% which puts the variable in the same place in each branch.
|
|
|
|
% This module requires arg_infos and livenesses to have already been computed,
|
|
% and stack slots allocated.
|
|
|
|
% If the appropriate option is set, the code calls the follow_vars module
|
|
% to help guide its decisions.
|
|
|
|
% See compiler/notes/allocation.html for a description of the framework that
|
|
% this pass operates within.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- module store_alloc.
|
|
|
|
:- interface.
|
|
|
|
:- import_module hlds_module, hlds_pred.
|
|
|
|
:- pred store_alloc_in_proc(proc_info, pred_id, module_info, proc_info).
|
|
:- mode store_alloc_in_proc(in, in, in, out) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module follow_vars, liveness, hlds_goal, llds, prog_data.
|
|
:- import_module options, globals, goal_util, mode_util, instmap, trace.
|
|
:- import_module list, map, set, std_util, assoc_list.
|
|
:- import_module bool, int, require.
|
|
|
|
:- type stack_slot_info
|
|
---> stack_slot_info(
|
|
bool, % was follow_vars run?
|
|
int, % the number of real r regs
|
|
stack_slots % maps each var to its stack slot
|
|
% (if it has one)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
store_alloc_in_proc(ProcInfo0, PredId, ModuleInfo, ProcInfo) :-
|
|
module_info_globals(ModuleInfo, Globals),
|
|
globals__lookup_bool_option(Globals, follow_vars, ApplyFollowVars),
|
|
( ApplyFollowVars = yes ->
|
|
proc_info_goal(ProcInfo0, Goal0),
|
|
|
|
find_final_follow_vars(ProcInfo0, FollowVars0),
|
|
find_follow_vars_in_goal(Goal0, ModuleInfo,
|
|
FollowVars0, Goal1, FollowVars),
|
|
Goal1 = GoalExpr1 - GoalInfo1,
|
|
goal_info_set_follow_vars(GoalInfo1, yes(FollowVars),
|
|
GoalInfo2),
|
|
Goal2 = GoalExpr1 - GoalInfo2
|
|
;
|
|
proc_info_goal(ProcInfo0, Goal2)
|
|
),
|
|
initial_liveness(ProcInfo0, PredId, ModuleInfo, Liveness0),
|
|
globals__get_trace_level(Globals, TraceLevel),
|
|
( TraceLevel \= none ->
|
|
trace__fail_vars(ModuleInfo, ProcInfo0, ResumeVars0)
|
|
;
|
|
set__init(ResumeVars0)
|
|
),
|
|
globals__lookup_int_option(Globals, num_real_r_regs, NumRealRRegs),
|
|
proc_info_stack_slots(ProcInfo0, StackSlots),
|
|
StackSlotsInfo = stack_slot_info(ApplyFollowVars, NumRealRRegs,
|
|
StackSlots),
|
|
store_alloc_in_goal(Goal2, Liveness0, ResumeVars0, ModuleInfo,
|
|
StackSlotsInfo, Goal, _),
|
|
proc_info_set_goal(ProcInfo0, Goal, ProcInfo).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred store_alloc_in_goal(hlds_goal, liveness_info, set(prog_var),
|
|
module_info, stack_slot_info, hlds_goal, liveness_info).
|
|
:- mode store_alloc_in_goal(in, in, in, in, in, out, out) is det.
|
|
|
|
store_alloc_in_goal(Goal0 - GoalInfo0, Liveness0, ResumeVars0, ModuleInfo,
|
|
StackSlotInfo, Goal - GoalInfo0, Liveness) :-
|
|
% note: we must be careful to apply deaths before births
|
|
goal_info_get_pre_deaths(GoalInfo0, PreDeaths),
|
|
goal_info_get_pre_births(GoalInfo0, PreBirths),
|
|
goal_info_get_post_deaths(GoalInfo0, PostDeaths),
|
|
goal_info_get_post_births(GoalInfo0, PostBirths),
|
|
|
|
set__difference(Liveness0, PreDeaths, Liveness1),
|
|
set__union(Liveness1, PreBirths, Liveness2),
|
|
store_alloc_in_goal_2(Goal0, Liveness2, ResumeVars0, ModuleInfo,
|
|
StackSlotInfo, Goal1, Liveness3),
|
|
set__difference(Liveness3, PostDeaths, Liveness4),
|
|
% If any variables magically become live in the PostBirths,
|
|
% then they have to mundanely become live in a parallel goal,
|
|
% so we don't need to allocate anything for them here.
|
|
%
|
|
% Any variables that become magically live at the end of the goal
|
|
% should not be included in the store map.
|
|
set__union(Liveness4, PostBirths, Liveness),
|
|
(
|
|
Goal1 = switch(Var, CanFail, Cases, FollowVars)
|
|
->
|
|
set__union(Liveness4, ResumeVars0, MappedSet),
|
|
set__to_sorted_list(MappedSet, MappedVars),
|
|
store_alloc_allocate_storage(MappedVars, FollowVars,
|
|
StackSlotInfo, StoreMap),
|
|
Goal = switch(Var, CanFail, Cases, StoreMap)
|
|
;
|
|
Goal1 = if_then_else(Vars, Cond, Then, Else, FollowVars)
|
|
->
|
|
set__union(Liveness4, ResumeVars0, MappedSet),
|
|
set__to_sorted_list(MappedSet, MappedVars),
|
|
store_alloc_allocate_storage(MappedVars, FollowVars,
|
|
StackSlotInfo, StoreMap),
|
|
Goal = if_then_else(Vars, Cond, Then, Else, StoreMap)
|
|
;
|
|
Goal1 = disj(Disjuncts, FollowVars)
|
|
->
|
|
set__union(Liveness4, ResumeVars0, MappedSet),
|
|
set__to_sorted_list(MappedSet, MappedVars),
|
|
store_alloc_allocate_storage(MappedVars, FollowVars,
|
|
StackSlotInfo, StoreMap),
|
|
Goal = disj(Disjuncts, StoreMap)
|
|
;
|
|
Goal = Goal1
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Here we process each of the different sorts of goals.
|
|
|
|
:- pred store_alloc_in_goal_2(hlds_goal_expr, liveness_info,
|
|
set(prog_var), module_info, stack_slot_info, hlds_goal_expr,
|
|
liveness_info).
|
|
:- mode store_alloc_in_goal_2(in, in, in, in, in, out, out) is det.
|
|
|
|
store_alloc_in_goal_2(conj(Goals0), Liveness0, ResumeVars0, ModuleInfo,
|
|
StackSlotInfo, conj(Goals), Liveness) :-
|
|
store_alloc_in_conj(Goals0, Liveness0, ResumeVars0, ModuleInfo,
|
|
StackSlotInfo, Goals, Liveness).
|
|
|
|
store_alloc_in_goal_2(par_conj(Goals0, SM), Liveness0, ResumeVars0, ModuleInfo,
|
|
StackSlotInfo, par_conj(Goals, SM), Liveness) :-
|
|
store_alloc_in_par_conj(Goals0, Liveness0, ResumeVars0, ModuleInfo,
|
|
StackSlotInfo, Goals, Liveness).
|
|
|
|
store_alloc_in_goal_2(disj(Goals0, FV), Liveness0, ResumeVars0, ModuleInfo,
|
|
StackSlotInfo, disj(Goals, FV), Liveness) :-
|
|
store_alloc_in_disj(Goals0, Liveness0, ResumeVars0, ModuleInfo,
|
|
StackSlotInfo, Goals, Liveness).
|
|
|
|
store_alloc_in_goal_2(not(Goal0), Liveness0, _ResumeVars0, ModuleInfo,
|
|
StackSlotInfo, not(Goal), Liveness) :-
|
|
Goal0 = _ - GoalInfo0,
|
|
goal_info_get_resume_point(GoalInfo0, ResumeNot),
|
|
goal_info_resume_vars_and_loc(ResumeNot, ResumeNotVars, _),
|
|
store_alloc_in_goal(Goal0, Liveness0, ResumeNotVars, ModuleInfo,
|
|
StackSlotInfo, Goal, Liveness).
|
|
|
|
store_alloc_in_goal_2(switch(Var, Det, Cases0, FV), Liveness0, ResumeVars0,
|
|
ModuleInfo, StackSlotInfo,
|
|
switch(Var, Det, Cases, FV), Liveness) :-
|
|
store_alloc_in_cases(Cases0, Liveness0, ResumeVars0, ModuleInfo,
|
|
StackSlotInfo, Cases, Liveness).
|
|
|
|
store_alloc_in_goal_2(if_then_else(Vars, Cond0, Then0, Else0, FV),
|
|
Liveness0, ResumeVars0, ModuleInfo, StackSlotInfo,
|
|
if_then_else(Vars, Cond, Then, Else, FV), Liveness) :-
|
|
Cond0 = _ - CondGoalInfo0,
|
|
goal_info_get_resume_point(CondGoalInfo0, ResumeCond),
|
|
goal_info_resume_vars_and_loc(ResumeCond, ResumeCondVars, _),
|
|
store_alloc_in_goal(Cond0, Liveness0, ResumeCondVars, ModuleInfo,
|
|
StackSlotInfo, Cond, Liveness1),
|
|
store_alloc_in_goal(Then0, Liveness1, ResumeVars0, ModuleInfo,
|
|
StackSlotInfo, Then, Liveness),
|
|
store_alloc_in_goal(Else0, Liveness0, ResumeVars0, ModuleInfo,
|
|
StackSlotInfo, Else, _Liveness2).
|
|
|
|
store_alloc_in_goal_2(some(Vars, Goal0), Liveness0, ResumeVars0, ModuleInfo,
|
|
StackSlotInfo, some(Vars, Goal), Liveness) :-
|
|
store_alloc_in_goal(Goal0, Liveness0, ResumeVars0, ModuleInfo,
|
|
StackSlotInfo, Goal, Liveness).
|
|
|
|
store_alloc_in_goal_2(higher_order_call(A, B, C, D, E, F), Liveness, _, _,
|
|
_, higher_order_call(A, B, C, D, E, F), Liveness).
|
|
|
|
store_alloc_in_goal_2(class_method_call(A, B, C, D, E, F), Liveness, _, _,
|
|
_, class_method_call(A, B, C, D, E, F), Liveness).
|
|
|
|
store_alloc_in_goal_2(call(A, B, C, D, E, F), Liveness, _, _,
|
|
_, call(A, B, C, D, E, F), Liveness).
|
|
|
|
store_alloc_in_goal_2(unify(A,B,C,D,E), Liveness, _, _,
|
|
_, unify(A,B,C,D,E), Liveness).
|
|
|
|
store_alloc_in_goal_2(pragma_c_code(A, B, C, D, E, F, G), Liveness, _, _,
|
|
_, pragma_c_code(A, B, C, D, E, F, G), Liveness).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred store_alloc_in_conj(list(hlds_goal), liveness_info, set(prog_var),
|
|
module_info, stack_slot_info, list(hlds_goal), liveness_info).
|
|
:- mode store_alloc_in_conj(in, in, in, in, in, out, out) is det.
|
|
|
|
store_alloc_in_conj([], Liveness, _, _, _, [], Liveness).
|
|
store_alloc_in_conj([Goal0 | Goals0], Liveness0, ResumeVars0, ModuleInfo,
|
|
StackSlotInfo, [Goal | Goals], Liveness) :-
|
|
(
|
|
% XXX should be threading the instmap
|
|
Goal0 = _ - GoalInfo,
|
|
goal_info_get_instmap_delta(GoalInfo, InstMapDelta),
|
|
instmap_delta_is_unreachable(InstMapDelta)
|
|
->
|
|
store_alloc_in_goal(Goal0, Liveness0, ResumeVars0, ModuleInfo,
|
|
StackSlotInfo, Goal, Liveness),
|
|
Goals = Goals0
|
|
;
|
|
store_alloc_in_goal(Goal0, Liveness0, ResumeVars0, ModuleInfo,
|
|
StackSlotInfo, Goal, Liveness1),
|
|
store_alloc_in_conj(Goals0, Liveness1, ResumeVars0, ModuleInfo,
|
|
StackSlotInfo, Goals, Liveness)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred store_alloc_in_par_conj(list(hlds_goal), liveness_info, set(prog_var),
|
|
module_info, stack_slot_info, list(hlds_goal), liveness_info).
|
|
:- mode store_alloc_in_par_conj(in, in, in, in, in, out, out) is det.
|
|
|
|
store_alloc_in_par_conj([], Liveness, _, _, _, [], Liveness).
|
|
store_alloc_in_par_conj([Goal0 | Goals0], Liveness0, ResumeVars0, ModuleInfo,
|
|
StackSlotInfo, [Goal | Goals], Liveness) :-
|
|
store_alloc_in_goal(Goal0, Liveness0, ResumeVars0, ModuleInfo,
|
|
StackSlotInfo, Goal, Liveness),
|
|
store_alloc_in_par_conj(Goals0, Liveness0, ResumeVars0, ModuleInfo,
|
|
StackSlotInfo, Goals, _Liveness1).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred store_alloc_in_disj(list(hlds_goal), liveness_info, set(prog_var),
|
|
module_info, stack_slot_info, list(hlds_goal), liveness_info).
|
|
:- mode store_alloc_in_disj(in, in, in, in, in, out, out) is det.
|
|
|
|
store_alloc_in_disj([], Liveness, _, _, _, [], Liveness).
|
|
store_alloc_in_disj([Goal0 | Goals0], Liveness0, ResumeVars0, ModuleInfo,
|
|
StackSlotInfo, [Goal | Goals], Liveness) :-
|
|
Goal0 = _ - GoalInfo0,
|
|
goal_info_get_resume_point(GoalInfo0, ResumeGoal),
|
|
(
|
|
ResumeGoal = no_resume_point,
|
|
ResumeGoalVars = ResumeVars0
|
|
;
|
|
ResumeGoal = resume_point(ResumeGoalVars, _)
|
|
),
|
|
store_alloc_in_goal(Goal0, Liveness0, ResumeGoalVars, ModuleInfo,
|
|
StackSlotInfo, Goal, Liveness),
|
|
store_alloc_in_disj(Goals0, Liveness0, ResumeVars0, ModuleInfo,
|
|
StackSlotInfo, Goals, _Liveness1).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred store_alloc_in_cases(list(case), liveness_info, set(prog_var),
|
|
module_info, stack_slot_info, list(case), liveness_info).
|
|
:- mode store_alloc_in_cases(in, in, in, in, in, out, out) is det.
|
|
|
|
store_alloc_in_cases([], Liveness, _, _, _, [], Liveness).
|
|
store_alloc_in_cases([case(Cons, Goal0) | Goals0], Liveness0, ResumeVars0,
|
|
ModuleInfo, StackSlotInfo,
|
|
[case(Cons, Goal) | Goals], Liveness) :-
|
|
store_alloc_in_goal(Goal0, Liveness0, ResumeVars0, ModuleInfo,
|
|
StackSlotInfo, Goal, Liveness),
|
|
store_alloc_in_cases(Goals0, Liveness0, ResumeVars0, ModuleInfo,
|
|
StackSlotInfo, Goals, _Liveness1).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Given a follow_map which
|
|
%
|
|
% 1 may contain entries for non-live variables,
|
|
%
|
|
% 2 may contain no entry for a live variable,
|
|
%
|
|
% 3 which may map two live variables to one lval, and/or
|
|
%
|
|
% 4 map an lval to the artificial location reg(r(-1)),
|
|
%
|
|
% generate a store map that maps every live variable to its own
|
|
% real location.
|
|
|
|
:- pred store_alloc_allocate_storage(list(prog_var), follow_vars,
|
|
stack_slot_info, store_map).
|
|
:- mode store_alloc_allocate_storage(in, in, in, out) is det.
|
|
|
|
store_alloc_allocate_storage(LiveVars, FollowVars, StackSlotInfo, StoreMap) :-
|
|
|
|
% This addresses point 1
|
|
map__keys(FollowVars, FollowKeys),
|
|
store_alloc_remove_nonlive(FollowKeys, LiveVars, FollowVars, StoreMap0),
|
|
|
|
% This addresses points 3 and 4
|
|
map__keys(StoreMap0, StoreVars),
|
|
set__init(SeenLvals0),
|
|
store_alloc_handle_conflicts_and_nonreal(StoreVars, 1, N,
|
|
SeenLvals0, SeenLvals, StoreMap0, StoreMap1),
|
|
|
|
% This addresses point 2
|
|
store_alloc_allocate_extras(LiveVars, N, SeenLvals, StackSlotInfo,
|
|
StoreMap1, StoreMap).
|
|
|
|
:- pred store_alloc_remove_nonlive(list(prog_var), list(prog_var),
|
|
store_map, store_map).
|
|
:- mode store_alloc_remove_nonlive(in, in, in, out) is det.
|
|
|
|
store_alloc_remove_nonlive([], _LiveVars, StoreMap, StoreMap).
|
|
store_alloc_remove_nonlive([Var | Vars], LiveVars, StoreMap0, StoreMap) :-
|
|
( list__member(Var, LiveVars) ->
|
|
StoreMap1 = StoreMap0
|
|
;
|
|
map__delete(StoreMap0, Var, StoreMap1)
|
|
),
|
|
store_alloc_remove_nonlive(Vars, LiveVars, StoreMap1, StoreMap).
|
|
|
|
:- pred store_alloc_handle_conflicts_and_nonreal(list(prog_var),
|
|
int, int, set(lval), set(lval), store_map, store_map).
|
|
:- mode store_alloc_handle_conflicts_and_nonreal(in, in, out, in, out, in, out)
|
|
is det.
|
|
|
|
store_alloc_handle_conflicts_and_nonreal([], N, N, SeenLvals, SeenLvals,
|
|
StoreMap, StoreMap).
|
|
store_alloc_handle_conflicts_and_nonreal([Var | Vars], N0, N,
|
|
SeenLvals0, SeenLvals, StoreMap0, StoreMap) :-
|
|
map__lookup(StoreMap0, Var, Lval),
|
|
(
|
|
( artificial_lval(Lval)
|
|
; set__member(Lval, SeenLvals0)
|
|
)
|
|
->
|
|
next_free_reg(N0, SeenLvals0, N1),
|
|
FinalLval = reg(r, N1),
|
|
map__det_update(StoreMap0, Var, FinalLval, StoreMap1)
|
|
;
|
|
N1 = N0,
|
|
FinalLval = Lval,
|
|
StoreMap1 = StoreMap0
|
|
),
|
|
set__insert(SeenLvals0, FinalLval, SeenLvals1),
|
|
store_alloc_handle_conflicts_and_nonreal(Vars, N1, N,
|
|
SeenLvals1, SeenLvals, StoreMap1, StoreMap).
|
|
|
|
:- pred store_alloc_allocate_extras(list(prog_var), int, set(lval),
|
|
stack_slot_info, store_map, store_map).
|
|
:- mode store_alloc_allocate_extras(in, in, in, in, in, out) is det.
|
|
|
|
store_alloc_allocate_extras([], _, _, _, StoreMap, StoreMap).
|
|
store_alloc_allocate_extras([Var | Vars], N0, SeenLvals0, StackSlotInfo,
|
|
StoreMap0, StoreMap) :-
|
|
(
|
|
map__contains(StoreMap0, Var)
|
|
->
|
|
% We have already allocated a slot for this variable.
|
|
N1 = N0,
|
|
StoreMap1 = StoreMap0,
|
|
SeenLvals1 = SeenLvals0
|
|
;
|
|
% We have not yet allocated a slot for this variable,
|
|
% which means it is not in the follow vars (if any).
|
|
StackSlotInfo = stack_slot_info(FollowVars, NumRealRRegs,
|
|
StackSlots),
|
|
(
|
|
map__search(StackSlots, Var, StackSlot),
|
|
\+ set__member(StackSlot, SeenLvals0),
|
|
(
|
|
FollowVars = yes
|
|
% If follow_vars was run, then the only
|
|
% reason why a var would not be in the
|
|
% follow_vars set is if it was supposed to
|
|
% be in its stack slot.
|
|
;
|
|
FollowVars = no,
|
|
% If follow_vars was not run, then we
|
|
% prefer to put the variable in a register,
|
|
% provided it is a real register.
|
|
next_free_reg(N0, SeenLvals0, TentativeReg),
|
|
TentativeReg =< NumRealRRegs
|
|
)
|
|
->
|
|
Locn = StackSlot,
|
|
N1 = N0
|
|
;
|
|
next_free_reg(N0, SeenLvals0, N1),
|
|
Locn = reg(r, N1)
|
|
),
|
|
map__det_insert(StoreMap0, Var, Locn, StoreMap1),
|
|
set__insert(SeenLvals0, Locn, SeenLvals1)
|
|
),
|
|
store_alloc_allocate_extras(Vars, N1, SeenLvals1, StackSlotInfo,
|
|
StoreMap1, StoreMap).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% The follow_vars pass maps some variables r(-1) as a hint to the
|
|
% code generator to put them in any free register. Since store maps
|
|
% require real locations, we can't use such hints directly.
|
|
|
|
% For robustness, we check for N < 1 instead of N = -1.
|
|
|
|
:- pred artificial_lval(lval).
|
|
:- mode artificial_lval(in) is semidet.
|
|
|
|
artificial_lval(reg(_Type, Num)) :-
|
|
Num < 1.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred next_free_reg(int, set(lval), int).
|
|
:- mode next_free_reg(in, in, out) is det.
|
|
|
|
next_free_reg(N0, Values, N) :-
|
|
( set__member(reg(r, N0), Values) ->
|
|
N1 is N0 + 1,
|
|
next_free_reg(N1, Values, N)
|
|
;
|
|
N = N0
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|