%---------------------------------------------------------------------------% % 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_info.m % % Main authors: conway, zs. % % This file defines the code_info type and various operations on it. % The code_info structure is the 'state' of the code generator. % % This file is organized into nine submodules: % % - the code_info structure and its access predicates % - simple wrappers around access predicates % - handling branched control structures % - handling failure continuations % - handling liveness issues % - saving and restoring heap pointers, trail tickets etc % - interfacing to var_locn % - managing the info required by garbage collection and value numbering % - managing stack slots % %---------------------------------------------------------------------------% :- module ll_backend__code_info. :- interface. :- import_module backend_libs__code_model. :- import_module backend_libs__proc_label. :- import_module hlds__hlds_data. :- import_module hlds__hlds_goal. :- import_module hlds__hlds_llds. :- import_module hlds__hlds_module. :- import_module hlds__hlds_pred. :- import_module hlds__instmap. :- import_module libs__globals. :- import_module libs__trace_params. :- import_module ll_backend__continuation_info. :- import_module ll_backend__global_data. :- import_module ll_backend__llds. :- import_module ll_backend__trace. :- import_module parse_tree__prog_data. :- import_module bool, set, list, map, std_util, assoc_list, counter, term. :- implementation. :- import_module check_hlds__mode_util. :- import_module check_hlds__type_util. :- import_module hlds__hlds_code_util. :- import_module libs__options. :- import_module libs__tree. :- import_module ll_backend__arg_info. :- import_module ll_backend__code_util. :- import_module ll_backend__exprn_aux. :- import_module ll_backend__llds_out. :- import_module ll_backend__var_locn. :- import_module parse_tree__prog_out. :- import_module varset. :- import_module set, stack. :- import_module string, require, char, bimap, int. %---------------------------------------------------------------------------% % Submodule for the code_info type and its access predicates. % % This submodule has the following components: % % declarations for exported access predicates % declarations for non-exported access predicates % the definition of the type and the init predicate % the definition of the get access predicates % the definition of the set access predicates % % Please keep the order of mention of the various fields % consistent in each of these five components. :- interface. :- type code_info. % Create a new code_info structure. Also return the % outermost resumption point, and info about the non-fixed % stack slots used for tracing purposes. :- pred code_info__init(bool::in, globals::in, pred_id::in, proc_id::in, pred_info::in, proc_info::in, follow_vars::in, module_info::in, static_cell_info::in, resume_point_info::out, trace_slot_info::out, code_info::out) is det. % Get the globals table. :- pred code_info__get_globals(code_info::in, globals::out) is det. % Get the HLDS of the entire module. :- pred code_info__get_module_info(code_info::in, module_info::out) is det. % Get the id of the predicate we are generating code for. :- pred code_info__get_pred_id(code_info::in, pred_id::out) is det. % Get the id of the procedure we are generating code for. :- pred code_info__get_proc_id(code_info::in, proc_id::out) is det. % Get the HLDS of the procedure we are generating code for. :- pred code_info__get_proc_info(code_info::in, proc_info::out) is det. % Get the HLDS of the predicate containing the procedure % we are generating code for. :- pred code_info__get_pred_info(code_info::in, pred_info::out) is det. % Get the variables for the current procedure. :- pred code_info__get_varset(code_info::in, prog_varset::out) is det. :- pred code_info__get_maybe_trace_info(code_info::in, maybe(trace_info)::out) is det. % Get the set of currently forward-live variables. :- pred code_info__get_forward_live_vars(code_info::in, set(prog_var)::out) is det. % Set the set of currently forward-live variables. :- pred code_info__set_forward_live_vars(set(prog_var)::in, code_info::in, code_info::out) is det. % Get the table mapping variables to the current % instantiation states. :- pred code_info__get_instmap(code_info::in, instmap::out) is det. % Set the table mapping variables to the current % instantiation states. :- pred code_info__set_instmap(instmap::in, code_info::in, code_info::out) is det. % The number of the last local label allocated. :- pred code_info__get_label_counter(code_info::in, counter::out) is det. % Get the flag that indicates whether succip is used or not. :- pred code_info__get_succip_used(code_info::in, bool::out) is det. % Get the label layout information created by tracing % during code generation. :- pred code_info__get_layout_info(code_info::in, map(label, internal_layout_info)::out) is det. % Get the global static data structures that have % been created during code generation for closure layouts. :- pred code_info__get_closure_layouts(code_info::in, list(comp_gen_c_data)::out) is det. :- pred code_info__get_max_reg_in_use_at_trace(code_info::in, int::out) is det. :- pred code_info__set_max_reg_in_use_at_trace(int::in, code_info::in, code_info::out) is det. % Get the flag which is true iff the procedure has so far % emitted code that creates a temporary nondet stack frame. :- pred code_info__get_created_temp_frame(code_info::in, bool::out) is det. :- pred code_info__get_static_cell_info(code_info::in, static_cell_info::out) is det. :- pred code_info__set_static_cell_info(static_cell_info::in, code_info::in, code_info::out) is det. %---------------------------------------------------------------------------% :- implementation. :- pred code_info__get_var_slot_count(code_info::in, int::out) is det. :- pred code_info__set_maybe_trace_info(maybe(trace_info)::in, code_info::in, code_info::out) is det. :- pred code_info__get_opt_no_return_calls(code_info::in, bool::out) is det. :- pred code_info__get_zombies(code_info::in, set(prog_var)::out) is det. :- pred code_info__set_zombies(set(prog_var)::in, code_info::in, code_info::out) is det. :- pred code_info__get_var_locn_info(code_info::in, var_locn_info::out) is det. :- pred code_info__set_var_locn_info(var_locn_info::in, code_info::in, code_info::out) is det. :- pred code_info__get_temps_in_use(code_info::in, set(lval)::out) is det. :- pred code_info__set_temps_in_use(set(lval)::in, code_info::in, code_info::out) is det. :- pred code_info__get_fail_info(code_info::in, fail_info::out) is det. :- pred code_info__set_fail_info(fail_info::in, code_info::in, code_info::out) is det. :- pred code_info__set_label_counter(counter::in, code_info::in, code_info::out) is det. :- pred code_info__set_succip_used(bool::in, code_info::in, code_info::out) is det. :- pred code_info__set_layout_info(map(label, internal_layout_info)::in, code_info::in, code_info::out) is det. :- pred code_info__get_max_temp_slot_count(code_info::in, int::out) is det. :- pred code_info__set_max_temp_slot_count(int::in, code_info::in, code_info::out) is det. :- pred code_info__get_temp_content_map(code_info::in, map(lval, slot_contents)::out) is det. :- pred code_info__set_temp_content_map(map(lval, slot_contents)::in, code_info::in, code_info::out) is det. :- pred code_info__set_closure_layouts(list(comp_gen_c_data)::in, code_info::in, code_info::out) is det. :- pred code_info__get_closure_seq_counter(code_info::in, counter::out) is det. :- pred code_info__set_closure_seq_counter(counter::in, code_info::in, code_info::out) is det. :- pred code_info__set_created_temp_frame(bool::in, code_info::in, code_info::out) is det. %---------------------------------------------------------------------------% % The code_info structure has three groups of fields. % % Some fields are static; they are set when the code_info structure % is initialized, and never changed afterwards. % % Some fields record information about the state of the code generator % at a particular location in the HLDS code of the current procedure. % At the start of the branched control structure, the code generator % remembers the values of these fields, and starts generating code % for each branch from the same location-dependent state. % % Some fields record persistent information that does not depend % on a code location. Updates to these fields must remain effective % even when the code generator resets its location-dependent state. :- type code_info ---> code_info( code_info_static :: code_info_static, code_info_loc_dep :: code_info_loc_dep, code_info_persistent :: code_info_persistent ). :- type code_info_static ---> code_info_static( globals :: globals, % For the code generation options. module_info :: module_info, % The module_info structure - you just % never know when you might need it. pred_id :: pred_id, % The id of the current predicate. proc_id :: proc_id, % The id of the current procedure. proc_info :: proc_info, % The proc_info for this procedure. pred_info :: pred_info, % The pred_info for the predicate containing % this procedure. varset :: prog_varset, % The variables in this procedure. var_slot_count :: int, % The number of stack slots allocated. % for storing variables. % (Some extra stack slots are used % for saving and restoring registers.) maybe_trace_info :: maybe(trace_info), % Information about which stack slots % the call sequence number and depth % are stored in, provided tracing is % switched on. opt_no_resume_calls :: bool ). :- type code_info_loc_dep ---> code_info_loc_dep( forward_live_vars :: set(prog_var), % Variables that are forward live % after this goal. instmap :: instmap, % Current insts of the live variables. zombies :: set(prog_var), % Zombie variables; variables that are not % forward live but which are protected by % an enclosing resume point. var_locn_info :: var_locn_info, % A map storing the information about % the status of each known variable. % (Known vars = forward live vars + zombies) temps_in_use :: set(lval), % The set of temporary locations currently in % use. These lvals must be all be keys in the % map of temporary locations ever used, which % is one of the persistent fields below. Any % keys in that map which are not in this set % are free for reuse. fail_info :: fail_info % Information about how to manage failures. ). :- type code_info_persistent ---> code_info_persistent( label_num_src :: counter, % Counter for the local labels used % by this procedure. store_succip :: bool, % do we need to store succip? label_info :: map(label, internal_layout_info), % Information on which values % are live and where at which labels, % for tracing and/or accurate gc. stackslot_max :: int, % The maximum number of extra % temporary stackslots that have been % used during the procedure. temp_contents :: map(lval, slot_contents), % The temporary locations that have ever been % used on the stack, and what they contain. % Once we have used a stack slot to store % e.g. a ticket, we never reuse that slot % to hold something else, e.g. a saved hp. % This policy prevents us from making such % conflicting choices in parallel branches, % which would make it impossible to describe % to gc what the slot contains after the end % of the branched control structure. closure_layout_seq :: counter, closure_layouts :: list(comp_gen_c_data), % Closure layout structures generated by this % procedure. max_reg_used :: int, % At each call to MR_trace, we compute the % highest rN register number that contains % a useful value. This slot contains the % maximum of these highest values. Therefore % at all calls to MR_trace in the procedure, % we need only save the registers whose numbers % are equal to or smaller than this field. % This slot contains -1 if tracing is not % enabled. created_temp_frame:: bool, % True iff the procedure has created one or % more temporary nondet frames. static_cell_info :: static_cell_info ). %---------------------------------------------------------------------------% code_info__init(SaveSuccip, Globals, PredId, ProcId, PredInfo, ProcInfo, FollowVars, ModuleInfo, StaticCellInfo, ResumePoint, TraceSlotInfo, CodeInfo) :- proc_info_get_initial_instmap(ProcInfo, ModuleInfo, InstMap), proc_info_liveness_info(ProcInfo, Liveness), proc_info_interface_code_model(ProcInfo, CodeModel), arg_info__build_input_arg_list(ProcInfo, ArgList), proc_info_varset(ProcInfo, VarSet), proc_info_stack_slots(ProcInfo, StackSlots), globals__get_options(Globals, Options), globals__get_trace_level(Globals, TraceLevel), ( eff_trace_level_is_none(PredInfo, ProcInfo, TraceLevel) = no -> trace__fail_vars(ModuleInfo, ProcInfo, FailVars), MaybeFailVars = yes(FailVars), set__union(Liveness, FailVars, EffLiveness) ; MaybeFailVars = no, EffLiveness = Liveness ), var_locn__init_state(ArgList, EffLiveness, VarSet, StackSlots, FollowVars, Options, VarLocnInfo), stack__init(ResumePoints), globals__lookup_bool_option(Globals, allow_hijacks, AllowHijack), ( AllowHijack = yes, Hijack = allowed ; AllowHijack = no, Hijack = not_allowed ), DummyFailInfo = fail_info(ResumePoints, resume_point_unknown, may_be_different, not_inside_non_condition, Hijack), map__init(TempContentMap), set__init(TempsInUse), set__init(Zombies), map__init(LayoutMap), code_info__max_var_slot(StackSlots, VarSlotMax), trace__reserved_slots(ModuleInfo, PredInfo, ProcInfo, Globals, FixedSlots, _), int__max(VarSlotMax, FixedSlots, SlotMax), globals__lookup_bool_option(Globals, opt_no_return_calls, OptNoReturnCalls), CodeInfo0 = code_info( code_info_static( Globals, ModuleInfo, PredId, ProcId, ProcInfo, PredInfo, VarSet, SlotMax, no, OptNoReturnCalls ), code_info_loc_dep( Liveness, InstMap, Zombies, VarLocnInfo, TempsInUse, DummyFailInfo % code_info__init_fail_info % will override this dummy value ), code_info_persistent( counter__init(1), SaveSuccip, LayoutMap, 0, TempContentMap, counter__init(1), [], -1, no, StaticCellInfo ) ), code_info__init_maybe_trace_info(TraceLevel, Globals, ModuleInfo, PredInfo, ProcInfo, TraceSlotInfo, CodeInfo0, CodeInfo1), code_info__init_fail_info(CodeModel, MaybeFailVars, ResumePoint, CodeInfo1, CodeInfo). :- pred code_info__init_maybe_trace_info(trace_level::in, globals::in, module_info::in, pred_info::in, proc_info::in, trace_slot_info::out, code_info::in, code_info::out) is det. code_info__init_maybe_trace_info(TraceLevel, Globals, ModuleInfo, PredInfo, ProcInfo, TraceSlotInfo, !CI) :- ( eff_trace_level_is_none(PredInfo, ProcInfo, TraceLevel) = no -> trace__setup(ModuleInfo, PredInfo, ProcInfo, Globals, TraceSlotInfo, TraceInfo, !CI), code_info__set_maybe_trace_info(yes(TraceInfo), !CI) ; TraceSlotInfo = trace_slot_info(no, no, no, no, no) ). %---------------------------------------------------------------------------% code_info__get_globals(CI, CI ^ code_info_static ^ globals). code_info__get_module_info(CI, CI ^ code_info_static ^ module_info). code_info__get_pred_id(CI, CI ^ code_info_static ^ pred_id). code_info__get_proc_id(CI, CI ^ code_info_static ^ proc_id). code_info__get_proc_info(CI, CI ^ code_info_static ^ proc_info). code_info__get_pred_info(CI, CI ^ code_info_static ^ pred_info). code_info__get_varset(CI, CI ^ code_info_static ^ varset). code_info__get_var_slot_count(CI, CI ^ code_info_static ^ var_slot_count). code_info__get_maybe_trace_info(CI, CI ^ code_info_static ^ maybe_trace_info). code_info__get_opt_no_return_calls(CI, CI ^ code_info_static ^ opt_no_resume_calls). code_info__get_forward_live_vars(CI, CI ^ code_info_loc_dep ^ forward_live_vars). code_info__get_instmap(CI, CI ^ code_info_loc_dep ^ instmap). code_info__get_zombies(CI, CI ^ code_info_loc_dep ^ zombies). code_info__get_var_locn_info(CI, CI ^ code_info_loc_dep ^ var_locn_info). code_info__get_temps_in_use(CI, CI ^ code_info_loc_dep ^ temps_in_use). code_info__get_fail_info(CI, CI ^ code_info_loc_dep ^ fail_info). code_info__get_label_counter(CI, CI ^ code_info_persistent ^ label_num_src). code_info__get_succip_used(CI, CI ^ code_info_persistent ^ store_succip). code_info__get_layout_info(CI, CI ^ code_info_persistent ^ label_info). code_info__get_max_temp_slot_count(CI, CI ^ code_info_persistent ^ stackslot_max). code_info__get_temp_content_map(CI, CI ^ code_info_persistent ^ temp_contents). code_info__get_closure_seq_counter(CI, CI ^ code_info_persistent ^ closure_layout_seq). code_info__get_closure_layouts(CI, CI ^ code_info_persistent ^ closure_layouts). code_info__get_max_reg_in_use_at_trace(CI, CI ^ code_info_persistent ^ max_reg_used). code_info__get_created_temp_frame(CI, CI ^ code_info_persistent ^ created_temp_frame). code_info__get_static_cell_info(CI, CI ^ code_info_persistent ^ static_cell_info). %---------------------------------------------------------------------------% code_info__set_maybe_trace_info(TI, CI, CI ^ code_info_static ^ maybe_trace_info := TI). code_info__set_forward_live_vars(LV, CI, CI ^ code_info_loc_dep ^ forward_live_vars := LV). code_info__set_instmap(IM, CI, CI ^ code_info_loc_dep ^ instmap := IM). code_info__set_zombies(Zs, CI, CI ^ code_info_loc_dep ^ zombies := Zs). code_info__set_var_locn_info(EI, CI, CI ^ code_info_loc_dep ^ var_locn_info := EI). code_info__set_temps_in_use(TI, CI, CI ^ code_info_loc_dep ^ temps_in_use := TI). code_info__set_fail_info(FI, CI, CI ^ code_info_loc_dep ^ fail_info := FI). code_info__set_label_counter(LC, CI, CI ^ code_info_persistent ^ label_num_src := LC). code_info__set_succip_used(SU, CI, CI ^ code_info_persistent ^ store_succip := SU). code_info__set_layout_info(LI, CI, CI ^ code_info_persistent ^ label_info := LI). code_info__set_max_temp_slot_count(TM, CI, CI ^ code_info_persistent ^ stackslot_max := TM). code_info__set_temp_content_map(CM, CI, CI ^ code_info_persistent ^ temp_contents := CM). code_info__set_closure_seq_counter(CLS, CI, CI ^ code_info_persistent ^ closure_layout_seq := CLS). code_info__set_closure_layouts(CG, CI, CI ^ code_info_persistent ^ closure_layouts := CG). code_info__set_max_reg_in_use_at_trace(MR, CI, CI ^ code_info_persistent ^ max_reg_used := MR). code_info__set_created_temp_frame(MR, CI, CI ^ code_info_persistent ^ created_temp_frame := MR). code_info__set_static_cell_info(SCI, CI, CI ^ code_info_persistent ^ static_cell_info := SCI). %---------------------------------------------------------------------------% %---------------------------------------------------------------------------% % Submodule for simple wrappers around access predicates. :- interface. % Get the hlds mapping from variables to stack slots :- pred code_info__get_stack_slots(code_info::in, stack_slots::out) is det. % Get the table that contains advice about where % variables should be put. :- pred code_info__get_follow_var_map(code_info::in, follow_vars_map::out) is det. % Get the integer that gives the number of the next % non-reserved register. :- pred code_info__get_next_non_reserved(code_info::in, int::out) is det. % Set the table that contains advice about where % variables should be put. :- pred code_info__set_follow_vars(follow_vars::in, code_info::in, code_info::out) is det. % code_info__pre_goal_update(GoalInfo, Atomic, OldCodeInfo, NewCodeInfo) % updates OldCodeInfo to produce NewCodeInfo with the changes % specified by GoalInfo. :- pred code_info__pre_goal_update(hlds_goal_info::in, bool::in, code_info::in, code_info::out) is det. % code_info__post_goal_update(GoalInfo, OldCodeInfo, NewCodeInfo) % updates OldCodeInfo to produce NewCodeInfo with the changes described % by GoalInfo. :- pred code_info__post_goal_update(hlds_goal_info::in, code_info::in, code_info::out) is det. % Find out whether the body of the current procedure should use % typeinfo liveness. :- func code_info__body_typeinfo_liveness(code_info) = bool. % Find out the type of the given variable. :- func code_info__variable_type(code_info, prog_var) = (type). :- func code_info__lookup_type_defn(code_info, (type)) = hlds_type_defn. % Given a constructor id, and a variable (so that we can work out the % type of the constructor), determine correct tag (representation) % of that constructor. :- func code_info__cons_id_to_tag(code_info, prog_var, cons_id) = cons_tag. % Get the code model of the current procedure. :- func code_info__get_proc_model(code_info) = code_model. % Get the list of the head variables of the current procedure. :- func code_info__get_headvars(code_info) = list(prog_var). % Get the call argument information for the current procedure :- func code_info__get_arginfo(code_info) = list(arg_info). % Get the call argument info for a given mode of a given predicate :- func code_info__get_pred_proc_arginfo(code_info, pred_id, proc_id) = list(arg_info). % Get the set of variables currently needed by the resume % points of enclosing goals. :- func code_info__current_resume_point_vars(code_info) = set(prog_var). :- func code_info__variable_to_string(code_info, prog_var) = string. % Create a code address which holds the address of the specified % procedure. % The fourth 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. % % If the procs_per_c_function option tells us to put more than one % procedure into each C function, but not all procedures in the module % are in one function, then we would like to be able to use the % fast form of reference to a procedure for references not only from % within the same procedure but also from other procedures within % the same C function. However, at the time of code generation, % we do not yet know which procedures will be put into the same % C functions, and so we cannot do this. :- func code_info__make_entry_label(code_info, module_info, pred_id, proc_id, bool) = code_addr. % Generate the next local label in sequence. :- pred code_info__get_next_label(label::out, code_info::in, code_info::out) is det. % Note that the succip slot is used, and thus cannot be % optimized away. :- pred code_info__succip_is_used(code_info::in, code_info::out) is det. :- pred code_info__add_trace_layout_for_label(label::in, term__context::in, trace_port::in, bool::in, goal_path::in, layout_label_info::in, code_info::in, code_info::out) is det. :- pred code_info__get_cur_proc_label(code_info::in, proc_label::out) is det. :- pred code_info__get_next_closure_seq_no(int::out, code_info::in, code_info::out) is det. :- pred code_info__add_closure_layout(comp_gen_c_data::in, code_info::in, code_info::out) is det. :- pred code_info__add_static_cell(assoc_list(rval, llds_type)::in, data_addr::out, code_info::in, code_info::out) is det. :- pred code_info__add_static_cell_natural_types(list(rval)::in, data_addr::out, code_info::in, code_info::out) is det. %---------------------------------------------------------------------------% :- implementation. :- pred code_info__add_resume_layout_for_label(label::in, layout_label_info::in, code_info::in, code_info::out) is det. code_info__get_stack_slots(CI, StackSlots) :- code_info__get_var_locn_info(CI, VarLocnInfo), var_locn__get_stack_slots(VarLocnInfo, StackSlots). code_info__get_follow_var_map(CI, FollowVarMap) :- code_info__get_var_locn_info(CI, VarLocnInfo), var_locn__get_follow_var_map(VarLocnInfo, FollowVarMap). code_info__get_next_non_reserved(CI, NextNonReserved) :- code_info__get_var_locn_info(CI, VarLocnInfo), var_locn__get_next_non_reserved(VarLocnInfo, NextNonReserved). code_info__set_follow_vars(FollowVars, !CI) :- code_info__get_var_locn_info(!.CI, VarLocnInfo0), var_locn__set_follow_vars(FollowVars, VarLocnInfo0, VarLocnInfo), code_info__set_var_locn_info(VarLocnInfo, !CI). %-----------------------------------------------------------------------------% % Update the code info structure to be consistent % immediately prior to generating a goal. code_info__pre_goal_update(GoalInfo, Atomic, !CI) :- % The liveness pass puts resume_point annotations on some kinds % of goals. The parts of the code generator that handle those kinds % of goals should handle the resume point annotation as well; % when they do, they remove the annotation. The following code % is a sanity check to make sure that this has in fact been done. goal_info_get_resume_point(GoalInfo, ResumePoint), ( ResumePoint = no_resume_point ; ResumePoint = resume_point(_, _), error("pre_goal_update with resume point") ), goal_info_get_follow_vars(GoalInfo, MaybeFollowVars), ( MaybeFollowVars = yes(FollowVars), code_info__set_follow_vars(FollowVars, !CI) ; MaybeFollowVars = no ), % note: we must be careful to apply deaths before births goal_info_get_pre_deaths(GoalInfo, PreDeaths), code_info__rem_forward_live_vars(PreDeaths, !CI), code_info__maybe_make_vars_forward_dead(PreDeaths, no, !CI), goal_info_get_pre_births(GoalInfo, PreBirths), code_info__add_forward_live_vars(PreBirths, !CI), ( Atomic = yes -> goal_info_get_post_deaths(GoalInfo, PostDeaths), code_info__rem_forward_live_vars(PostDeaths, !CI) ; true ). % Update the code info structure to be consistent % immediately after generating a goal. code_info__post_goal_update(GoalInfo, !CI) :- % note: we must be careful to apply deaths before births goal_info_get_post_deaths(GoalInfo, PostDeaths), code_info__rem_forward_live_vars(PostDeaths, !CI), code_info__maybe_make_vars_forward_dead(PostDeaths, no, !CI), goal_info_get_post_births(GoalInfo, PostBirths), code_info__add_forward_live_vars(PostBirths, !CI), code_info__make_vars_forward_live(PostBirths, !CI), goal_info_get_instmap_delta(GoalInfo, InstMapDelta), code_info__get_instmap(!.CI, InstMap0), instmap__apply_instmap_delta(InstMap0, InstMapDelta, InstMap), code_info__set_instmap(InstMap, !CI). %---------------------------------------------------------------------------% code_info__body_typeinfo_liveness(CI) = TypeInfoLiveness :- code_info__get_module_info(CI, ModuleInfo), code_info__get_pred_id(CI, PredId), module_info_pred_info(ModuleInfo, PredId, PredInfo), code_info__get_globals(CI, Globals), body_should_use_typeinfo_liveness(PredInfo, Globals, TypeInfoLiveness). :- func code_info__get_var_types(code_info) = map(prog_var, type). code_info__get_var_types(CI) = VarTypes :- code_info__get_proc_info(CI, ProcInfo), proc_info_vartypes(ProcInfo, VarTypes). code_info__variable_type(CI, Var) = Type :- map__lookup(code_info__get_var_types(CI), Var, Type). code_info__lookup_type_defn(CI, Type) = TypeDefn :- code_info__get_module_info(CI, ModuleInfo), ( type_to_ctor_and_args(Type, TypeCtorPrime, _) -> TypeCtor = TypeCtorPrime ; error("unknown type in code_info__lookup_type_defn") ), module_info_types(ModuleInfo, TypeTable), map__lookup(TypeTable, TypeCtor, TypeDefn). code_info__cons_id_to_tag(CI, Var, ConsId) = ConsTag :- code_info__get_module_info(CI, ModuleInfo), ConsTag = cons_id_to_tag(ConsId, code_info__variable_type(CI, Var), ModuleInfo). %---------------------------------------------------------------------------% code_info__get_proc_model(CI) = CodeModel :- code_info__get_proc_info(CI, ProcInfo), proc_info_interface_code_model(ProcInfo, CodeModel). code_info__get_headvars(CI) = HeadVars :- code_info__get_module_info(CI, ModuleInfo), code_info__get_pred_id(CI, PredId), code_info__get_proc_id(CI, ProcId), module_info_pred_proc_info(ModuleInfo, PredId, ProcId, _, ProcInfo), proc_info_headvars(ProcInfo, HeadVars). code_info__get_arginfo(CI) = ArgInfo :- code_info__get_pred_id(CI, PredId), code_info__get_proc_id(CI, ProcId), ArgInfo = code_info__get_pred_proc_arginfo(CI, PredId, ProcId). code_info__get_pred_proc_arginfo(CI, PredId, ProcId) = ArgInfo :- code_info__get_module_info(CI, ModuleInfo), module_info_pred_proc_info(ModuleInfo, PredId, ProcId, _, ProcInfo), proc_info_arg_info(ProcInfo, ArgInfo). code_info__current_resume_point_vars(CI) = ResumeVars :- code_info__get_fail_info(CI, FailInfo), FailInfo = fail_info(ResumePointStack, _, _, _, _), stack__top_det(ResumePointStack, ResumePointInfo), code_info__pick_first_resume_point(ResumePointInfo, ResumeMap, _), map__keys(ResumeMap, ResumeMapVarList), set__list_to_set(ResumeMapVarList, ResumeVars). code_info__variable_to_string(CI, Var) = Name :- code_info__get_varset(CI, Varset), varset__lookup_name(Varset, Var, Name). %---------------------------------------------------------------------------% code_info__make_entry_label(CI, ModuleInfo, PredId, ProcId, Immed0) = PredAddress :- ( Immed0 = no, Immed = no ; Immed0 = yes, code_info__get_globals(CI, Globals), globals__lookup_int_option(Globals, procs_per_c_function, ProcsPerFunc), code_info__get_pred_id(CI, CurPredId), code_info__get_proc_id(CI, CurProcId), Immed = yes(ProcsPerFunc - proc(CurPredId, CurProcId)) ), code_util__make_entry_label(ModuleInfo, PredId, ProcId, Immed, PredAddress). code_info__get_next_label(Label, !CI) :- code_info__get_module_info(!.CI, ModuleInfo), code_info__get_pred_id(!.CI, PredId), code_info__get_proc_id(!.CI, ProcId), code_info__get_label_counter(!.CI, C0), counter__allocate(N, C0, C), code_info__set_label_counter(C, !CI), code_util__make_internal_label(ModuleInfo, PredId, ProcId, N, Label). code_info__succip_is_used(!CI) :- code_info__set_succip_used(yes, !CI). code_info__add_trace_layout_for_label(Label, Context, Port, IsHidden, Path, Layout, !CI) :- code_info__get_layout_info(!.CI, Internals0), Exec = yes(trace_port_layout_info(Context, Port, IsHidden, Path, Layout)), ( map__search(Internals0, Label, Internal0) -> Internal0 = internal_layout_info(Exec0, Resume, Return), ( Exec0 = no -> true ; error("adding trace layout for already known label") ), Internal = internal_layout_info(Exec, Resume, Return), map__set(Internals0, Label, Internal, Internals) ; Internal = internal_layout_info(Exec, no, no), map__det_insert(Internals0, Label, Internal, Internals) ), code_info__set_layout_info(Internals, !CI). code_info__add_resume_layout_for_label(Label, LayoutInfo, !CI) :- code_info__get_layout_info(!.CI, Internals0), Resume = yes(LayoutInfo), ( map__search(Internals0, Label, Internal0) -> Internal0 = internal_layout_info(Exec, Resume0, Return), ( Resume0 = no -> true ; error("adding gc layout for already known label") ), Internal = internal_layout_info(Exec, Resume, Return), map__set(Internals0, Label, Internal, Internals) ; Internal = internal_layout_info(no, Resume, no), map__det_insert(Internals0, Label, Internal, Internals) ), code_info__set_layout_info(Internals, !CI). :- pred code_info__get_active_temps_data(code_info::in, assoc_list(lval, slot_contents)::out) is det. code_info__get_active_temps_data(CI, Temps) :- code_info__get_temps_in_use(CI, TempsInUse), code_info__get_temp_content_map(CI, TempContentMap), map__select(TempContentMap, TempsInUse, TempsInUseContentMap), map__to_assoc_list(TempsInUseContentMap, Temps). code_info__get_cur_proc_label(CI, ProcLabel) :- code_info__get_module_info(CI, ModuleInfo), code_info__get_pred_id(CI, PredId), code_info__get_proc_id(CI, ProcId), ProcLabel = make_proc_label(ModuleInfo, PredId, ProcId). code_info__get_next_closure_seq_no(SeqNo, !CI) :- code_info__get_closure_seq_counter(!.CI, C0), counter__allocate(SeqNo, C0, C), code_info__set_closure_seq_counter(C, !CI). code_info__add_closure_layout(ClosureLayout, !CI) :- code_info__get_closure_layouts(!.CI, ClosureLayouts), code_info__set_closure_layouts([ClosureLayout | ClosureLayouts], !CI). code_info__add_static_cell(RvalsTypes, DataAddr, !CI) :- code_info__get_static_cell_info(!.CI, StaticCellInfo0), add_static_cell(RvalsTypes, DataAddr, StaticCellInfo0, StaticCellInfo), code_info__set_static_cell_info(StaticCellInfo, !CI). code_info__add_static_cell_natural_types(Rvals, DataAddr, !CI) :- code_info__get_static_cell_info(!.CI, StaticCellInfo0), add_static_cell_natural_types(Rvals, DataAddr, StaticCellInfo0, StaticCellInfo), code_info__set_static_cell_info(StaticCellInfo, !CI). %---------------------------------------------------------------------------% %---------------------------------------------------------------------------% % Submodule for handling branched control structures. :- interface. :- type position_info. :- type branch_end_info. :- type branch_end == maybe(branch_end_info). :- pred code_info__remember_position(code_info::in, position_info::out) is det. :- pred code_info__reset_to_position(position_info::in, code_info::in, code_info::out) is det. :- pred code_info__reset_resume_known(position_info::in, code_info::in, code_info::out) is det. :- pred code_info__generate_branch_end(store_map::in, branch_end::in, branch_end::out, code_tree::out, code_info::in, code_info::out) is det. :- pred code_info__after_all_branches(store_map::in, branch_end::in, code_info::in, code_info::out) is det. :- pred code_info__save_hp_in_branch(code_tree::out, lval::out, position_info::in, position_info::out) is det. :- implementation. :- type position_info ---> position_info( code_info % The code_info at a given position % in the code of the procedure. ). :- type branch_end_info ---> branch_end_info( code_info % The code_info at the end of a branch. ). code_info__remember_position(CI, position_info(CI)). code_info__reset_to_position(position_info(PosCI), CurCI, NextCI) :- PosCI = code_info(_, LocDep, _), CurCI = code_info(Static, _, Persistent), NextCI = code_info(Static, LocDep, Persistent). code_info__reset_resume_known(BranchStart, !CI) :- BranchStart = position_info(BranchStartCI), code_info__get_fail_info(BranchStartCI, BranchStartFailInfo), BranchStartFailInfo = fail_info(_, BSResumeKnown, _, _, _), code_info__get_fail_info(!.CI, CurFailInfo), CurFailInfo = fail_info(CurFailStack, _, CurCurfMaxfr, CurCondEnv, CurHijack), NewFailInfo = fail_info(CurFailStack, BSResumeKnown, CurCurfMaxfr, CurCondEnv, CurHijack), code_info__set_fail_info(NewFailInfo, !CI). code_info__generate_branch_end(StoreMap, MaybeEnd0, MaybeEnd, Code, !CI) :- % The code generator generates better code % if it knows in advance where each variable should % go. We don't need to reset the follow_vars % afterwards, since every goal following a branched % control structure must in any case be annotated with % its own follow_var set. map__to_assoc_list(StoreMap, VarLocs), map__from_assoc_list(VarLocs, FollowVarsMap), assoc_list__values(VarLocs, Locs), code_util__max_mentioned_reg(Locs, MaxMentionedReg), code_info__set_follow_vars( follow_vars(FollowVarsMap, MaxMentionedReg + 1), !CI), code_info__get_instmap(!.CI, InstMap), ( instmap__is_reachable(InstMap) -> code_info__place_vars(VarLocs, Code, !CI) ; % With --opt-no-return-call, the variables that we would have % saved across a call that cannot return have had the last % of their code generation state destroyed, so calling % place_vars would cause a code generator abort. However, % pretending that all the variables are where the store map % says they should be is perfectly fine, since we can never % reach the end of *this* branch anyway. code_info__remake_with_store_map(StoreMap, !CI), Code = empty ), EndCodeInfo1 = !.CI, ( MaybeEnd0 = no, EndCodeInfo = EndCodeInfo1 ; MaybeEnd0 = yes(branch_end_info(EndCodeInfo0)), % Make sure the left context we leave the % branched structure with is valid for all branches. code_info__get_fail_info(EndCodeInfo0, FailInfo0), code_info__get_fail_info(EndCodeInfo1, FailInfo1), FailInfo0 = fail_info(_, ResumeKnown0, CurfrMaxfr0, CondEnv0, Hijack0), FailInfo1 = fail_info(R, ResumeKnown1, CurfrMaxfr1, CondEnv1, Hijack1), ( ResumeKnown0 = resume_point_known(Redoip0), ResumeKnown1 = resume_point_known(Redoip1) -> ResumeKnown = resume_point_known(Redoip0), require(unify(Redoip0, Redoip1), "redoip mismatch in generate_branch_end") ; ResumeKnown = resume_point_unknown ), ( CurfrMaxfr0 = must_be_equal, CurfrMaxfr1 = must_be_equal -> CurfrMaxfr = must_be_equal ; CurfrMaxfr = may_be_different ), ( Hijack0 = allowed, Hijack1 = allowed -> Hijack = allowed ; Hijack = not_allowed ), require(unify(CondEnv0, CondEnv1), "some but not all branches inside a non condition"), FailInfo = fail_info(R, ResumeKnown, CurfrMaxfr, CondEnv0, Hijack), code_info__set_fail_info(FailInfo, EndCodeInfo1, EndCodeInfoA), % Make sure the "temps in use" set at the end of the % branched control structure includes every slot % in use at the end of any branch. code_info__get_temps_in_use(EndCodeInfo0, TempsInUse0), code_info__get_temps_in_use(EndCodeInfo1, TempsInUse1), set__union(TempsInUse0, TempsInUse1, TempsInUse), code_info__set_temps_in_use(TempsInUse, EndCodeInfoA, EndCodeInfo) ), MaybeEnd = yes(branch_end_info(EndCodeInfo)). code_info__after_all_branches(StoreMap, MaybeEnd, !CI) :- ( MaybeEnd = yes(BranchEnd), BranchEnd = branch_end_info(BranchEndCodeInfo), code_info__reset_to_position(position_info(BranchEndCodeInfo), !CI), code_info__remake_with_store_map(StoreMap, !CI) ; MaybeEnd = no, error("no branches in branched control structure") ). % code_info__remake_with_store_map throws away the var_info data % structure, forgetting the current locations of all variables, % and rebuilds it from scratch based on the given store map. % The new var_info will know about only the variables present % in the store map, and will believe they are where the store map % says they are. :- pred code_info__remake_with_store_map(store_map::in, code_info::in, code_info::out) is det. code_info__remake_with_store_map(StoreMap, !CI) :- map__to_assoc_list(StoreMap, VarLvals), code_info__get_var_locn_info(!.CI, VarLocnInfo0), var_locn__reinit_state(VarLvals, VarLocnInfo0, VarLocnInfo), code_info__set_var_locn_info(VarLocnInfo, !CI). code_info__save_hp_in_branch(Code, Slot, Pos0, Pos) :- Pos0 = position_info(CodeInfo0), code_info__save_hp(Code, Slot, CodeInfo0, CodeInfo), Pos = position_info(CodeInfo). %---------------------------------------------------------------------------% %---------------------------------------------------------------------------% % Submodule for the handling of failure continuations. % The principles underlying this submodule of code_info.m are % documented in the file compiler/notes/failure.html, which also % defines terms such as "quarter hijack"). Some parts of the submodule % also require knowledge of compiler/notes/allocation.html. :- interface. :- type resume_map. :- type resume_point_info. % `prepare_for_disj_hijack' should be called before entering % a disjunction. It saves the values of any nondet stack slots % the disjunction may hijack, and if necessary, sets the redofr % slot of the top frame to point to this frame. The code at the % start of the individual disjuncts will override the redoip slot. % % `undo_disj_hijack' should be called before entering the last % disjunct of a disjunction. It undoes the effects of % `prepare_for_disj_hijack'. :- type disj_hijack_info. :- pred code_info__prepare_for_disj_hijack(code_model::in, disj_hijack_info::out, code_tree::out, code_info::in, code_info::out) is det. :- pred code_info__undo_disj_hijack(disj_hijack_info::in, code_tree::out, code_info::in, code_info::out) is det. % `prepare_for_ite_hijack' should be called before entering % an if-then-else. It saves the values of any nondet stack slots % the if-then-else may hijack, and if necessary, sets the redofr % slot of the top frame to point to this frame. Our caller % will then override the redoip slot to point to the start of % the else part before generating the code of the condition. % % `ite_enter_then', which should be called generating code for % the condition, sets up the failure state of the code generator % for generating the then-part, and returns the code sequences % to be used at the starts of the then-part and the else-part % to undo the effects of any hijacking. :- type ite_hijack_info. :- pred code_info__prepare_for_ite_hijack(code_model::in, ite_hijack_info::out, code_tree::out, code_info::in, code_info::out) is det. :- pred code_info__ite_enter_then(ite_hijack_info::in, code_tree::out, code_tree::out, code_info::in, code_info::out) is det. % `enter_simple_neg' and `leave_simple_neg' should be called before % and after generating the code for a negated unification, in % situations where failure is a direct branch. We handle this case % specially, because it occurs frequently and should not require % a flushing of the expression cache, whereas the general way of % handling negations does require a flush. These two predicates % handle all aspects of the negation except for the unification % itself. :- type simple_neg_info. :- pred code_info__enter_simple_neg(set(prog_var)::in, hlds_goal_info::in, simple_neg_info::out, code_info::in, code_info::out) is det. :- pred code_info__leave_simple_neg(hlds_goal_info::in, simple_neg_info::in, code_info::in, code_info::out) is det. % `prepare_for_det_commit' and `generate_det_commit' should be % called before and after generating the code for the multi goal % being cut across. If the goal succeeds, the commit will cut % any choice points generated in the goal. :- type det_commit_info. :- pred code_info__prepare_for_det_commit(det_commit_info::out, code_tree::out, code_info::in, code_info::out) is det. :- pred code_info__generate_det_commit(det_commit_info::in, code_tree::out, code_info::in, code_info::out) is det. % `prepare_for_semi_commit' and `generate_semi_commit' should be % called before and after generating the code for the nondet goal % being cut across. If the goal succeeds, the commit will cut % any choice points generated in the goal. :- type semi_commit_info. :- pred code_info__prepare_for_semi_commit(semi_commit_info::out, code_tree::out, code_info::in, code_info::out) is det. :- pred code_info__generate_semi_commit(semi_commit_info::in, code_tree::out, code_info::in, code_info::out) is det. % Put the given resume point into effect, by pushing it on to % the resume point stack, and if necessary generating code to % override the redoip of the top nondet stack frame. :- pred code_info__effect_resume_point(resume_point_info::in, code_model::in, code_tree::out, code_info::in, code_info::out) is det. :- pred code_info__pop_resume_point(code_info::in, code_info::out) is det. % Return the details of the resume point currently on top of the % failure continuation stack. :- pred code_info__top_resume_point(code_info::in, resume_point_info::out) is det. % Call this predicate to say "we have just left a disjunction; % we don't know what address the following code will need to % backtrack to". :- pred code_info__set_resume_point_to_unknown(code_info::in, code_info::out) is det. % Call this predicate to say "we have just returned from a model_non % call; we don't know what address the following code will need to % backtrack to, and there may now be nondet frames on top of ours % that do not have their redofr slots pointing to our frame". :- pred code_info__set_resume_point_and_frame_to_unknown(code_info::in, code_info::out) is det. % Generate code for executing a failure that is appropriate for the % current failure environment. :- pred code_info__generate_failure(code_tree::out, code_info::in, code_info::out) is det. % Generate code that checks if the given rval is false, and if yes, % executes a failure that is appropriate for the current failure % environment. :- pred code_info__fail_if_rval_is_false(rval::in, code_tree::out, code_info::in, code_info::out) is det. % Checks whether the appropriate code for failure in the current % failure environment is a direct branch. :- pred code_info__failure_is_direct_branch(code_info::in, code_addr::out) is semidet. % Checks under what circumstances the current failure environment % would allow a model_non call at this point to be turned into a % tail call, provided of course that the return from the call is % followed immediately by succeed(). :- pred code_info__may_use_nondet_tailcall(code_info::in, nondet_tail_call::out) is det. % Materialize the given variables into registers or stack slots. :- pred code_info__produce_vars(set(prog_var)::in, resume_map::out, code_tree::out, code_info::in, code_info::out) is det. % Put the variables needed in enclosing failure continuations % into their stack slots. :- pred code_info__flush_resume_vars_to_stack(code_tree::out, code_info::in, code_info::out) is det. % Set up the resume_point_info structure. :- pred code_info__make_resume_point(set(prog_var)::in, resume_locs::in, resume_map::in, resume_point_info::out, code_info::in, code_info::out) is det. % Generate the code for a resume point. :- pred code_info__generate_resume_point(resume_point_info::in, code_tree::out, code_info::in, code_info::out) is det. % List the variables that need to be preserved for the given % resume point. :- pred code_info__resume_point_vars(resume_point_info::in, list(prog_var)::out) is det. % See whether the given resume point includes a code address % that presumes all the resume point variables to be in their % stack slots. If yes, return that code address; otherwise, % abort the compiler. :- pred code_info__resume_point_stack_addr(resume_point_info::in, code_addr::out) is det. %---------------------------------------------------------------------------% :- implementation. % The part of the code generator state that says how to handle % failures; also called the failure continuation stack. :- type fail_info ---> fail_info( stack(resume_point_info), resume_point_known, curfr_vs_maxfr, condition_env, hijack_allowed ). % A resumption point has one or two labels associated with it. % Backtracking can arrive at either label. The code following % each label will assume that the variables needed at the resumption % point are in the locations given by the resume_map associated with % the given label and nowhere else. Any code that can cause % backtracking to a label must make sure that those variables are % in the positions expected by the label. % % The only time when a code_addr in a resume_point info is not a label % is when the code_addr is do_fail, which indicates that the resumption % point is not in (this invocation of) this procedure. :- type resume_point_info ---> orig_only(resume_map, code_addr) ; stack_only(resume_map, code_addr) ; orig_and_stack(resume_map, code_addr, resume_map, code_addr) ; stack_and_orig(resume_map, code_addr, resume_map, code_addr). % A resume map maps the variables that will be needed at a resumption % point to the locations in which they will be. :- type resume_map == map(prog_var, set(lval)). :- type redoip_update ---> has_been_done ; wont_be_done. :- type resume_point_known ---> resume_point_known(redoip_update) ; resume_point_unknown. :- type curfr_vs_maxfr ---> must_be_equal ; may_be_different. :- type condition_env ---> inside_non_condition ; not_inside_non_condition. :- type hijack_allowed ---> allowed ; not_allowed. %---------------------------------------------------------------------------% :- type disj_hijack_info ---> disj_no_hijack ; disj_temp_frame ; disj_quarter_hijack ; disj_half_hijack( lval % The stack slot in which we saved % the value of the hijacked redoip. ) ; disj_full_hijack( lval, % The stack slot in which we saved % the value of the hijacked redoip. lval % The stack slot in which we saved % the value of the hijacked redofr. ). code_info__prepare_for_disj_hijack(CodeModel, HijackInfo, Code, !CI) :- code_info__get_fail_info(!.CI, FailInfo), FailInfo = fail_info(_, ResumeKnown, CurfrMaxfr, CondEnv, Allow), ( CodeModel \= model_non -> HijackInfo = disj_no_hijack, Code = node([ comment("disj no hijack") - "" ]) ; ( Allow = not_allowed ; CondEnv = inside_non_condition ) -> HijackInfo = disj_temp_frame, code_info__create_temp_frame(do_fail, "prepare for disjunction", Code, !CI) ; CurfrMaxfr = must_be_equal, ResumeKnown = resume_point_known(has_been_done) -> HijackInfo = disj_quarter_hijack, Code = node([ comment("disj quarter hijack") - "" ]) ; CurfrMaxfr = must_be_equal -> % Here ResumeKnown must be resume_point_unknown % or resume_point_known(wont_be_done). code_info__acquire_temp_slot(lval(redoip(lval(curfr))), RedoipSlot, !CI), HijackInfo = disj_half_hijack(RedoipSlot), Code = node([ assign(RedoipSlot, lval(redoip(lval(curfr)))) - "prepare for half disj hijack" ]) ; % Here CurfrMaxfr must be may_be_different. code_info__acquire_temp_slot(lval(redoip(lval(maxfr))), RedoipSlot, !CI), code_info__acquire_temp_slot(lval(redofr(lval(maxfr))), RedofrSlot, !CI), HijackInfo = disj_full_hijack(RedoipSlot, RedofrSlot), Code = node([ assign(RedoipSlot, lval(redoip(lval(maxfr)))) - "prepare for full disj hijack", assign(RedofrSlot, lval(redofr(lval(maxfr)))) - "prepare for full disj hijack", assign(redofr(lval(maxfr)), lval(curfr)) - "prepare for full disj hijack" ]) ). code_info__undo_disj_hijack(HijackInfo, Code, !CI) :- code_info__get_fail_info(!.CI, FailInfo0), FailInfo0 = fail_info(ResumePoints, ResumeKnown, CurfrMaxfr, CondEnv, Allow), ( HijackInfo = disj_no_hijack, Code = empty ; HijackInfo = disj_temp_frame, Code = node([ assign(maxfr, lval(prevfr(lval(maxfr)))) - "restore maxfr for temp frame disj" ]) ; HijackInfo = disj_quarter_hijack, require(unify(CurfrMaxfr, must_be_equal), "maxfr may differ from curfr in disj_quarter_hijack"), stack__top_det(ResumePoints, ResumePoint), code_info__pick_stack_resume_point(ResumePoint, _, StackLabel), LabelConst = const(code_addr_const(StackLabel)), Code = node([ assign(redoip(lval(curfr)), LabelConst) - "restore redoip for quarter disj hijack" ]) ; HijackInfo = disj_half_hijack(RedoipSlot), require(unify(ResumeKnown, resume_point_unknown), "resume point known in disj_half_hijack"), require(unify(CurfrMaxfr, must_be_equal), "maxfr may differ from curfr in disj_half_hijack"), Code = node([ assign(redoip(lval(curfr)), lval(RedoipSlot)) - "restore redoip for half disj hijack" ]) ; HijackInfo = disj_full_hijack(RedoipSlot, RedofrSlot), require(unify(CurfrMaxfr, may_be_different), "maxfr same as curfr in disj_full_hijack"), Code = node([ assign(redoip(lval(maxfr)), lval(RedoipSlot)) - "restore redoip for full disj hijack", assign(redofr(lval(maxfr)), lval(RedofrSlot)) - "restore redofr for full disj hijack" ]) ), ( % HijackInfo \= disj_no_hijack if and only if % the disjunction is model_non. HijackInfo \= disj_no_hijack, CondEnv = inside_non_condition -> FailInfo = fail_info(ResumePoints, resume_point_unknown, CurfrMaxfr, CondEnv, Allow), code_info__set_fail_info(FailInfo, !CI) ; true ). %---------------------------------------------------------------------------% :- type ite_hijack_info ---> ite_info( resume_point_known, condition_env, ite_hijack_type ). :- type ite_hijack_type ---> ite_no_hijack ; ite_temp_frame( lval % The stack slot in which we saved % the value of maxfr. ) ; ite_quarter_hijack ; ite_half_hijack( lval % The stack slot in which we saved % the value of the hijacked redoip. ) ; ite_full_hijack( lval, % The stack slot in which we saved % the value of the hijacked redoip. lval, % The stack slot in which we saved % the value of the hijacked redofr. lval % The stack slot in which we saved % the value of maxfr. ). code_info__prepare_for_ite_hijack(EffCodeModel, HijackInfo, Code, !CI) :- code_info__get_fail_info(!.CI, FailInfo), FailInfo = fail_info(_, ResumeKnown, CurfrMaxfr, CondEnv, Allow), ( EffCodeModel \= model_non -> HijackType = ite_no_hijack, Code = node([ comment("ite no hijack") - "" ]) ; ( Allow = not_allowed ; CondEnv = inside_non_condition ) -> code_info__acquire_temp_slot(lval(maxfr), MaxfrSlot, !CI), HijackType = ite_temp_frame(MaxfrSlot), code_info__create_temp_frame(do_fail, "prepare for ite", TempFrameCode, !CI), MaxfrCode = node([ assign(MaxfrSlot, lval(maxfr)) - "prepare for ite" ]), Code = tree(TempFrameCode, MaxfrCode) ; CurfrMaxfr = must_be_equal, ResumeKnown = resume_point_known(_) -> HijackType = ite_quarter_hijack, Code = node([ comment("ite quarter hijack") - "" ]) ; CurfrMaxfr = must_be_equal -> % Here ResumeKnown must be resume_point_unknown. code_info__acquire_temp_slot(lval(redoip(lval(curfr))), RedoipSlot, !CI), HijackType = ite_half_hijack(RedoipSlot), Code = node([ assign(RedoipSlot, lval(redoip(lval(curfr)))) - "prepare for half ite hijack" ]) ; % Here CurfrMaxfr must be may_be_different. code_info__acquire_temp_slot(lval(redoip(lval(maxfr))), RedoipSlot, !CI), code_info__acquire_temp_slot(lval(redofr(lval(maxfr))), RedofrSlot, !CI), code_info__acquire_temp_slot(lval(maxfr), MaxfrSlot, !CI), HijackType = ite_full_hijack(RedoipSlot, RedofrSlot, MaxfrSlot), Code = node([ assign(MaxfrSlot, lval(maxfr)) - "prepare for full ite hijack", assign(RedoipSlot, lval(redoip(lval(maxfr)))) - "prepare for full ite hijack", assign(RedofrSlot, lval(redofr(lval(maxfr)))) - "prepare for full ite hijack", assign(redofr(lval(maxfr)), lval(curfr)) - "prepare for full ite hijack" ]) ), HijackInfo = ite_info(ResumeKnown, CondEnv, HijackType), ( EffCodeModel = model_non -> code_info__inside_non_condition(!CI) ; true ). code_info__ite_enter_then(HijackInfo, ThenCode, ElseCode, !CI) :- code_info__get_fail_info(!.CI, FailInfo0), FailInfo0 = fail_info(ResumePoints0, ResumeKnown0, CurfrMaxfr, _, Allow), stack__pop_det(ResumePoints0, _, ResumePoints), HijackInfo = ite_info(HijackResumeKnown, OldCondEnv, HijackType), ( HijackType = ite_no_hijack, ThenCode = empty, ElseCode = ThenCode ; HijackType = ite_temp_frame(MaxfrSlot), ThenCode = node([ % We can't remove the frame, it may not be on top. assign(redoip(lval(MaxfrSlot)), const(code_addr_const(do_fail))) - "soft cut for temp frame ite" ]), ElseCode = node([ assign(maxfr, lval(prevfr(lval(MaxfrSlot)))) - "restore maxfr for temp frame ite" ]) ; HijackType = ite_quarter_hijack, stack__top_det(ResumePoints, ResumePoint), ( code_info__maybe_pick_stack_resume_point(ResumePoint, _, StackLabel) -> LabelConst = const(code_addr_const(StackLabel)), ThenCode = node([ assign(redoip(lval(curfr)), LabelConst) - "restore redoip for quarter ite hijack" ]) ; % This can happen only if ResumePoint is unreachable % from here. ThenCode = empty ), ElseCode = ThenCode ; HijackType = ite_half_hijack(RedoipSlot), ThenCode = node([ assign(redoip(lval(curfr)), lval(RedoipSlot)) - "restore redoip for half ite hijack" ]), ElseCode = ThenCode ; HijackType = ite_full_hijack(RedoipSlot, RedofrSlot, MaxfrSlot), ThenCode = node([ assign(redoip(lval(MaxfrSlot)), lval(RedoipSlot)) - "restore redoip for full ite hijack", assign(redofr(lval(MaxfrSlot)), lval(RedofrSlot)) - "restore redofr for full ite hijack" ]), ElseCode = node([ assign(redoip(lval(maxfr)), lval(RedoipSlot)) - "restore redoip for full ite hijack", assign(redofr(lval(maxfr)), lval(RedofrSlot)) - "restore redofr for full ite hijack" ]) ), ( ResumeKnown0 = resume_point_unknown -> ResumeKnown = resume_point_unknown ; ResumeKnown = HijackResumeKnown ), FailInfo = fail_info(ResumePoints, ResumeKnown, CurfrMaxfr, OldCondEnv, Allow), code_info__set_fail_info(FailInfo, !CI). %---------------------------------------------------------------------------% :- type simple_neg_info == fail_info. code_info__enter_simple_neg(ResumeVars, GoalInfo, FailInfo0, !CI) :- code_info__get_fail_info(!.CI, FailInfo0), % The only reason why we push a resume point at all % is to protect the variables in ResumeVars from becoming % unknown; by including them in the domain of the resume point, % we guarantee that they will become zombies instead of % unknown if they die in the pre- or post-goal updates. % Therefore the only part of ResumePoint that matters % is the set of variables in the resume map; the other % parts of ResumePoint (the locations, the code address) % will not be referenced. set__to_sorted_list(ResumeVars, ResumeVarList), map__init(ResumeMap0), code_info__make_fake_resume_map(ResumeVarList, ResumeMap0, ResumeMap), ResumePoint = orig_only(ResumeMap, do_redo), code_info__effect_resume_point(ResumePoint, model_semi, Code, !CI), require(unify(Code, empty), "nonempty code for simple neg"), code_info__pre_goal_update(GoalInfo, yes, !CI). code_info__leave_simple_neg(GoalInfo, FailInfo, !CI) :- code_info__post_goal_update(GoalInfo, !CI), code_info__set_fail_info(FailInfo, !CI). :- pred code_info__make_fake_resume_map(list(prog_var)::in, map(prog_var, set(lval))::in, map(prog_var, set(lval))::out) is det. code_info__make_fake_resume_map([], ResumeMap, ResumeMap). code_info__make_fake_resume_map([Var | Vars], ResumeMap0, ResumeMap) :- % a visibly fake location set__singleton_set(Locns, reg(r, -1)), map__det_insert(ResumeMap0, Var, Locns, ResumeMap1), code_info__make_fake_resume_map(Vars, ResumeMap1, ResumeMap). %---------------------------------------------------------------------------% :- type det_commit_info ---> det_commit_info( maybe(lval), % Location of saved maxfr. maybe(pair(lval)) % Location of saved ticket % counter and trail pointer. ). code_info__prepare_for_det_commit(DetCommitInfo, Code, !CI) :- code_info__get_fail_info(!.CI, FailInfo0), FailInfo0 = fail_info(_, _, CurfrMaxfr, _, _), ( CurfrMaxfr = may_be_different, code_info__acquire_temp_slot(lval(maxfr), MaxfrSlot, !CI), SaveMaxfrCode = node([ assign(MaxfrSlot, lval(maxfr)) - "save the value of maxfr" ]), MaybeMaxfrSlot = yes(MaxfrSlot) ; CurfrMaxfr = must_be_equal, SaveMaxfrCode = empty, MaybeMaxfrSlot = no ), code_info__maybe_save_trail_info(MaybeTrailSlots, SaveTrailCode, !CI), DetCommitInfo = det_commit_info(MaybeMaxfrSlot, MaybeTrailSlots), Code = tree(SaveMaxfrCode, SaveTrailCode). code_info__generate_det_commit(DetCommitInfo, Code, !CI) :- DetCommitInfo = det_commit_info(MaybeMaxfrSlot, MaybeTrailSlots), ( MaybeMaxfrSlot = yes(MaxfrSlot), RestoreMaxfrCode = node([ assign(maxfr, lval(MaxfrSlot)) - "restore the value of maxfr - perform commit" ]), code_info__release_temp_slot(MaxfrSlot, !CI) ; MaybeMaxfrSlot = no, RestoreMaxfrCode = node([ assign(maxfr, lval(curfr)) - "restore the value of maxfr - perform commit" ]) ), code_info__maybe_restore_trail_info(MaybeTrailSlots, CommitTrailCode, _, !CI), Code = tree(RestoreMaxfrCode, CommitTrailCode). %---------------------------------------------------------------------------% :- type semi_commit_info ---> semi_commit_info( fail_info, % Fail_info on entry. resume_point_info, commit_hijack_info, maybe(pair(lval)) % Location of saved ticket % counter and trail pointer. ). :- type commit_hijack_info ---> commit_temp_frame( lval, % The stack slot in which we saved % the old value of maxfr. bool % Do we bracket the goal with % MR_commit_mark and MR_commit_cut? ) ; commit_quarter_hijack ; commit_half_hijack( lval % The stack slot in which we saved % the value of the hijacked redoip. ) ; commit_full_hijack( lval, % The stack slot in which we saved % the value of the hijacked redoip. lval, % The stack slot in which we saved % the value of the hijacked redofr. lval % The stack slot in which we saved % the value of maxfr. ). code_info__prepare_for_semi_commit(SemiCommitInfo, Code, !CI) :- code_info__get_fail_info(!.CI, FailInfo0), FailInfo0 = fail_info(ResumePoints0, ResumeKnown, CurfrMaxfr, CondEnv, Allow), stack__top_det(ResumePoints0, TopResumePoint), code_info__clone_resume_point(TopResumePoint, NewResumePoint, !CI), stack__push(ResumePoints0, NewResumePoint, ResumePoints), FailInfo = fail_info(ResumePoints, resume_point_known(has_been_done), CurfrMaxfr, CondEnv, Allow), code_info__set_fail_info(FailInfo, !CI), code_info__pick_stack_resume_point(NewResumePoint, _, StackLabel), StackLabelConst = const(code_addr_const(StackLabel)), ( ( Allow = not_allowed ; CondEnv = inside_non_condition ) -> code_info__acquire_temp_slot(lval(maxfr), MaxfrSlot, !CI), MaxfrCode = node([ assign(MaxfrSlot, lval(maxfr)) - "prepare for temp frame commit" ]), code_info__create_temp_frame(StackLabel, "prepare for temp frame commit", TempFrameCode, !CI), code_info__get_globals(!.CI, Globals), globals__lookup_bool_option(Globals, use_minimal_model, UseMinimalModel), HijackInfo = commit_temp_frame(MaxfrSlot, UseMinimalModel), ( UseMinimalModel = yes, % If the code we are committing across starts but % does not complete the evaluation of a tabled subgoal, % the cut will remove the generator's choice point, % so that the evaluation of the subgoal will never % be completed. We handle such "dangling" generators % by removing them from the subgoal trie of the % tabled procedure. This requires knowing what % tabled subgoals are started inside commits, % which is why we wrap the goal being committed across % inside MR_commit_{mark,cut}. Components = [ pragma_c_raw_code( "\t\tMR_save_transient_registers();\n", live_lvals_info(set__init)), pragma_c_raw_code( "\t\tMR_commit_mark();\n", live_lvals_info(set__init)), pragma_c_raw_code( "\t\tMR_restore_transient_registers();\n", live_lvals_info(set__init)) ], MarkCode = node([ pragma_c([], Components, will_not_call_mercury, no, no, no, no, no) - "" ]) ; UseMinimalModel = no, MarkCode = empty ), HijackCode = tree(MaxfrCode, tree(TempFrameCode, MarkCode)) ; ResumeKnown = resume_point_known(has_been_done), CurfrMaxfr = must_be_equal -> HijackInfo = commit_quarter_hijack, HijackCode = node([ assign(redoip(lval(curfr)), StackLabelConst) - "hijack the redofr slot" ]) ; CurfrMaxfr = must_be_equal -> % Here ResumeKnown must be resume_point_unknown or % resume_point_known(wont_be_done). code_info__acquire_temp_slot(lval(redoip(lval(curfr))), RedoipSlot, !CI), HijackInfo = commit_half_hijack(RedoipSlot), HijackCode = node([ assign(RedoipSlot, lval(redoip(lval(curfr)))) - "prepare for half commit hijack", assign(redoip(lval(curfr)), StackLabelConst) - "hijack the redofr slot" ]) ; % Here CurfrMaxfr must be may_be_different. code_info__acquire_temp_slot(lval(redoip(lval(maxfr))), RedoipSlot, !CI), code_info__acquire_temp_slot(lval(redofr(lval(maxfr))), RedofrSlot, !CI), code_info__acquire_temp_slot(lval(maxfr), MaxfrSlot, !CI), HijackInfo = commit_full_hijack(RedoipSlot, RedofrSlot, MaxfrSlot), HijackCode = node([ assign(RedoipSlot, lval(redoip(lval(maxfr)))) - "prepare for full commit hijack", assign(RedofrSlot, lval(redofr(lval(maxfr)))) - "prepare for full commit hijack", assign(MaxfrSlot, lval(maxfr)) - "prepare for full commit hijack", assign(redofr(lval(maxfr)), lval(curfr)) - "hijack the redofr slot", assign(redoip(lval(maxfr)), StackLabelConst) - "hijack the redoip slot" ]) ), code_info__maybe_save_trail_info(MaybeTrailSlots, SaveTrailCode, !CI), SemiCommitInfo = semi_commit_info(FailInfo0, NewResumePoint, HijackInfo, MaybeTrailSlots), Code = tree(HijackCode, SaveTrailCode). code_info__generate_semi_commit(SemiCommitInfo, Code, !CI) :- SemiCommitInfo = semi_commit_info(FailInfo, ResumePoint, HijackInfo, MaybeTrailSlots), code_info__set_fail_info(FailInfo, !CI), % XXX should release the temp slots in each arm of the switch ( HijackInfo = commit_temp_frame(MaxfrSlot, UseMinimalModel), MaxfrCode = node([ assign(maxfr, lval(MaxfrSlot)) - "restore maxfr for temp frame hijack" ]), ( UseMinimalModel = yes, % See the comment in prepare_for_semi_commit above. Components = [ pragma_c_raw_code("\t\tMR_commit_cut();\n", live_lvals_info(set__init)) ], CutCode = node([ pragma_c([], Components, will_not_call_mercury, no, no, no, no, no) - "commit for temp frame hijack" ]) ; UseMinimalModel = no, CutCode = empty ), SuccessUndoCode = tree(MaxfrCode, CutCode), FailureUndoCode = tree(MaxfrCode, CutCode) ; HijackInfo = commit_quarter_hijack, FailInfo = fail_info(ResumePoints, _, _, _, _), stack__top_det(ResumePoints, TopResumePoint), code_info__pick_stack_resume_point(TopResumePoint, _, StackLabel), StackLabelConst = const(code_addr_const(StackLabel)), SuccessUndoCode = node([ assign(maxfr, lval(curfr)) - "restore maxfr for quarter commit hijack", assign(redoip(lval(maxfr)), StackLabelConst) - "restore redoip for quarter commit hijack" ]), FailureUndoCode = node([ assign(redoip(lval(maxfr)), StackLabelConst) - "restore redoip for quarter commit hijack" ]) ; HijackInfo = commit_half_hijack(RedoipSlot), SuccessUndoCode = node([ assign(maxfr, lval(curfr)) - "restore maxfr for half commit hijack", assign(redoip(lval(maxfr)), lval(RedoipSlot)) - "restore redoip for half commit hijack" ]), FailureUndoCode = node([ assign(redoip(lval(maxfr)), lval(RedoipSlot)) - "restore redoip for half commit hijack" ]) ; HijackInfo = commit_full_hijack(RedoipSlot, RedofrSlot, MaxfrSlot), SuccessUndoCode = node([ assign(maxfr, lval(MaxfrSlot)) - "restore maxfr for full commit hijack", assign(redoip(lval(maxfr)), lval(RedoipSlot)) - "restore redoip for full commit hijack", assign(redofr(lval(maxfr)), lval(RedofrSlot)) - "restore redofr for full commit hijack" ]), FailureUndoCode = node([ assign(redoip(lval(maxfr)), lval(RedoipSlot)) - "restore redoip for full commit hijack", assign(redofr(lval(maxfr)), lval(RedofrSlot)) - "restore redofr for full commit hijack" ]) ), code_info__remember_position(!.CI, AfterCommit), code_info__generate_resume_point(ResumePoint, ResumePointCode, !CI), code_info__generate_failure(FailCode, !CI), code_info__reset_to_position(AfterCommit, !CI), code_info__maybe_restore_trail_info(MaybeTrailSlots, CommitTrailCode, RestoreTrailCode, !CI), code_info__get_next_label(SuccLabel, !CI), GotoSuccLabel = node([ goto(label(SuccLabel)) - "Jump to success continuation" ]), SuccLabelCode = node([ label(SuccLabel) - "Success continuation" ]), SuccessCode = tree(SuccessUndoCode, CommitTrailCode), FailureCode = tree(ResumePointCode, tree(FailureUndoCode, tree(RestoreTrailCode, FailCode))), Code = tree(SuccessCode, tree(GotoSuccLabel, tree(FailureCode, SuccLabelCode))). %---------------------------------------------------------------------------% :- pred code_info__inside_non_condition(code_info::in, code_info::out) is det. code_info__inside_non_condition(!CI) :- code_info__get_fail_info(!.CI, FailInfo0), FailInfo0 = fail_info(ResumePoints, ResumeKnown, CurfrMaxfr, _, Allow), FailInfo = fail_info(ResumePoints, ResumeKnown, CurfrMaxfr, inside_non_condition, Allow), code_info__set_fail_info(FailInfo, !CI). :- pred code_info__create_temp_frame(code_addr::in, string::in, code_tree::out, code_info::in, code_info::out) is det. code_info__create_temp_frame(Redoip, Comment, Code, !CI) :- ( code_info__get_proc_model(!.CI) = model_non -> Kind = nondet_stack_proc ; Kind = det_stack_proc ), Code = node([ mkframe(temp_frame(Kind), Redoip) - Comment ]), code_info__set_created_temp_frame(yes, !CI), code_info__get_fail_info(!.CI, FailInfo0), FailInfo0 = fail_info(ResumePoints, ResumeKnown, _, CondEnv, Allow), FailInfo = fail_info(ResumePoints, ResumeKnown, may_be_different, CondEnv, Allow), code_info__set_fail_info(FailInfo, !CI). %---------------------------------------------------------------------------% code_info__effect_resume_point(ResumePoint, CodeModel, Code, !CI) :- code_info__get_fail_info(!.CI, FailInfo0), FailInfo0 = fail_info(ResumePoints0, _ResumeKnown, CurfrMaxfr, CondEnv, Allow), ( stack__top(ResumePoints0, OldResumePoint) -> code_info__pick_first_resume_point(OldResumePoint, OldMap, _), code_info__pick_first_resume_point(ResumePoint, NewMap, _), map__keys(OldMap, OldKeys), map__keys(NewMap, NewKeys), set__list_to_set(OldKeys, OldKeySet), set__list_to_set(NewKeys, NewKeySet), require(set__subset(OldKeySet, NewKeySet), "non-nested resume point variable sets") ; true ), stack__push(ResumePoints0, ResumePoint, ResumePoints), ( CodeModel = model_non -> code_info__pick_stack_resume_point(ResumePoint, _, StackLabel), LabelConst = const(code_addr_const(StackLabel)), Code = node([ assign(redoip(lval(maxfr)), LabelConst) - "hijack redoip to effect resume point" ]), RedoipUpdate = has_been_done ; Code = empty, RedoipUpdate = wont_be_done ), FailInfo = fail_info(ResumePoints, resume_point_known(RedoipUpdate), CurfrMaxfr, CondEnv, Allow), code_info__set_fail_info(FailInfo, !CI). code_info__pop_resume_point(!CI) :- code_info__get_fail_info(!.CI, FailInfo0), FailInfo0 = fail_info(ResumePoints0, ResumeKnown, CurfrMaxfr, CondEnv, Allow), stack__pop_det(ResumePoints0, _, ResumePoints), FailInfo = fail_info(ResumePoints, ResumeKnown, CurfrMaxfr, CondEnv, Allow), code_info__set_fail_info(FailInfo, !CI). %---------------------------------------------------------------------------% code_info__top_resume_point(CI, ResumePoint) :- code_info__get_fail_info(CI, FailInfo), FailInfo = fail_info(ResumePoints, _, _, _, _), stack__top_det(ResumePoints, ResumePoint). code_info__set_resume_point_to_unknown(!CI) :- code_info__get_fail_info(!.CI, FailInfo0), FailInfo0 = fail_info(ResumePoints, _, CurfrMaxfr, CondEnv, Allow), FailInfo = fail_info(ResumePoints, resume_point_unknown, CurfrMaxfr, CondEnv, Allow), code_info__set_fail_info(FailInfo, !CI). code_info__set_resume_point_and_frame_to_unknown(!CI) :- code_info__get_fail_info(!.CI, FailInfo0), FailInfo0 = fail_info(ResumePoints, _, _, CondEnv, Allow), FailInfo = fail_info(ResumePoints, resume_point_unknown, may_be_different, CondEnv, Allow), code_info__set_fail_info(FailInfo, !CI). %---------------------------------------------------------------------------% code_info__generate_failure(Code, !CI) :- code_info__get_fail_info(!.CI, FailInfo), FailInfo = fail_info(ResumePoints, ResumeKnown, _, _, _), ( ResumeKnown = resume_point_known(_), stack__top_det(ResumePoints, TopResumePoint), ( code_info__pick_matching_resume_addr(!.CI, TopResumePoint, FailureAddress0) -> FailureAddress = FailureAddress0, PlaceCode = empty ; code_info__pick_first_resume_point(TopResumePoint, Map, FailureAddress), map__to_assoc_list(Map, AssocList), code_info__remember_position(!.CI, CurPos), code_info__pick_and_place_vars(AssocList, _, PlaceCode, !CI), code_info__reset_to_position(CurPos, !CI) ), BranchCode = node([goto(FailureAddress) - "fail"]), Code = tree(PlaceCode, BranchCode) ; ResumeKnown = resume_point_unknown, Code = node([goto(do_redo) - "fail"]) ). code_info__fail_if_rval_is_false(Rval0, Code, !CI) :- code_info__get_fail_info(!.CI, FailInfo), FailInfo = fail_info(ResumePoints, ResumeKnown, _, _, _), ( ResumeKnown = resume_point_known(_), stack__top_det(ResumePoints, TopResumePoint), ( code_info__pick_matching_resume_addr(!.CI, TopResumePoint, FailureAddress0) -> % We branch away if the test *fails* code_util__neg_rval(Rval0, Rval), Code = node([ if_val(Rval, FailureAddress0) - "Test for failure" ]) ; code_info__pick_first_resume_point(TopResumePoint, Map, FailureAddress), map__to_assoc_list(Map, AssocList), code_info__get_next_label(SuccessLabel, !CI), code_info__remember_position(!.CI, CurPos), code_info__pick_and_place_vars(AssocList, _, PlaceCode, !CI), code_info__reset_to_position(CurPos, !CI), SuccessAddress = label(SuccessLabel), % We branch away if the test *fails*, % therefore if the test succeeds, we branch % around the code that moves variables to % their failure locations and branches away % to the failure continuation TestCode = node([ if_val(Rval0, SuccessAddress) - "Test for failure" ]), TailCode = node([ goto(FailureAddress) - "Goto failure", label(SuccessLabel) - "Success continuation" ]), Code = tree(TestCode, tree(PlaceCode, TailCode)) ) ; ResumeKnown = resume_point_unknown, % We branch away if the test *fails* code_util__neg_rval(Rval0, Rval), Code = node([ if_val(Rval, do_redo) - "Test for failure" ]) ). %---------------------------------------------------------------------------% code_info__failure_is_direct_branch(CI, CodeAddr) :- code_info__get_fail_info(CI, FailInfo), FailInfo = fail_info(ResumePoints, resume_point_known(_), _, _, _), stack__top(ResumePoints, TopResumePoint), code_info__pick_matching_resume_addr(CI, TopResumePoint, CodeAddr). code_info__may_use_nondet_tailcall(CI, TailCallStatus) :- code_info__get_fail_info(CI, FailInfo), FailInfo = fail_info(ResumePoints0, ResumeKnown, _, _, _), ( stack__pop(ResumePoints0, ResumePoint1, ResumePoints1), stack__is_empty(ResumePoints1), ResumePoint1 = stack_only(_, do_fail) -> ( ResumeKnown = resume_point_known(_), TailCallStatus = unchecked_tail_call ; ResumeKnown = resume_point_unknown, TailCallStatus = checked_tail_call ) ; TailCallStatus = no_tail_call ). %---------------------------------------------------------------------------% % See whether the current locations of variables match the locations % associated with any of the options in the given failure map. % If yes, return the code_addr of that option. :- pred code_info__pick_matching_resume_addr(code_info::in, resume_point_info::in, code_addr::out) is semidet. code_info__pick_matching_resume_addr(CI, ResumeMaps, Addr) :- code_info__variable_locations(CI, CurLocs), ( ResumeMaps = orig_only(Map1, Addr1), ( code_info__match_resume_loc(Map1, CurLocs) -> Addr = Addr1 ; fail ) ; ResumeMaps = stack_only(Map1, Addr1), ( code_info__match_resume_loc(Map1, CurLocs) -> Addr = Addr1 ; fail ) ; ResumeMaps = orig_and_stack(Map1, Addr1, Map2, Addr2), ( code_info__match_resume_loc(Map1, CurLocs) -> Addr = Addr1 ; code_info__match_resume_loc(Map2, CurLocs) -> Addr = Addr2 ; fail ) ; ResumeMaps = stack_and_orig(Map1, Addr1, Map2, Addr2), ( code_info__match_resume_loc(Map1, CurLocs) -> Addr = Addr1 ; code_info__match_resume_loc(Map2, CurLocs) -> Addr = Addr2 ; fail ) ). :- pred code_info__match_resume_loc(resume_map::in, resume_map::in) is semidet. code_info__match_resume_loc(Map, Locations0) :- map__keys(Map, KeyList), set__list_to_set(KeyList, Keys), map__select(Locations0, Keys, Locations), map__to_assoc_list(Locations, List), \+ ( list__member(Var - Actual, List), \+ ( map__search(Map, Var, Lvals), set__subset(Lvals, Actual) ) ). :- pred code_info__pick_first_resume_point(resume_point_info::in, resume_map::out, code_addr::out) is det. code_info__pick_first_resume_point(orig_only(Map, Addr), Map, Addr). code_info__pick_first_resume_point(stack_only(Map, Addr), Map, Addr). code_info__pick_first_resume_point(orig_and_stack(Map, Addr, _, _), Map, Addr). code_info__pick_first_resume_point(stack_and_orig(Map, Addr, _, _), Map, Addr). :- pred code_info__pick_stack_resume_point(resume_point_info::in, resume_map::out, code_addr::out) is det. code_info__pick_stack_resume_point(ResumePoint, Map, Addr) :- ( code_info__maybe_pick_stack_resume_point(ResumePoint, Map1, Addr1) -> Map = Map1, Addr = Addr1 ; error("no stack resume point") ). :- pred code_info__maybe_pick_stack_resume_point(resume_point_info::in, resume_map::out, code_addr::out) is semidet. code_info__maybe_pick_stack_resume_point(stack_only(Map, Addr), Map, Addr). code_info__maybe_pick_stack_resume_point(orig_and_stack(_, _, Map, Addr), Map, Addr). code_info__maybe_pick_stack_resume_point(stack_and_orig(Map, Addr, _, _), Map, Addr). %---------------------------------------------------------------------------% code_info__produce_vars(Vars, Map, Code, !CI) :- set__to_sorted_list(Vars, VarList), code_info__produce_vars_2(VarList, Map, Code, !CI). :- pred code_info__produce_vars_2(list(prog_var)::in, map(prog_var, set(lval))::out, code_tree::out, code_info::in, code_info::out) is det. code_info__produce_vars_2([], Map, empty, !CI) :- map__init(Map). code_info__produce_vars_2([V | Vs], Map, Code, !CI) :- code_info__produce_vars_2(Vs, Map0, Code0, !CI), code_info__produce_variable_in_reg_or_stack(V, Code1, Lval, !CI), set__singleton_set(Lvals, Lval), map__set(Map0, V, Lvals, Map), Code = tree(Code0, Code1). code_info__flush_resume_vars_to_stack(Code, !CI) :- code_info__compute_resume_var_stack_locs(!.CI, VarLocs), code_info__place_vars(VarLocs, Code, !CI). :- pred compute_resume_var_stack_locs(code_info::in, assoc_list(prog_var, lval)::out) is det. code_info__compute_resume_var_stack_locs(CI, VarLocs) :- code_info__get_fail_info(CI, FailInfo), FailInfo = fail_info(ResumePointStack, _, _, _, _), stack__top_det(ResumePointStack, ResumePoint), code_info__pick_stack_resume_point(ResumePoint, StackMap, _), map__to_assoc_list(StackMap, VarLocSets), code_info__pick_var_places(VarLocSets, VarLocs). %---------------------------------------------------------------------------% :- pred code_info__init_fail_info(code_model::in, maybe(set(prog_var))::in, resume_point_info::out, code_info::in, code_info::out) is det. code_info__init_fail_info(CodeModel, MaybeFailVars, ResumePoint, !CI) :- ( CodeModel = model_det, code_info__get_next_label(ResumeLabel, !CI), ResumeAddress = label(ResumeLabel), ResumeKnown = resume_point_unknown, CurfrMaxfr = may_be_different ; CodeModel = model_semi, % The resume point for this label % will be part of the procedure epilog. code_info__get_next_label(ResumeLabel, !CI), ResumeAddress = label(ResumeLabel), ResumeKnown = resume_point_known(wont_be_done), CurfrMaxfr = may_be_different ; CodeModel = model_non, ( MaybeFailVars = yes(_) -> code_info__get_next_label(ResumeLabel, !CI), ResumeAddress = label(ResumeLabel) ; ResumeAddress = do_fail ), ResumeKnown = resume_point_known(has_been_done), CurfrMaxfr = must_be_equal ), ( MaybeFailVars = yes(FailVars) -> code_info__get_stack_slots(!.CI, StackSlots), map__select(StackSlots, FailVars, StackMap0), map__to_assoc_list(StackMap0, StackList0), code_info__make_singleton_sets(StackList0, StackList), map__from_assoc_list(StackList, StackMap) ; map__init(StackMap) ), ResumePoint = stack_only(StackMap, ResumeAddress), stack__init(ResumeStack0), stack__push(ResumeStack0, ResumePoint, ResumeStack), code_info__get_fail_info(!.CI, FailInfo0), FailInfo0 = fail_info(_, _, _, _, Allow), FailInfo = fail_info(ResumeStack, ResumeKnown, CurfrMaxfr, not_inside_non_condition, Allow), code_info__set_fail_info(FailInfo, !CI). %---------------------------------------------------------------------------% code_info__make_resume_point(ResumeVars, ResumeLocs, FullMap, ResumePoint, !CI) :- code_info__get_stack_slots(!.CI, StackSlots), map__select(FullMap, ResumeVars, OrigMap), ( ResumeLocs = orig_only, code_info__get_next_label(OrigLabel, !CI), OrigAddr = label(OrigLabel), ResumePoint = orig_only(OrigMap, OrigAddr) ; ResumeLocs = stack_only, code_info__make_stack_resume_map(ResumeVars, StackSlots, StackMap), code_info__get_next_label(StackLabel, !CI), StackAddr = label(StackLabel), ResumePoint = stack_only(StackMap, StackAddr) ; ResumeLocs = orig_and_stack, code_info__make_stack_resume_map(ResumeVars, StackSlots, StackMap), code_info__get_next_label(OrigLabel, !CI), OrigAddr = label(OrigLabel), code_info__get_next_label(StackLabel, !CI), StackAddr = label(StackLabel), ResumePoint = orig_and_stack(OrigMap, OrigAddr, StackMap, StackAddr) ; ResumeLocs = stack_and_orig, code_info__make_stack_resume_map(ResumeVars, StackSlots, StackMap), code_info__get_next_label(StackLabel, !CI), StackAddr = label(StackLabel), code_info__get_next_label(OrigLabel, !CI), OrigAddr = label(OrigLabel), ResumePoint = stack_and_orig(StackMap, StackAddr, OrigMap, OrigAddr) ). :- pred code_info__make_stack_resume_map(set(prog_var)::in, stack_slots::in, map(prog_var, set(lval))::out) is det. code_info__make_stack_resume_map(ResumeVars, StackSlots, StackMap) :- map__select(StackSlots, ResumeVars, StackMap0), map__to_assoc_list(StackMap0, StackList0), code_info__make_singleton_sets(StackList0, StackList), map__from_assoc_list(StackList, StackMap). :- pred code_info__make_singleton_sets(assoc_list(prog_var, lval)::in, assoc_list(prog_var, set(lval))::out) is det. code_info__make_singleton_sets([], []). code_info__make_singleton_sets([V - L | Rest0], [V - Ls | Rest]) :- set__singleton_set(Ls, L), code_info__make_singleton_sets(Rest0, Rest). %---------------------------------------------------------------------------% % The code we generate for a resumption point looks like this: % % label(StackLabel) % % % label(OrigLabel) % % % Failures at different points may cause control to arrive at % the resumption point via either label, which is why the last % line is necessary. % % The idea is that failures from other procedures will go to % StackLabel, and that failures from this procedure while % everything is in its original place will go to OrigLabel. % Failures from this procedure where not everything is in its % original place can go to either, after moving the resume variables % to the places where the label expects them. % % The above layout (stack, then orig) is the most common. However, % liveness.m may decide that one or other of the two labels will % never be referred to (e.g. because there are no calls inside % the range of effect of the resumption point or because a call % follows immediately after the establishment of the resumption % point), or that it would be more efficient to put the two labels % in the other order (e.g. because the code after the resumption point % needs most of the variables in their stack slots). code_info__generate_resume_point(ResumePoint, Code, !CI) :- ( ResumePoint = orig_only(Map1, Addr1), extract_label_from_code_addr(Addr1, Label1), Code = node([ label(Label1) - "orig only failure continuation" ]), code_info__set_var_locations(Map1, !CI) ; ResumePoint = stack_only(Map1, Addr1), extract_label_from_code_addr(Addr1, Label1), Code = node([ label(Label1) - "stack only failure continuation" ]), code_info__set_var_locations(Map1, !CI), code_info__generate_resume_layout(Label1, Map1, !CI) ; ResumePoint = stack_and_orig(Map1, Addr1, Map2, Addr2), extract_label_from_code_addr(Addr1, Label1), extract_label_from_code_addr(Addr2, Label2), Label1Code = node([ label(Label1) - "stack failure continuation before orig" ]), code_info__set_var_locations(Map1, !CI), code_info__generate_resume_layout(Label1, Map1, !CI), map__to_assoc_list(Map2, AssocList2), code_info__place_resume_vars(AssocList2, PlaceCode, !CI), Label2Code = node([ label(Label2) - "orig failure continuation after stack" ]), code_info__set_var_locations(Map2, !CI), Code = tree(Label1Code, tree(PlaceCode, Label2Code)) ; ResumePoint = orig_and_stack(Map1, Addr1, Map2, Addr2), extract_label_from_code_addr(Addr1, Label1), extract_label_from_code_addr(Addr2, Label2), Label1Code = node([ label(Label1) - "orig failure continuation before stack" ]), code_info__set_var_locations(Map1, !CI), map__to_assoc_list(Map2, AssocList2), code_info__place_resume_vars(AssocList2, PlaceCode, !CI), Label2Code = node([ label(Label2) - "stack failure continuation after orig" ]), code_info__set_var_locations(Map2, !CI), code_info__generate_resume_layout(Label2, Map2, !CI), Code = tree(Label1Code, tree(PlaceCode, Label2Code)) ). :- pred extract_label_from_code_addr(code_addr::in, label::out) is det. extract_label_from_code_addr(CodeAddr, Label) :- ( CodeAddr = label(Label0) -> Label = Label0 ; error("extract_label_from_code_addr: non-label!") ). :- pred code_info__place_resume_vars(assoc_list(prog_var, set(lval))::in, code_tree::out, code_info::in, code_info::out) is det. code_info__place_resume_vars([], empty, !CI). code_info__place_resume_vars([Var - TargetSet | Rest], Code, !CI) :- set__to_sorted_list(TargetSet, Targets), code_info__place_resume_var(Var, Targets, FirstCode, !CI), Code = tree(FirstCode, RestCode), code_info__place_resume_vars(Rest, RestCode, !CI). :- pred code_info__place_resume_var(prog_var::in, list(lval)::in, code_tree::out, code_info::in, code_info::out) is det. code_info__place_resume_var(_Var, [], empty, !CI). code_info__place_resume_var(Var, [Target | Targets], Code, !CI) :- code_info__place_var(Var, Target, FirstCode, !CI), code_info__place_resume_var(Var, Targets, RestCode, !CI), Code = tree(FirstCode, RestCode). % Reset the code generator's database of what is where. % Remember that the variables in the map are available in their % associated rvals; forget about all other variables. :- pred code_info__set_var_locations(resume_map::in, code_info::in, code_info::out) is det. code_info__set_var_locations(Map, !CI) :- map__to_assoc_list(Map, LvalList0), code_info__flatten_varlval_list(LvalList0, LvalList), code_info__get_var_locn_info(!.CI, VarLocnInfo0), var_locn__reinit_state(LvalList, VarLocnInfo0, VarLocnInfo), code_info__set_var_locn_info(VarLocnInfo, !CI). :- pred code_info__flatten_varlval_list(assoc_list(prog_var, set(lval))::in, assoc_list(prog_var, lval)::out) is det. code_info__flatten_varlval_list([], []). code_info__flatten_varlval_list([V - Rvals | Rest0], All) :- code_info__flatten_varlval_list(Rest0, Rest), set__to_sorted_list(Rvals, RvalList), code_info__flatten_varlval_list_2(RvalList, V, Rest1), list__append(Rest1, Rest, All). :- pred code_info__flatten_varlval_list_2(list(lval)::in, prog_var::in, assoc_list(prog_var, lval)::out) is det. code_info__flatten_varlval_list_2([], _V, []). code_info__flatten_varlval_list_2([R | Rs], V, [V - R | Rest]) :- code_info__flatten_varlval_list_2(Rs, V, Rest). code_info__resume_point_vars(ResumePoint, Vars) :- code_info__pick_first_resume_point(ResumePoint, ResumeMap, _), map__keys(ResumeMap, Vars). code_info__resume_point_stack_addr(ResumePoint, StackAddr) :- code_info__pick_stack_resume_point(ResumePoint, _, StackAddr). %---------------------------------------------------------------------------% :- pred code_info__maybe_save_trail_info(maybe(pair(lval))::out, code_tree::out, code_info::in, code_info::out) is det. code_info__maybe_save_trail_info(MaybeTrailSlots, SaveTrailCode, !CI) :- code_info__get_globals(!.CI, Globals), globals__lookup_bool_option(Globals, use_trail, UseTrail), ( UseTrail = yes -> code_info__acquire_temp_slot(ticket_counter, CounterSlot, !CI), code_info__acquire_temp_slot(ticket, TrailPtrSlot, !CI), MaybeTrailSlots = yes(CounterSlot - TrailPtrSlot), SaveTrailCode = node([ mark_ticket_stack(CounterSlot) - "save the ticket counter", store_ticket(TrailPtrSlot) - "save the trail pointer" ]) ; MaybeTrailSlots = no, SaveTrailCode = empty ). :- pred code_info__maybe_restore_trail_info(maybe(pair(lval))::in, code_tree::out, code_tree::out, code_info::in, code_info::out) is det. code_info__maybe_restore_trail_info(MaybeTrailSlots, CommitCode, RestoreCode, !CI) :- ( MaybeTrailSlots = no, CommitCode = empty, RestoreCode = empty ; MaybeTrailSlots = yes(CounterSlot - TrailPtrSlot), CommitCode = node([ reset_ticket(lval(TrailPtrSlot), commit) - "discard trail entries and restore trail ptr", prune_tickets_to(lval(CounterSlot)) - "restore ticket counter (but not high water mark)" ]), RestoreCode = node([ reset_ticket(lval(TrailPtrSlot), undo) - "apply trail entries and restore trail ptr", discard_ticket - "restore ticket counter and high water mark" ]), code_info__release_temp_slot(CounterSlot, !CI), code_info__release_temp_slot(TrailPtrSlot, !CI) ). %---------------------------------------------------------------------------% :- pred code_info__clone_resume_point(resume_point_info::in, resume_point_info::out, code_info::in, code_info::out) is det. code_info__clone_resume_point(ResumePoint0, ResumePoint, !CI) :- ( ResumePoint0 = orig_only(_, _), error("cloning orig_only resume point") ; ResumePoint0 = stack_only(Map1, _), code_info__get_next_label(Label1, !CI), Addr1 = label(Label1), ResumePoint = stack_only(Map1, Addr1) ; ResumePoint0 = stack_and_orig(Map1, _, Map2, _), code_info__get_next_label(Label1, !CI), Addr1 = label(Label1), code_info__get_next_label(Label2, !CI), Addr2 = label(Label2), ResumePoint = stack_and_orig(Map1, Addr1, Map2, Addr2) ; ResumePoint0 = orig_and_stack(Map1, _, Map2, _), code_info__get_next_label(Label2, !CI), Addr2 = label(Label2), code_info__get_next_label(Label1, !CI), Addr1 = label(Label1), ResumePoint = stack_and_orig(Map2, Addr2, Map1, Addr1) ). %---------------------------------------------------------------------------% %---------------------------------------------------------------------------% % Submodule to deal with liveness issues. % The principles underlying this submodule of code_info.m are % documented in the file compiler/notes/allocation.html. :- interface. :- pred code_info__get_known_variables(code_info::in, list(prog_var)::out) is det. :- pred code_info__variable_is_forward_live(code_info::in, prog_var::in) is semidet. :- pred code_info__make_vars_forward_dead(set(prog_var)::in, code_info::in, code_info::out) is det. :- pred code_info__pickup_zombies(set(prog_var)::out, code_info::in, code_info::out) is det. %---------------------------------------------------------------------------% :- implementation. :- pred code_info__add_forward_live_vars(set(prog_var)::in, code_info::in, code_info::out) is det. :- pred code_info__rem_forward_live_vars(set(prog_var)::in, code_info::in, code_info::out) is det. % Make these variables appear magically live. % We don't care where they are put. :- pred code_info__make_vars_forward_live(set(prog_var)::in, code_info::in, code_info::out) is det. code_info__get_known_variables(CI, VarList) :- code_info__get_forward_live_vars(CI, ForwardLiveVars), ResumeVars = code_info__current_resume_point_vars(CI), set__union(ForwardLiveVars, ResumeVars, Vars), set__to_sorted_list(Vars, VarList). code_info__variable_is_forward_live(CI, Var) :- code_info__get_forward_live_vars(CI, Liveness), set__member(Var, Liveness). code_info__add_forward_live_vars(Births, !CI) :- code_info__get_forward_live_vars(!.CI, Liveness0), set__union(Liveness0, Births, Liveness), code_info__set_forward_live_vars(Liveness, !CI). code_info__rem_forward_live_vars(Deaths, !CI) :- code_info__get_forward_live_vars(!.CI, Liveness0), set__difference(Liveness0, Deaths, Liveness), code_info__set_forward_live_vars(Liveness, !CI). code_info__make_vars_forward_live(Vars, !CI) :- code_info__get_stack_slots(!.CI, StackSlots), code_info__get_var_locn_info(!.CI, VarLocnInfo0), set__to_sorted_list(Vars, VarList), code_info__make_vars_forward_live_2(VarList, StackSlots, 1, VarLocnInfo0, VarLocnInfo), code_info__set_var_locn_info(VarLocnInfo, !CI). :- pred code_info__make_vars_forward_live_2(list(prog_var)::in, stack_slots::in, int::in, var_locn_info::in, var_locn_info::out) is det. code_info__make_vars_forward_live_2([], _, _, !VarLocnInfo). code_info__make_vars_forward_live_2([Var | Vars], StackSlots, N0, !VarLocnInfo) :- ( map__search(StackSlots, Var, Lval0) -> Lval = Lval0, N1 = N0 ; code_info__find_unused_reg(!.VarLocnInfo, N0, N1), Lval = reg(r, N1) ), var_locn__set_magic_var_location(Var, Lval, !VarLocnInfo), code_info__make_vars_forward_live_2(Vars, StackSlots, N1, !VarLocnInfo). :- pred code_info__find_unused_reg(var_locn_info::in, int::in, int::out) is det. code_info__find_unused_reg(VLI, N0, N) :- ( var_locn__lval_in_use(VLI, reg(r, N0)) -> code_info__find_unused_reg(VLI, N0 + 1, N) ; N = N0 ). code_info__make_vars_forward_dead(Vars, !CI) :- code_info__maybe_make_vars_forward_dead(Vars, yes, !CI). :- pred code_info__maybe_make_vars_forward_dead(set(prog_var)::in, bool::in, code_info::in, code_info::out) is det. code_info__maybe_make_vars_forward_dead(Vars0, FirstTime, !CI) :- ResumeVars = code_info__current_resume_point_vars(!.CI), set__intersect(Vars0, ResumeVars, FlushVars), code_info__get_zombies(!.CI, Zombies0), set__union(Zombies0, FlushVars, Zombies), code_info__set_zombies(Zombies, !CI), set__difference(Vars0, Zombies, Vars), set__to_sorted_list(Vars, VarList), code_info__maybe_make_vars_forward_dead_2(VarList, FirstTime, !CI). :- pred code_info__maybe_make_vars_forward_dead_2(list(prog_var)::in, bool::in, code_info::in, code_info::out) is det. code_info__maybe_make_vars_forward_dead_2([], _, !CI). code_info__maybe_make_vars_forward_dead_2([V | Vs], FirstTime, !CI) :- code_info__get_var_locn_info(!.CI, VarLocnInfo0), var_locn__var_becomes_dead(V, FirstTime, VarLocnInfo0, VarLocnInfo), code_info__set_var_locn_info(VarLocnInfo, !CI), code_info__maybe_make_vars_forward_dead_2(Vs, FirstTime, !CI). code_info__pickup_zombies(Zombies, !CI) :- code_info__get_zombies(!.CI, Zombies), code_info__set_zombies(set__init, !CI). %---------------------------------------------------------------------------% %---------------------------------------------------------------------------% % Submodule for handling the saving and restoration % of trail tickets, heap pointers, stack pointers etc. :- interface. :- pred code_info__save_hp(code_tree::out, lval::out, code_info::in, code_info::out) is det. :- pred code_info__restore_hp(lval::in, code_tree::out) is det. :- pred code_info__release_hp(lval::in, code_info::in, code_info::out) is det. :- pred code_info__restore_and_release_hp(lval::in, code_tree::out, code_info::in, code_info::out) is det. :- pred code_info__maybe_save_hp(bool::in, code_tree::out, maybe(lval)::out, code_info::in, code_info::out) is det. :- pred code_info__maybe_restore_hp(maybe(lval)::in, code_tree::out) is det. :- pred code_info__maybe_release_hp(maybe(lval)::in, code_info::in, code_info::out) is det. :- pred code_info__maybe_restore_and_release_hp(maybe(lval)::in, code_tree::out, code_info::in, code_info::out) is det. :- pred code_info__save_ticket(code_tree::out, lval::out, code_info::in, code_info::out) is det. :- pred code_info__reset_ticket(lval::in, reset_trail_reason::in, code_tree::out) is det. :- pred code_info__release_ticket(lval::in, code_info::in, code_info::out) is det. :- pred code_info__reset_and_prune_ticket(lval::in, reset_trail_reason::in, code_tree::out) is det. :- pred code_info__reset_prune_and_release_ticket(lval::in, reset_trail_reason::in, code_tree::out, code_info::in, code_info::out) is det. :- pred code_info__reset_and_discard_ticket(lval::in, reset_trail_reason::in, code_tree::out) is det. :- pred code_info__reset_discard_and_release_ticket(lval::in, reset_trail_reason::in, code_tree::out, code_info::in, code_info::out) is det. :- pred code_info__maybe_save_ticket(bool::in, code_tree::out, maybe(lval)::out, code_info::in, code_info::out) is det. :- pred code_info__maybe_reset_ticket(maybe(lval)::in, reset_trail_reason::in, code_tree::out) is det. :- pred code_info__maybe_release_ticket(maybe(lval)::in, code_info::in, code_info::out) is det. :- pred code_info__maybe_reset_and_prune_ticket(maybe(lval)::in, reset_trail_reason::in, code_tree::out) is det. :- pred code_info__maybe_reset_prune_and_release_ticket(maybe(lval)::in, reset_trail_reason::in, code_tree::out, code_info::in, code_info::out) is det. :- pred code_info__maybe_reset_and_discard_ticket(maybe(lval)::in, reset_trail_reason::in, code_tree::out) is det. :- pred code_info__maybe_reset_discard_and_release_ticket(maybe(lval)::in, reset_trail_reason::in, code_tree::out, code_info::in, code_info::out) is det. %---------------------------------------------------------------------------% :- implementation. code_info__save_hp(Code, HpSlot, !CI) :- code_info__acquire_temp_slot(lval(hp), HpSlot, !CI), Code = node([ mark_hp(HpSlot) - "Save heap pointer" ]). code_info__restore_hp(HpSlot, Code) :- Code = node([ restore_hp(lval(HpSlot)) - "Restore heap pointer" ]). code_info__release_hp(HpSlot, !CI) :- code_info__release_temp_slot(HpSlot, !CI). code_info__restore_and_release_hp(HpSlot, Code, !CI) :- code_info__restore_hp(HpSlot, Code), code_info__release_hp(HpSlot, !CI). %---------------------------------------------------------------------------% code_info__maybe_save_hp(Maybe, Code, MaybeHpSlot, !CI) :- ( Maybe = yes -> code_info__save_hp(Code, HpSlot, !CI), MaybeHpSlot = yes(HpSlot) ; Code = empty, MaybeHpSlot = no ). code_info__maybe_restore_hp(MaybeHpSlot, Code) :- ( MaybeHpSlot = yes(HpSlot) -> code_info__restore_hp(HpSlot, Code) ; Code = empty ). code_info__maybe_release_hp(MaybeHpSlot, !CI) :- ( MaybeHpSlot = yes(HpSlot) -> code_info__release_hp(HpSlot, !CI) ; true ). code_info__maybe_restore_and_release_hp(MaybeHpSlot, Code, !CI) :- ( MaybeHpSlot = yes(HpSlot) -> code_info__restore_and_release_hp(HpSlot, Code, !CI) ; Code = empty ). %---------------------------------------------------------------------------% code_info__save_ticket(Code, TicketSlot, !CI) :- code_info__acquire_temp_slot(ticket, TicketSlot, !CI), Code = node([ store_ticket(TicketSlot) - "Save trail state" ]). code_info__reset_ticket(TicketSlot, Reason, Code) :- Code = node([ reset_ticket(lval(TicketSlot), Reason) - "Reset trail" ]). code_info__release_ticket(TicketSlot, !CI) :- code_info__release_temp_slot(TicketSlot, !CI). code_info__reset_and_prune_ticket(TicketSlot, Reason, Code) :- Code = node([ reset_ticket(lval(TicketSlot), Reason) - "Restore trail", prune_ticket - "Prune ticket stack" ]). code_info__reset_prune_and_release_ticket(TicketSlot, Reason, Code, !CI) :- Code = node([ reset_ticket(lval(TicketSlot), Reason) - "Release trail", prune_ticket - "Prune ticket stack" ]), code_info__release_temp_slot(TicketSlot, !CI). code_info__reset_and_discard_ticket(TicketSlot, Reason, Code) :- Code = node([ reset_ticket(lval(TicketSlot), Reason) - "Restore trail", discard_ticket - "Pop ticket stack" ]). code_info__reset_discard_and_release_ticket(TicketSlot, Reason, Code, !CI) :- Code = node([ reset_ticket(lval(TicketSlot), Reason) - "Release trail", discard_ticket - "Pop ticket stack" ]), code_info__release_temp_slot(TicketSlot, !CI). %---------------------------------------------------------------------------% code_info__maybe_save_ticket(Maybe, Code, MaybeTicketSlot, !CI) :- ( Maybe = yes -> code_info__save_ticket(Code, TicketSlot, !CI), MaybeTicketSlot = yes(TicketSlot) ; Code = empty, MaybeTicketSlot = no ). code_info__maybe_reset_ticket(MaybeTicketSlot, Reason, Code) :- ( MaybeTicketSlot = yes(TicketSlot) -> code_info__reset_ticket(TicketSlot, Reason, Code) ; Code = empty ). code_info__maybe_release_ticket(MaybeTicketSlot, !CI) :- ( MaybeTicketSlot = yes(TicketSlot) -> code_info__release_ticket(TicketSlot, !CI) ; true ). code_info__maybe_reset_and_prune_ticket(MaybeTicketSlot, Reason, Code) :- ( MaybeTicketSlot = yes(TicketSlot) -> code_info__reset_and_prune_ticket(TicketSlot, Reason, Code) ; Code = empty ). code_info__maybe_reset_prune_and_release_ticket(MaybeTicketSlot, Reason, Code, !CI) :- ( MaybeTicketSlot = yes(TicketSlot) -> code_info__reset_prune_and_release_ticket(TicketSlot, Reason, Code, !CI) ; Code = empty ). code_info__maybe_reset_and_discard_ticket(MaybeTicketSlot, Reason, Code) :- ( MaybeTicketSlot = yes(TicketSlot) -> code_info__reset_and_discard_ticket(TicketSlot, Reason, Code) ; Code = empty ). code_info__maybe_reset_discard_and_release_ticket(MaybeTicketSlot, Reason, Code, !CI) :- ( MaybeTicketSlot = yes(TicketSlot) -> code_info__reset_discard_and_release_ticket(TicketSlot, Reason, Code, !CI) ; Code = empty ). %---------------------------------------------------------------------------% %---------------------------------------------------------------------------% % Submodule to deal with var_locn. % Most of these procedures just forward to the var_locn module. % See var_locn for documentation. :- interface. :- pred code_info__variable_locations(code_info::in, map(prog_var, set(lval))::out) is det. :- pred code_info__set_var_location(prog_var::in, lval::in, code_info::in, code_info::out) is det. :- pred code_info__assign_var_to_var(prog_var::in, prog_var::in, code_info::in, code_info::out) is det. :- pred code_info__assign_lval_to_var(prog_var::in, lval::in, code_tree::out, code_info::in, code_info::out) is det. :- pred code_info__assign_const_to_var(prog_var::in, rval::in, code_info::in, code_info::out) is det. :- pred code_info__assign_expr_to_var(prog_var::in, rval::in, code_tree::out, code_info::in, code_info::out) is det. % code_info__assign_cell_to_var(Var, ReserveWordAtStart, Ptag, Vector, % Size, TypeMsg, Code, !CI): :- pred code_info__assign_cell_to_var(prog_var::in, bool::in, tag::in, list(maybe(rval))::in, maybe(term_size_value)::in, string::in, code_tree::out, code_info::in, code_info::out) is det. :- pred code_info__place_var(prog_var::in, lval::in, code_tree::out, code_info::in, code_info::out) is det. :- pred code_info__produce_variable(prog_var::in, code_tree::out, rval::out, code_info::in, code_info::out) is det. :- pred code_info__produce_variable_in_reg(prog_var::in, code_tree::out, lval::out, code_info::in, code_info::out) is det. :- pred code_info__produce_variable_in_reg_or_stack(prog_var::in, code_tree::out, lval::out, code_info::in, code_info::out) is det. :- pred code_info__materialize_vars_in_rval(rval::in, rval::out, code_tree::out, code_info::in, code_info::out) is det. :- pred code_info__acquire_reg_for_var(prog_var::in, lval::out, code_info::in, code_info::out) is det. :- pred code_info__acquire_reg(reg_type::in, lval::out, code_info::in, code_info::out) is det. :- pred code_info__release_reg(lval::in, code_info::in, code_info::out) is det. :- pred code_info__reserve_r1(code_tree::out, code_info::in, code_info::out) is det. :- pred code_info__clear_r1(code_tree::out, code_info::in, code_info::out) is det. :- type call_direction ---> caller ; callee. % Move variables to where they need to be at the time of the call: % % - The variables that need to be saved across the call (either because % they are forward live after the call or because they are protected % by an enclosing resumption point) will be saved on the stack. % Note that if the call cannot succeed and the trace level is none, % then no variables need to be saved across the call. (If the call % cannot succeed but the trace level is not none, then we still % save the usual variables on the stack to make them available % for up-level printing in the debugger.) % % - The input arguments will be moved to their registers. :- pred code_info__setup_call(hlds_goal_info::in, assoc_list(prog_var, arg_info)::in, set(lval)::out, code_tree::out, code_info::in, code_info::out) is det. % Move the output arguments of the current procedure to where % they need to be at return. :- pred code_info__setup_return(assoc_list(prog_var, arg_info)::in, set(lval)::out, code_tree::out, code_info::in, code_info::out) is det. :- pred code_info__lock_regs(int::in, assoc_list(prog_var, lval)::in, code_info::in, code_info::out) is det. :- pred code_info__unlock_regs(code_info::in, code_info::out) is det. % Record the fact that all the registers have been clobbered (as by a % call). If the bool argument is true, then the call cannot return, and % thus it is OK for this action to delete the last record of the state % of a variable. :- pred code_info__clear_all_registers(bool::in, code_info::in, code_info::out) is det. :- pred code_info__clobber_regs(list(lval)::in, code_info::in, code_info::out) is det. :- pred code_info__save_variables(set(prog_var)::in, set(lval)::out, code_tree::out, code_info::in, code_info::out) is det. :- pred code_info__save_variables_on_stack(list(prog_var)::in, code_tree::out, code_info::in, code_info::out) is det. :- pred code_info__max_reg_in_use(code_info::in, int::out) is det. %---------------------------------------------------------------------------% :- implementation. code_info__variable_locations(CI, Lvals) :- code_info__get_var_locn_info(CI, VarLocnInfo), var_locn__get_var_locations(VarLocnInfo, Lvals). :- func code_info__rval_map_to_lval_map(prog_var, set(rval)) = set(lval). code_info__rval_map_to_lval_map(_Var, Rvals) = set__filter_map(code_info__rval_is_lval, Rvals). :- func code_info__rval_is_lval(rval) = lval is semidet. code_info__rval_is_lval(lval(Lval)) = Lval. code_info__set_var_location(Var, Lval, !CI) :- code_info__get_var_locn_info(!.CI, VarLocnInfo0), var_locn__check_and_set_magic_var_location(Var, Lval, VarLocnInfo0, VarLocnInfo), code_info__set_var_locn_info(VarLocnInfo, !CI). code_info__assign_var_to_var(Var, AssignedVar, !CI) :- code_info__get_var_locn_info(!.CI, VarLocnInfo0), var_locn__assign_var_to_var(Var, AssignedVar, VarLocnInfo0, VarLocnInfo), code_info__set_var_locn_info(VarLocnInfo, !CI). code_info__assign_lval_to_var(Var, Lval, Code, !CI) :- code_info__get_var_locn_info(!.CI, VarLocnInfo0), code_info__get_static_cell_info(!.CI, StaticCellInfo), var_locn__assign_lval_to_var(Var, Lval, StaticCellInfo, Code, VarLocnInfo0, VarLocnInfo), code_info__set_var_locn_info(VarLocnInfo, !CI). code_info__assign_const_to_var(Var, ConstRval, !CI) :- code_info__get_var_locn_info(!.CI, VarLocnInfo0), var_locn__assign_const_to_var(Var, ConstRval, VarLocnInfo0, VarLocnInfo), code_info__set_var_locn_info(VarLocnInfo, !CI). code_info__assign_expr_to_var(Var, Rval, Code, !CI) :- code_info__get_var_locn_info(!.CI, VarLocnInfo0), ( code_util__lvals_in_rval(Rval, Lvals), Lvals = [] -> var_locn__assign_expr_to_var(Var, Rval, Code, VarLocnInfo0, VarLocnInfo) ; error("code_info__assign_expr_to_var: non-var lvals") ), code_info__set_var_locn_info(VarLocnInfo, !CI). code_info__assign_cell_to_var(Var, ReserveWordAtStart, Ptag, Vector, Size, TypeMsg, Code, !CI) :- code_info__get_var_locn_info(!.CI, VarLocnInfo0), code_info__get_static_cell_info(!.CI, StaticCellInfo0), var_locn__assign_cell_to_var(Var, ReserveWordAtStart, Ptag, Vector, Size, TypeMsg, Code, StaticCellInfo0, StaticCellInfo, VarLocnInfo0, VarLocnInfo), code_info__set_static_cell_info(StaticCellInfo, !CI), code_info__set_var_locn_info(VarLocnInfo, !CI). code_info__place_var(Var, Lval, Code, !CI) :- code_info__get_var_locn_info(!.CI, VarLocnInfo0), var_locn__place_var(Var, Lval, Code, VarLocnInfo0, VarLocnInfo), code_info__set_var_locn_info(VarLocnInfo, !CI). :- pred code_info__pick_and_place_vars(assoc_list(prog_var, set(lval))::in, set(lval)::out, code_tree::out, code_info::in, code_info::out) is det. code_info__pick_and_place_vars(VarLocSets, LiveLocs, Code, !CI) :- code_info__pick_var_places(VarLocSets, VarLocs), assoc_list__values(VarLocs, Locs), set__list_to_set(Locs, LiveLocs), code_info__place_vars(VarLocs, Code, !CI). :- pred code_info__pick_var_places(assoc_list(prog_var, set(lval))::in, assoc_list(prog_var, lval)::out) is det. code_info__pick_var_places([], []). code_info__pick_var_places([Var - LvalSet | VarLvalSets], VarLvals) :- code_info__pick_var_places(VarLvalSets, VarLvals0), ( set__to_sorted_list(LvalSet, LvalList), LvalList = [Lval | _] -> VarLvals = [Var - Lval | VarLvals0] ; VarLvals = VarLvals0 ). :- pred code_info__place_vars(assoc_list(prog_var, lval)::in, code_tree::out, code_info::in, code_info::out) is det. code_info__place_vars(VarLocs, Code, !CI) :- code_info__get_var_locn_info(!.CI, VarLocnInfo0), var_locn__place_vars(VarLocs, Code, VarLocnInfo0, VarLocnInfo), code_info__set_var_locn_info(VarLocnInfo, !CI). code_info__produce_variable(Var, Code, Rval, !CI) :- code_info__get_var_locn_info(!.CI, VarLocnInfo0), var_locn__produce_var(Var, Rval, Code, VarLocnInfo0, VarLocnInfo), code_info__set_var_locn_info(VarLocnInfo, !CI). code_info__produce_variable_in_reg(Var, Code, Lval, !CI) :- code_info__get_var_locn_info(!.CI, VarLocnInfo0), var_locn__produce_var_in_reg(Var, Lval, Code, VarLocnInfo0, VarLocnInfo), code_info__set_var_locn_info(VarLocnInfo, !CI). code_info__produce_variable_in_reg_or_stack(Var, Code, Lval, !CI) :- code_info__get_var_locn_info(!.CI, VarLocnInfo0), var_locn__produce_var_in_reg_or_stack(Var, Lval, Code, VarLocnInfo0, VarLocnInfo), code_info__set_var_locn_info(VarLocnInfo, !CI). code_info__materialize_vars_in_rval(Rval0, Rval, Code, !CI) :- code_info__get_var_locn_info(!.CI, VarLocnInfo0), ( Rval0 = lval(Lval0) -> var_locn__materialize_vars_in_lval(Lval0, Lval, Code, VarLocnInfo0, VarLocnInfo), Rval = lval(Lval) ; exprn_aux__vars_in_rval(Rval0, []) -> Rval = Rval0, Code = empty, VarLocnInfo = VarLocnInfo0 ; error("code_info__materialize_vars_in_rval") ), code_info__set_var_locn_info(VarLocnInfo, !CI). code_info__acquire_reg_for_var(Var, Lval, !CI) :- code_info__get_follow_var_map(!.CI, FollowVarsMap), code_info__get_next_non_reserved(!.CI, NextNonReserved), code_info__get_var_locn_info(!.CI, VarLocnInfo0), ( map__search(FollowVarsMap, Var, PrefLval), PrefLval = reg(PrefRegType, PrefRegNum), PrefRegNum >= 1 -> require(unify(PrefRegType, r), "acquire non-r reg"), var_locn__acquire_reg_prefer_given(PrefRegNum, Lval, VarLocnInfo0, VarLocnInfo) ; % XXX We should only get a register if the map__search % succeeded; otherwise we should put the var in its stack slot. var_locn__acquire_reg_start_at_given(NextNonReserved, Lval, VarLocnInfo0, VarLocnInfo) ), code_info__set_var_locn_info(VarLocnInfo, !CI). code_info__acquire_reg(Type, Lval, !CI) :- code_info__get_var_locn_info(!.CI, VarLocnInfo0), require(unify(Type, r), "code_info__acquire_reg: unknown reg type"), var_locn__acquire_reg(Lval, VarLocnInfo0, VarLocnInfo), code_info__set_var_locn_info(VarLocnInfo, !CI). code_info__release_reg(Lval, !CI) :- code_info__get_var_locn_info(!.CI, VarLocnInfo0), var_locn__release_reg(Lval, VarLocnInfo0, VarLocnInfo), code_info__set_var_locn_info(VarLocnInfo, !CI). code_info__reserve_r1(Code, !CI) :- code_info__get_var_locn_info(!.CI, VarLocnInfo0), var_locn__clear_r1(Code, VarLocnInfo0, VarLocnInfo1), var_locn__acquire_reg_require_given(reg(r, 1), VarLocnInfo1, VarLocnInfo), code_info__set_var_locn_info(VarLocnInfo, !CI). code_info__clear_r1(empty, !CI) :- code_info__get_var_locn_info(!.CI, VarLocnInfo0), var_locn__release_reg(reg(r, 1), VarLocnInfo0, VarLocnInfo), code_info__set_var_locn_info(VarLocnInfo, !CI). %---------------------------------------------------------------------------% code_info__setup_return(VarArgInfos, OutLocs, Code, !CI) :- code_info__setup_call_args(VarArgInfos, callee, OutLocs, Code, !CI). code_info__setup_call(GoalInfo, ArgInfos, LiveLocs, Code, !CI) :- partition_args(ArgInfos, InArgInfos, OutArgInfos, _UnusedArgInfos), assoc_list__keys(OutArgInfos, OutVars), set__list_to_set(OutVars, OutVarSet), goal_info_get_determinism(GoalInfo, Detism), code_info__get_opt_no_return_calls(!.CI, OptNoReturnCalls), ( Detism = erroneous, OptNoReturnCalls = yes -> StackVarLocs = [] ; code_info__compute_forward_live_var_saves(!.CI, OutVarSet, ForwardVarLocs), goal_info_get_code_model(GoalInfo, CodeModel), ( CodeModel = model_non -> % Save variables protected by the nearest % resumption point on the stack. XXX This % should be unnecessary; with the current % setup, the code that established the resume % point should have saved those variables % on the stack already. However, later we % should arrange things so that this saving % of the resume vars on the stack is delayed % until the first call after the setup of % the resume point. code_info__compute_resume_var_stack_locs(!.CI, ResumeVarLocs), list__append(ResumeVarLocs, ForwardVarLocs, StackVarLocs) ; StackVarLocs = ForwardVarLocs ) ), code_info__get_var_locn_info(!.CI, VarLocnInfo0), code_info__var_arg_info_to_lval(InArgInfos, InArgLocs), list__append(StackVarLocs, InArgLocs, AllLocs), var_locn__place_vars(AllLocs, Code, VarLocnInfo0, VarLocnInfo), code_info__set_var_locn_info(VarLocnInfo, !CI), assoc_list__values(AllLocs, LiveLocList), set__list_to_set(LiveLocList, LiveLocs). :- pred code_info__setup_call_args(assoc_list(prog_var, arg_info)::in, call_direction::in, set(lval)::out, code_tree::out, code_info::in, code_info::out) is det. code_info__setup_call_args(AllArgsInfos, Direction, LiveLocs, Code, !CI) :- list__filter(code_info__call_arg_in_selected_dir(Direction), AllArgsInfos, ArgsInfos), code_info__var_arg_info_to_lval(ArgsInfos, ArgsLocns), code_info__get_var_locn_info(!.CI, VarLocnInfo0), var_locn__place_vars(ArgsLocns, Code, VarLocnInfo0, VarLocnInfo1), code_info__set_var_locn_info(VarLocnInfo1, !CI), assoc_list__values(ArgsLocns, LiveLocList), set__list_to_set(LiveLocList, LiveLocs), assoc_list__keys(ArgsLocns, ArgVars), code_info__which_variables_are_forward_live(!.CI, ArgVars, set__init, DeadVars), code_info__make_vars_forward_dead(DeadVars, !CI). :- pred code_info__var_arg_info_to_lval(assoc_list(prog_var, arg_info)::in, assoc_list(prog_var, lval)::out) is det. code_info__var_arg_info_to_lval([], []). code_info__var_arg_info_to_lval([Var - ArgInfo | RestInfos], [Var - Lval | RestLvals]) :- ArgInfo = arg_info(Loc, _Mode), code_util__arg_loc_to_register(Loc, Lval), code_info__var_arg_info_to_lval(RestInfos, RestLvals). :- pred code_info__which_variables_are_forward_live(code_info::in, list(prog_var)::in, set(prog_var)::in, set(prog_var)::out) is det. code_info__which_variables_are_forward_live(_, [], !DeadVars). code_info__which_variables_are_forward_live(CI, [Var | Vars], !DeadVars) :- ( code_info__variable_is_forward_live(CI, Var) -> true ; set__insert(!.DeadVars, Var, !:DeadVars) ), code_info__which_variables_are_forward_live(CI, Vars, !DeadVars). :- pred code_info__call_arg_in_selected_dir(call_direction::in, pair(prog_var, arg_info)::in) is semidet. code_info__call_arg_in_selected_dir(Direction, _ - arg_info(_, Mode)) :- ( Mode = top_in, Direction = caller ; Mode = top_out, Direction = callee ). code_info__lock_regs(N, Exceptions, !CI) :- code_info__get_var_locn_info(!.CI, VarLocnInfo0), var_locn__lock_regs(N, Exceptions, VarLocnInfo0, VarLocnInfo), code_info__set_var_locn_info(VarLocnInfo, !CI). code_info__unlock_regs(!CI) :- code_info__get_var_locn_info(!.CI, VarLocnInfo0), var_locn__unlock_regs(VarLocnInfo0, VarLocnInfo), code_info__set_var_locn_info(VarLocnInfo, !CI). code_info__clear_all_registers(OkToDeleteAny, !CI) :- code_info__get_var_locn_info(!.CI, VarLocnInfo0), var_locn__clobber_all_regs(OkToDeleteAny, VarLocnInfo0, VarLocnInfo), code_info__set_var_locn_info(VarLocnInfo, !CI). code_info__clobber_regs(Regs, !CI) :- code_info__get_var_locn_info(!.CI, VarLocnInfo0), var_locn__clobber_regs(Regs, VarLocnInfo0, VarLocnInfo), code_info__set_var_locn_info(VarLocnInfo, !CI). code_info__save_variables(OutArgs, SavedLocs, Code, !CI) :- code_info__compute_forward_live_var_saves(!.CI, OutArgs, VarLocs), assoc_list__values(VarLocs, SavedLocList), set__list_to_set(SavedLocList, SavedLocs), code_info__place_vars(VarLocs, Code, !CI). code_info__save_variables_on_stack(Vars, Code, !CI) :- list__map(code_info__associate_stack_slot(!.CI), Vars, VarLocs), code_info__place_vars(VarLocs, Code, !CI). :- pred code_info__compute_forward_live_var_saves(code_info::in, set(prog_var)::in, assoc_list(prog_var, lval)::out) is det. code_info__compute_forward_live_var_saves(CI, OutArgs, VarLocs) :- code_info__get_known_variables(CI, Variables0), set__list_to_set(Variables0, Vars0), TypeInfoLiveness = code_info__body_typeinfo_liveness(CI), code_info__get_proc_info(CI, ProcInfo), proc_info_vartypes(ProcInfo, VarTypes), proc_info_typeinfo_varmap(ProcInfo, TVarMap), proc_info_maybe_complete_with_typeinfo_vars(Vars0, TypeInfoLiveness, VarTypes, TVarMap, Vars1), set__difference(Vars1, OutArgs, Vars), set__to_sorted_list(Vars, Variables), list__map(code_info__associate_stack_slot(CI), Variables, VarLocs). :- pred code_info__associate_stack_slot(code_info::in, prog_var::in, pair(prog_var, lval)::out) is det. code_info__associate_stack_slot(CI, Var, Var - Slot) :- code_info__get_variable_slot(CI, Var, Slot). code_info__max_reg_in_use(CI, Max) :- code_info__get_var_locn_info(CI, VarLocnInfo), var_locn__max_reg_in_use(VarLocnInfo, Max). %---------------------------------------------------------------------------% %---------------------------------------------------------------------------% % Submodule for dealing with the recording of variable liveness % information around calls. % % Value numbering needs to know what locations are live before calls; % the garbage collector and the debugger need to know what locations % are live containing what types of values after calls. :- interface. :- pred code_info__generate_call_vn_livevals(code_info::in, list(arg_loc)::in, set(prog_var)::in, set(lval)::out) is det. :- pred code_info__generate_return_live_lvalues(code_info::in, assoc_list(prog_var, arg_loc)::in, instmap::in, bool::in, list(liveinfo)::out) is det. %---------------------------------------------------------------------------% :- implementation. code_info__generate_call_vn_livevals(CI, InputArgLocs, OutputArgs, LiveVals) :- code_info__generate_call_stack_vn_livevals(CI, OutputArgs, StackLiveVals), code_info__generate_input_var_vn(InputArgLocs, StackLiveVals, LiveVals). :- pred code_info__generate_call_stack_vn_livevals(code_info::in, set(prog_var)::in, set(lval)::out) is det. code_info__generate_call_stack_vn_livevals(CI, OutputArgs, LiveVals) :- code_info__get_known_variables(CI, KnownVarList), set__list_to_set(KnownVarList, KnownVars), set__difference(KnownVars, OutputArgs, LiveVars), set__to_sorted_list(LiveVars, LiveVarList), code_info__generate_stack_var_vn(CI, LiveVarList, set__init, LiveVals1), code_info__get_active_temps_data(CI, Temps), code_info__generate_call_temp_vn(Temps, LiveVals1, LiveVals). :- pred code_info__generate_stack_var_vn(code_info::in, list(prog_var)::in, set(lval)::in, set(lval)::out) is det. code_info__generate_stack_var_vn(_, [], !Vals). code_info__generate_stack_var_vn(CI, [V | Vs], !Vals) :- code_info__get_variable_slot(CI, V, Lval), set__insert(!.Vals, Lval, !:Vals), code_info__generate_stack_var_vn(CI, Vs, !Vals). :- pred code_info__generate_call_temp_vn(assoc_list(lval, slot_contents)::in, set(lval)::in, set(lval)::out) is det. code_info__generate_call_temp_vn([], !Vals). code_info__generate_call_temp_vn([Lval - _ | Temps], !Vals) :- set__insert(!.Vals, Lval, !:Vals), code_info__generate_call_temp_vn(Temps, !Vals). :- pred code_info__generate_input_var_vn(list(arg_loc)::in, set(lval)::in, set(lval)::out) is det. code_info__generate_input_var_vn([], !Vals). code_info__generate_input_var_vn([InputArgLoc | InputArgLocs], !Vals) :- code_util__arg_loc_to_register(InputArgLoc, Lval), set__insert(!.Vals, Lval, !:Vals), code_info__generate_input_var_vn(InputArgLocs, !Vals). %---------------------------------------------------------------------------% code_info__generate_return_live_lvalues(CI, OutputArgLocs, ReturnInstMap, OkToDeleteAny, LiveLvalues) :- code_info__variable_locations(CI, VarLocs), code_info__get_known_variables(CI, Vars), code_info__get_active_temps_data(CI, Temps), code_info__get_proc_info(CI, ProcInfo), code_info__get_globals(CI, Globals), code_info__get_module_info(CI, ModuleInfo), continuation_info__generate_return_live_lvalues(OutputArgLocs, ReturnInstMap, Vars, VarLocs, Temps, ProcInfo, ModuleInfo, Globals, OkToDeleteAny, LiveLvalues). :- pred code_info__generate_resume_layout(label::in, resume_map::in, code_info::in, code_info::out) is det. code_info__generate_resume_layout(Label, ResumeMap, !CI) :- code_info__get_globals(!.CI, Globals), globals__lookup_bool_option(Globals, agc_stack_layout, AgcStackLayout), ( AgcStackLayout = yes -> code_info__get_active_temps_data(!.CI, Temps), code_info__get_instmap(!.CI, InstMap), code_info__get_proc_info(!.CI, ProcInfo), code_info__get_module_info(!.CI, ModuleInfo), continuation_info__generate_resume_layout(ResumeMap, Temps, InstMap, ProcInfo, ModuleInfo, Layout), code_info__add_resume_layout_for_label(Label, Layout, !CI) ; true ). %---------------------------------------------------------------------------% %---------------------------------------------------------------------------% % Submodule for managing stack slots. % Det stack frames are organized as follows. % % ... unused ... % sp ---> % % ... local vars ... % % % ... temporaries ... % % % % The stack pointer points to the first free location at the % top of the stack. % % `code_info__succip_is_used' determines whether we need a slot to % hold the succip. % % Nondet stack frames also have the local variables above the % temporaries, but contain several fixed slots on top, and the % saved succip is stored in one of these. % % For both kinds of stack frames, the slots holding variables % are allocated during the live_vars pass, while the slots holding % temporaries are acquired (and if possible, released) on demand % during code generation. :- interface. % Returns the total stackslot count, but not including space for % succip. This total can change in the future if this call is % followed by further allocations of temp slots. :- pred code_info__get_total_stackslot_count(code_info::in, int::out) is det. % Acquire a stack slot for storing a temporary. The slot_contents % description is for accurate gc. :- pred code_info__acquire_temp_slot(slot_contents::in, lval::out, code_info::in, code_info::out) is det. % Release a stack slot acquired earlier for a temporary value. :- pred code_info__release_temp_slot(lval::in, code_info::in, code_info::out) is det. % Return the lval of the stack slot in which the given variable % is stored. Aborts if the variable does not have a stack slot % an assigned to it. :- pred code_info__get_variable_slot(code_info::in, prog_var::in, lval::out) is det. %---------------------------------------------------------------------------% %---------------------------------------------------------------------------% :- implementation. code_info__acquire_temp_slot(Item, StackVar, !CI) :- code_info__get_temps_in_use(!.CI, TempsInUse0), IsTempUsable = (pred(TempContent::in, Lval::out) is semidet :- TempContent = Lval - ContentType, ContentType = Item, \+ set__member(Lval, TempsInUse0) ), code_info__get_temp_content_map(!.CI, TempContentMap0), map__to_assoc_list(TempContentMap0, TempContentList), list__filter_map(IsTempUsable, TempContentList, UsableLvals), ( UsableLvals = [UsableLval | _], StackVar = UsableLval ; UsableLvals = [], code_info__get_var_slot_count(!.CI, VarSlots), code_info__get_max_temp_slot_count(!.CI, TempSlots0), TempSlots = TempSlots0 + 1, Slot = VarSlots + TempSlots, code_info__stack_variable(!.CI, Slot, StackVar), code_info__set_max_temp_slot_count(TempSlots, !CI), map__det_insert(TempContentMap0, StackVar, Item, TempContentMap), code_info__set_temp_content_map(TempContentMap, !CI) ), set__insert(TempsInUse0, StackVar, TempsInUse), code_info__set_temps_in_use(TempsInUse, !CI). code_info__release_temp_slot(StackVar, !CI) :- code_info__get_temps_in_use(!.CI, TempsInUse0), set__delete(TempsInUse0, StackVar, TempsInUse), code_info__set_temps_in_use(TempsInUse, !CI). %---------------------------------------------------------------------------% code_info__get_variable_slot(CI, Var, Slot) :- code_info__get_stack_slots(CI, StackSlots), ( map__search(StackSlots, Var, SlotPrime) -> Slot = SlotPrime ; Name = code_info__variable_to_string(CI, Var), term__var_to_int(Var, Num), string__int_to_string(Num, NumStr), string__append_list([ "code_info__get_variable_slot: variable `", Name, "' (", NumStr, ") not found"], Str), error(Str) ). code_info__get_total_stackslot_count(CI, NumSlots) :- code_info__get_var_slot_count(CI, SlotsForVars), code_info__get_max_temp_slot_count(CI, SlotsForTemps), NumSlots = SlotsForVars + SlotsForTemps. :- pred code_info__max_var_slot(stack_slots::in, int::out) is det. code_info__max_var_slot(StackSlots, SlotCount) :- map__values(StackSlots, StackSlotList), code_info__max_var_slot_2(StackSlotList, 0, SlotCount). :- pred code_info__max_var_slot_2(list(lval)::in, int::in, int::out) is det. code_info__max_var_slot_2([], Max, Max). code_info__max_var_slot_2([L | Ls], Max0, Max) :- ( L = stackvar(N) -> int__max(N, Max0, Max1) ; L = framevar(N) -> int__max(N, Max0, Max1) ; Max1 = Max0 ), code_info__max_var_slot_2(Ls, Max1, Max). :- pred code_info__stack_variable(code_info::in, int::in, lval::out) is det. code_info__stack_variable(CI, Num, Lval) :- ( code_info__get_proc_model(CI) = model_non -> Lval = framevar(Num) ; Lval = stackvar(Num) ). :- pred code_info__stack_variable_reference(code_info::in, int::in, rval::out) is det. code_info__stack_variable_reference(CI, Num, mem_addr(Ref)) :- ( code_info__get_proc_model(CI) = model_non -> Ref = framevar_ref(Num) ; Ref = stackvar_ref(Num) ). %---------------------------------------------------------------------------% %---------------------------------------------------------------------------%