%---------------------------------------------------------------------------% % vim: ft=mercury ts=4 sw=4 et %---------------------------------------------------------------------------% % Copyright (C) 1994-2012 The University of Melbourne. % This file may only be copied under the terms of the GNU General % Public License - see the file COPYING in the Mercury distribution. %---------------------------------------------------------------------------% % % File: 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 ten 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 % - support for debugging the code generator itself. % %---------------------------------------------------------------------------% :- module ll_backend.code_info. :- interface. :- import_module check_hlds.type_util. :- import_module hlds.code_model. :- 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 ll_backend.continuation_info. :- import_module ll_backend.global_data. :- import_module ll_backend.layout. :- import_module ll_backend.llds. :- import_module ll_backend.trace_gen. :- import_module mdbcomp.prim_data. :- import_module mdbcomp.goal_path. :- import_module parse_tree.prog_data. :- import_module parse_tree.set_of_var. :- import_module assoc_list. :- import_module bool. :- import_module counter. :- import_module io. :- import_module list. :- import_module map. :- import_module maybe. :- import_module set. :- import_module set_tree234. :- import_module term. %----------------------------------------------------------------------------% %----------------------------------------------------------------------------% :- implementation. :- import_module backend_libs.builtin_ops. :- import_module backend_libs.proc_label. :- import_module hlds.arg_info. :- import_module hlds.hlds_desc. :- import_module hlds.hlds_rtti. :- import_module libs.options. :- import_module libs.trace_params. :- import_module ll_backend.code_util. :- import_module ll_backend.opt_debug. :- import_module ll_backend.var_locn. :- import_module parse_tree.builtin_lib_types. :- import_module parse_tree.prog_type. :- import_module parse_tree.mercury_to_mercury. :- import_module cord. :- import_module int. :- import_module pair. :- import_module require. :- import_module set. :- import_module stack. :- import_module string. :- import_module varset. %---------------------------------------------------------------------------% % 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, abs_follow_vars::in, module_info::in, static_cell_info::in, const_struct_map::in, resume_point_info::out, trace_slot_info::out, maybe(containing_goal_map)::in, list(string)::in, int::in, code_info::out) is det. % Get the globals table. % :- pred get_globals(code_info::in, globals::out) is det. % Get the exprn_opts. % :- pred get_exprn_opts(code_info::in, exprn_opts::out) is det. % Get the HLDS of the entire module. % :- pred get_module_info(code_info::in, module_info::out) is det. % Get the id of the predicate we are generating code for. % :- pred get_pred_id(code_info::in, pred_id::out) is det. % Get the id of the procedure we are generating code for. % :- pred get_proc_id(code_info::in, proc_id::out) is det. % Get the HLDS of the predicate containing the procedure % we are generating code for. % :- pred get_pred_info(code_info::in, pred_info::out) is det. % Get the HLDS of the procedure we are generating code for. % :- pred get_proc_info(code_info::in, proc_info::out) is det. % Get the variables for the current procedure. % :- pred get_varset(code_info::in, prog_varset::out) is det. :- func get_var_types(code_info) = vartypes. :- pred get_maybe_trace_info(code_info::in, maybe(trace_info)::out) is det. :- pred get_emit_trail_ops(code_info::in, add_trail_ops::out) is det. :- pred get_emit_region_ops(code_info::in, add_region_ops::out) is det. % Get the set of currently forward-live variables. % :- pred get_forward_live_vars(code_info::in, set_of_progvar::out) is det. % Set the set of currently forward-live variables. % :- pred set_forward_live_vars(set_of_progvar::in, code_info::in, code_info::out) is det. % Get the table mapping variables to the current % instantiation states. % :- pred get_instmap(code_info::in, instmap::out) is det. % Set the table mapping variables to the current % instantiation states. % :- pred set_instmap(instmap::in, code_info::in, code_info::out) is det. % Get the current nesting depth for parallel conjunctions. % :- pred get_par_conj_depth(code_info::in, int::out) is det. % Set the current nesting depth for parallel conjunctions. % :- pred set_par_conj_depth(int::in, code_info::in, code_info::out) is det. % The number of the last local label allocated. % :- pred get_label_counter(code_info::in, counter::out) is det. % Get the flag that indicates whether succip is used or not. % :- pred get_succip_used(code_info::in, bool::out) is det. % Get the label layout information created by tracing % during code generation. % :- pred get_layout_info(code_info::in, proc_label_layout_info::out) is det. :- pred get_proc_trace_events(code_info::in, bool::out) is det. :- pred set_proc_trace_events(bool::in, code_info::in, code_info::out) is det. % Get the global static data structures that have % been created during code generation for closure layouts. % :- pred get_closure_layouts(code_info::in, list(closure_proc_id_data)::out) is det. :- pred get_max_reg_in_use_at_trace(code_info::in, int::out, int::out) is det. :- pred set_max_reg_in_use_at_trace(int::in, 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 get_created_temp_frame(code_info::in, bool::out) is det. :- pred get_static_cell_info(code_info::in, static_cell_info::out) is det. :- pred set_static_cell_info(static_cell_info::in, code_info::in, code_info::out) is det. :- pred get_alloc_sites(code_info::in, set_tree234(alloc_site_info)::out) is det. :- pred set_alloc_sites(set_tree234(alloc_site_info)::in, code_info::in, code_info::out) is det. :- pred get_used_env_vars(code_info::in, set(string)::out) is det. :- pred set_used_env_vars(set(string)::in, code_info::in, code_info::out) is det. :- pred get_opt_trail_ops(code_info::in, bool::out) is det. :- pred get_opt_region_ops(code_info::in, bool::out) is det. :- pred get_auto_comments(code_info::in, bool::out) is det. :- pred get_lcmc_null(code_info::in, bool::out) is det. :- pred get_containing_goal_map(code_info::in, maybe(containing_goal_map)::out) is det. :- pred get_containing_goal_map_det(code_info::in, containing_goal_map::out) is det. :- pred get_const_struct_map(code_info::in, const_struct_map::out) is det. :- pred add_out_of_line_code(llds_code::in, code_info::in, code_info::out) is det. :- pred get_out_of_line_code(code_info::in, llds_code::out) is det. %---------------------------------------------------------------------------% :- implementation. :- pred get_var_slot_count(code_info::in, int::out) is det. :- pred set_maybe_trace_info(maybe(trace_info)::in, code_info::in, code_info::out) is det. :- pred get_opt_no_return_calls(code_info::in, bool::out) is det. :- pred get_zombies(code_info::in, set_of_progvar::out) is det. :- pred set_zombies(set_of_progvar::in, code_info::in, code_info::out) is det. :- pred get_var_locn_info(code_info::in, var_locn_info::out) is det. :- pred set_var_locn_info(var_locn_info::in, code_info::in, code_info::out) is det. :- pred get_temps_in_use(code_info::in, set(lval)::out) is det. :- pred set_temps_in_use(set(lval)::in, code_info::in, code_info::out) is det. :- pred get_fail_info(code_info::in, fail_info::out) is det. :- pred set_fail_info(fail_info::in, code_info::in, code_info::out) is det. :- pred set_label_counter(counter::in, code_info::in, code_info::out) is det. :- pred set_succip_used(bool::in, code_info::in, code_info::out) is det. :- pred set_layout_info(proc_label_layout_info::in, code_info::in, code_info::out) is det. :- pred get_max_temp_slot_count(code_info::in, int::out) is det. :- pred set_max_temp_slot_count(int::in, code_info::in, code_info::out) is det. :- pred get_temp_content_map(code_info::in, map(lval, slot_contents)::out) is det. :- pred set_temp_content_map(map(lval, slot_contents)::in, code_info::in, code_info::out) is det. :- pred get_persistent_temps(code_info::in, set(lval)::out) is det. :- pred set_persistent_temps(set(lval)::in, code_info::in, code_info::out) is det. :- pred set_closure_layouts(list(closure_proc_id_data)::in, code_info::in, code_info::out) is det. :- pred get_closure_seq_counter(code_info::in, counter::out) is det. :- pred set_closure_seq_counter(counter::in, code_info::in, code_info::out) is det. :- pred 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( % For the code generation options. cis_globals :: globals, cis_exprn_opts :: exprn_opts, % The module_info structure - you just never know % when you might need it. cis_module_info :: module_info, % The id of the current predicate. cis_pred_id :: pred_id, % The id of the current procedure. cis_proc_id :: proc_id, % The pred_info for the predicate containing this procedure. cis_pred_info :: pred_info, % The proc_info for this procedure. cis_proc_info :: proc_info, % The proc_label for this procedure. cis_proc_label :: proc_label, % The variables in this procedure. cis_varset :: prog_varset, % The number of stack slots allocated. for storing variables. % (Some extra stack slots are used for saving and restoring % registers.) cis_var_slot_count :: int, % Information about which stack slots the call sequence number % and depth are stored in, provided tracing is switched on. cis_maybe_trace_info :: maybe(trace_info), % Should we optimize calls that cannot return? cis_opt_no_resume_calls :: bool, % Should we emit trail operations? cis_emit_trail_ops :: add_trail_ops, % Should we try to avoid generating trail operations? cis_opt_trail_ops :: bool, % Should we emit region operations? cis_emit_region_ops :: add_region_ops, % Should we try to avoid generating region operations? cis_opt_region_ops :: bool, % The setting of --auto-comments. cis_auto_comments :: bool, % The setting of --optimize-constructor-last-call-null. cis_lcmc_null :: bool, cis_containing_goal_map :: maybe(containing_goal_map), % Maps the number of an entry in the module's const_struct_db % to its rval. cis_const_struct_map :: const_struct_map ). :- type code_info_loc_dep ---> code_info_loc_dep( % Variables that are forward live after this goal. cild_forward_live_vars :: set_of_progvar, % Current insts of the live variables. cild_instmap :: instmap, % Zombie variables; variables that are not forward live % but which are protected by an enclosing resume point. cild_zombies :: set_of_progvar, % A map storing the information about the status of each known % variable. (Known vars = forward live vars + zombies.) cild_var_locn_info :: var_locn_info, % 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. cild_temps_in_use :: set(lval), % Information about how to manage failures. cild_fail_info :: fail_info, % How deep in a nested parallel conjunction we are. % This is zero at the beginning of a procedure and % is incremented as we enter parallel conjunctions. cild_par_conj_depth :: int ). :- type code_info_persistent ---> code_info_persistent( % Counter for the local labels used by this procedure. cip_label_num_src :: counter, % do we need to store succip? cip_store_succip :: bool, % Information on which values are live and where at which % labels, for tracing and/or accurate gc. cip_label_info :: proc_label_layout_info, % Did the procedure have any trace events? cip_proc_trace_events :: bool, % The maximum number of extra temporary stackslots that % have been used during the procedure. cip_stackslot_max :: int, % 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. cip_temp_contents :: map(lval, slot_contents), % Stack slot locations that should not be released even when % the code generator resets its location-dependent state. cip_persistent_temps :: set(lval), cip_closure_layout_seq :: counter, % Closure layout structures generated by this procedure. cip_closure_layouts :: list(closure_proc_id_data), % At each call to MR_trace, we compute the highest rN and fN % registers that contain useful values. These slot contain 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. cip_max_reg_r_used :: int, cip_max_reg_f_used :: int, % True iff the procedure has created one or more temporary % nondet frames. cip_created_temp_frame :: bool, cip_static_cell_info :: static_cell_info, cip_alloc_sites :: set_tree234(alloc_site_info), cip_used_env_vars :: set(string), % A counter and table for allocating and maintaining slots % where string IDs will be placed at runtime for threadscope % profiling. The actual string IDs are allocated at runtime % and their IDs are placed in an array slot which can be % referred to statically. cip_ts_string_table_size :: int, cip_ts_rev_string_table :: list(string), % Code that is part of this procedure, but that can be placed % after the procedure without a cache penalty. For example, % code that is spawned off by loop control is placed here. cip_out_of_line_code :: llds_code ). %---------------------------------------------------------------------------% code_info_init(SaveSuccip, Globals, PredId, ProcId, PredInfo, ProcInfo, FollowVars, ModuleInfo, StaticCellInfo, ConstStructMap, ResumePoint, TraceSlotInfo, MaybeContainingGoalMap, TSRevStringTable, TSStringTableSize, CodeInfo) :- ProcLabel = make_proc_label(ModuleInfo, PredId, ProcId), proc_info_get_initial_instmap(ProcInfo, ModuleInfo, InstMap), proc_info_get_liveness_info(ProcInfo, Liveness), CodeModel = proc_info_interface_code_model(ProcInfo), build_input_arg_list(ProcInfo, ArgList), proc_info_get_varset(ProcInfo, VarSet), proc_info_get_vartypes(ProcInfo, VarTypes), proc_info_get_stack_slots(ProcInfo, StackSlots), ExprnOpts = init_exprn_opts(Globals), globals.lookup_bool_option(Globals, use_float_registers, UseFloatRegs), ( UseFloatRegs = yes, FloatRegType = reg_f ; UseFloatRegs = no, FloatRegType = reg_r ), globals.get_trace_level(Globals, TraceLevel), ( eff_trace_level_is_none(ModuleInfo, PredInfo, ProcInfo, TraceLevel) = no -> trace_fail_vars(ModuleInfo, ProcInfo, FailVars), MaybeFailVars = yes(FailVars), set_of_var.union(Liveness, FailVars, EffLiveness) ; MaybeFailVars = no, EffLiveness = Liveness ), init_var_locn_state(ArgList, EffLiveness, VarSet, VarTypes, FloatRegType, StackSlots, FollowVars, 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(PersistentTemps), set.init(TempsInUse), Zombies = set_of_var.init, map.init(LayoutMap), 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), globals.lookup_bool_option(Globals, use_trail, UseTrail), globals.lookup_bool_option(Globals, disable_trail_ops, DisableTrailOps), ( UseTrail = yes, DisableTrailOps = no -> EmitTrailOps = add_trail_ops ; EmitTrailOps = do_not_add_trail_ops ), globals.lookup_bool_option(Globals, optimize_trail_usage, OptTrailOps), globals.lookup_bool_option(Globals, optimize_region_ops, OptRegionOps), globals.lookup_bool_option(Globals, region_analysis, UseRegions), ( UseRegions = yes, EmitRegionOps = add_region_ops ; UseRegions = no, EmitRegionOps = do_not_add_region_ops ), globals.lookup_bool_option(Globals, auto_comments, AutoComments), globals.lookup_bool_option(Globals, optimize_constructor_last_call_null, LCMCNull), CodeInfo0 = code_info( code_info_static( Globals, ExprnOpts, ModuleInfo, PredId, ProcId, PredInfo, ProcInfo, ProcLabel, VarSet, SlotMax, no, OptNoReturnCalls, EmitTrailOps, OptTrailOps, EmitRegionOps, OptRegionOps, AutoComments, LCMCNull, MaybeContainingGoalMap, ConstStructMap ), code_info_loc_dep( Liveness, InstMap, Zombies, VarLocnInfo, TempsInUse, DummyFailInfo, % init_fail_info will override this dummy value 0 % nested parallel conjunction depth ), code_info_persistent( counter.init(1), SaveSuccip, LayoutMap, no, 0, TempContentMap, PersistentTemps, counter.init(1), [], -1, -1, no, StaticCellInfo, set_tree234.init, set.init, TSStringTableSize, TSRevStringTable, cord.empty ) ), init_maybe_trace_info(TraceLevel, Globals, ModuleInfo, PredInfo, ProcInfo, TraceSlotInfo, CodeInfo0, CodeInfo1), init_fail_info(CodeModel, MaybeFailVars, ResumePoint, CodeInfo1, CodeInfo). :- func init_exprn_opts(globals) = exprn_opts. init_exprn_opts(Globals) = ExprnOpts :- globals.lookup_bool_option(Globals, gcc_non_local_gotos, OptNLG), ( OptNLG = yes, NLG = have_non_local_gotos ; OptNLG = no, NLG = do_not_have_non_local_gotos ), globals.lookup_bool_option(Globals, asm_labels, OptASM), ( OptASM = yes, ASM = have_asm_labels ; OptASM = no, ASM = do_not_have_asm_labels ), globals.lookup_bool_option(Globals, static_ground_cells, OptSGCell), ( OptSGCell = yes, SGCell = have_static_ground_cells ; OptSGCell = no, SGCell = do_not_have_static_ground_cells ), globals.lookup_bool_option(Globals, unboxed_float, OptUBF), ( OptUBF = yes, UBF = have_unboxed_floats ; OptUBF = no, UBF = do_not_have_unboxed_floats ), globals.lookup_bool_option(Globals, use_float_registers, OptFloatRegs), ( OptFloatRegs = yes, UseFloatRegs = use_float_registers ; OptFloatRegs = no, UseFloatRegs = do_not_use_float_registers ), globals.lookup_bool_option(Globals, static_ground_floats, OptSGFloat), ( OptSGFloat = yes, SGFloat = have_static_ground_floats ; OptSGFloat = no, SGFloat = do_not_have_static_ground_floats ), globals.lookup_bool_option(Globals, static_code_addresses, OptStaticCodeAddr), ( OptStaticCodeAddr = yes, StaticCodeAddrs = have_static_code_addresses ; OptStaticCodeAddr = no, StaticCodeAddrs = do_not_have_static_code_addresses ), ExprnOpts = exprn_opts(NLG, ASM, UBF, UseFloatRegs, SGCell, SGFloat, StaticCodeAddrs). :- pred 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. init_maybe_trace_info(TraceLevel, Globals, ModuleInfo, PredInfo, ProcInfo, TraceSlotInfo, !CI) :- ( eff_trace_level_is_none(ModuleInfo, PredInfo, ProcInfo, TraceLevel) = no -> proc_info_get_has_tail_call_events(ProcInfo, HasTailCallEvents), ( HasTailCallEvents = tail_call_events, get_next_label(TailRecLabel, !CI), MaybeTailRecLabel = yes(TailRecLabel) ; HasTailCallEvents = no_tail_call_events, MaybeTailRecLabel = no ), trace_setup(ModuleInfo, PredInfo, ProcInfo, Globals, MaybeTailRecLabel, TraceSlotInfo, TraceInfo, !CI), set_maybe_trace_info(yes(TraceInfo), !CI) ; TraceSlotInfo = trace_slot_info(no, no, no, no, no, no) ). %---------------------------------------------------------------------------% get_globals(CI, CI ^ code_info_static ^ cis_globals). get_module_info(CI, CI ^ code_info_static ^ cis_module_info). get_exprn_opts(CI, CI ^ code_info_static ^ cis_exprn_opts). get_pred_id(CI, CI ^ code_info_static ^ cis_pred_id). get_proc_id(CI, CI ^ code_info_static ^ cis_proc_id). get_pred_info(CI, CI ^ code_info_static ^ cis_pred_info). get_proc_info(CI, CI ^ code_info_static ^ cis_proc_info). get_varset(CI, CI ^ code_info_static ^ cis_varset). get_var_slot_count(CI, CI ^ code_info_static ^ cis_var_slot_count). get_maybe_trace_info(CI, CI ^ code_info_static ^ cis_maybe_trace_info). get_opt_no_return_calls(CI, CI ^ code_info_static ^ cis_opt_no_resume_calls). get_emit_trail_ops(CI, CI ^ code_info_static ^ cis_emit_trail_ops). get_opt_trail_ops(CI, CI ^ code_info_static ^ cis_opt_trail_ops). get_emit_region_ops(CI, CI ^ code_info_static ^ cis_emit_region_ops). get_opt_region_ops(CI, CI ^ code_info_static ^ cis_opt_region_ops). get_auto_comments(CI, CI ^ code_info_static ^ cis_auto_comments). get_lcmc_null(CI, CI ^ code_info_static ^ cis_lcmc_null). get_containing_goal_map(CI, CI ^ code_info_static ^ cis_containing_goal_map). get_const_struct_map(CI, CI ^ code_info_static ^ cis_const_struct_map). get_forward_live_vars(CI, CI ^ code_info_loc_dep ^ cild_forward_live_vars). get_instmap(CI, CI ^ code_info_loc_dep ^ cild_instmap). get_zombies(CI, CI ^ code_info_loc_dep ^ cild_zombies). get_var_locn_info(CI, CI ^ code_info_loc_dep ^ cild_var_locn_info). get_temps_in_use(CI, CI ^ code_info_loc_dep ^ cild_temps_in_use). get_fail_info(CI, CI ^ code_info_loc_dep ^ cild_fail_info). get_par_conj_depth(CI, CI ^ code_info_loc_dep ^ cild_par_conj_depth). get_label_counter(CI, CI ^ code_info_persistent ^ cip_label_num_src). get_succip_used(CI, CI ^ code_info_persistent ^ cip_store_succip). get_layout_info(CI, CI ^ code_info_persistent ^ cip_label_info). get_proc_trace_events(CI, CI ^ code_info_persistent ^ cip_proc_trace_events). get_max_temp_slot_count(CI, CI ^ code_info_persistent ^ cip_stackslot_max). get_temp_content_map(CI, CI ^ code_info_persistent ^ cip_temp_contents). get_persistent_temps(CI, CI ^ code_info_persistent ^ cip_persistent_temps). get_closure_seq_counter(CI, CI ^ code_info_persistent ^ cip_closure_layout_seq). get_closure_layouts(CI, CI ^ code_info_persistent ^ cip_closure_layouts). get_max_reg_in_use_at_trace(CI, MaxRegR, MaxRegF) :- MaxRegR = CI ^ code_info_persistent ^ cip_max_reg_r_used, MaxRegF = CI ^ code_info_persistent ^ cip_max_reg_f_used. get_created_temp_frame(CI, CI ^ code_info_persistent ^ cip_created_temp_frame). get_static_cell_info(CI, CI ^ code_info_persistent ^ cip_static_cell_info). get_alloc_sites(CI, CI ^ code_info_persistent ^ cip_alloc_sites). get_used_env_vars(CI, CI ^ code_info_persistent ^ cip_used_env_vars). %---------------------------------------------------------------------------% set_maybe_trace_info(TI, CI, CI ^ code_info_static ^ cis_maybe_trace_info := TI). set_forward_live_vars(LV, CI, CI ^ code_info_loc_dep ^ cild_forward_live_vars := LV). set_instmap(IM, CI, CI ^ code_info_loc_dep ^ cild_instmap := IM). set_zombies(Zs, CI, CI ^ code_info_loc_dep ^ cild_zombies := Zs). set_var_locn_info(EI, CI, CI ^ code_info_loc_dep ^ cild_var_locn_info := EI). set_temps_in_use(TI, CI, CI ^ code_info_loc_dep ^ cild_temps_in_use := TI). set_fail_info(FI, CI, CI ^ code_info_loc_dep ^ cild_fail_info := FI). set_par_conj_depth(N, CI, CI ^ code_info_loc_dep ^ cild_par_conj_depth := N). set_label_counter(LC, CI, CI ^ code_info_persistent ^ cip_label_num_src := LC). set_succip_used(SU, CI, CI ^ code_info_persistent ^ cip_store_succip := SU). set_layout_info(LI, CI, CI ^ code_info_persistent ^ cip_label_info := LI). set_proc_trace_events(PTE, CI, CI ^ code_info_persistent ^ cip_proc_trace_events := PTE). set_max_temp_slot_count(TM, CI, CI ^ code_info_persistent ^ cip_stackslot_max := TM). set_temp_content_map(CM, CI, CI ^ code_info_persistent ^ cip_temp_contents := CM). set_persistent_temps(PT, CI, CI ^ code_info_persistent ^ cip_persistent_temps := PT). set_closure_seq_counter(CLS, CI, CI ^ code_info_persistent ^ cip_closure_layout_seq := CLS). set_closure_layouts(CG, CI, CI ^ code_info_persistent ^ cip_closure_layouts := CG). set_max_reg_in_use_at_trace(MR, MF, !CI) :- !CI ^ code_info_persistent ^ cip_max_reg_r_used := MR, !CI ^ code_info_persistent ^ cip_max_reg_f_used := MF. set_created_temp_frame(MR, CI, CI ^ code_info_persistent ^ cip_created_temp_frame := MR). set_static_cell_info(SCI, CI, CI ^ code_info_persistent ^ cip_static_cell_info := SCI). set_alloc_sites(ASI, CI, CI ^ code_info_persistent ^ cip_alloc_sites := ASI). set_used_env_vars(UEV, CI, CI ^ code_info_persistent ^ cip_used_env_vars := UEV). get_containing_goal_map_det(CI, ContainingGoalMap) :- get_containing_goal_map(CI, MaybeContainingGoalMap), ( MaybeContainingGoalMap = yes(ContainingGoalMap) ; MaybeContainingGoalMap = no, unexpected($module, $pred, "no map") ). add_out_of_line_code(NewCode, !CI) :- Code0 = !.CI ^ code_info_persistent ^ cip_out_of_line_code, Code = Code0 ++ NewCode, !CI ^ code_info_persistent ^ cip_out_of_line_code := Code. get_out_of_line_code(CI, CI ^ code_info_persistent ^ cip_out_of_line_code). %---------------------------------------------------------------------------% %---------------------------------------------------------------------------% % Submodule for simple wrappers around access predicates. :- interface. % Get the hlds mapping from variables to stack slots % :- pred get_stack_slots(code_info::in, stack_slots::out) is det. % Get the table that contains advice about where % variables should be put. % :- pred get_follow_var_map(code_info::in, abs_follow_vars_map::out) is det. % Get the integer that gives the number of the next % non-reserved register. % :- pred get_next_non_reserved(code_info::in, reg_type::in, int::out) is det. % Set the table that contains advice about where % variables should be put. :- pred set_follow_vars(abs_follow_vars::in, code_info::in, code_info::out) is det. % pre_goal_update(GoalInfo, HasSubGoal, OldCodeInfo, NewCodeInfo) % updates OldCodeInfo to produce NewCodeInfo with the changes % specified by GoalInfo. % :- pred pre_goal_update(hlds_goal_info::in, has_subgoals::in, code_info::in, code_info::out) is det. % post_goal_update(GoalInfo, OldCodeInfo, NewCodeInfo) % updates OldCodeInfo to produce NewCodeInfo with the changes described % by GoalInfo. % :- pred 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 body_typeinfo_liveness(code_info) = bool. % Find out the type of the given variable. % :- func variable_type(code_info, prog_var) = mer_type. :- func variable_is_of_dummy_type(code_info, prog_var) = is_dummy_type. % Compute the principal type constructor of the given type, and return the % definition of this type constructor, if it has one (some type % constructors are built in, and some are hidden behind abstraction % barriers). % :- pred search_type_defn(code_info::in, mer_type::in, hlds_type_defn::out) is semidet. % Compute the principal type constructor of the given type, and return the % definition of this type constructor. Abort if it doesn't have a % definition (e.g. because it is a builtin). % :- func lookup_type_defn(code_info, mer_type) = hlds_type_defn. :- func lookup_cheaper_tag_test(code_info, mer_type) = maybe_cheaper_tag_test. :- func filter_region_vars(code_info, set_of_progvar) = set_of_progvar. % Get the code model of the current procedure. % :- func get_proc_model(code_info) = code_model. % Get the list of the head variables of the current procedure. % :- func get_headvars(code_info) = list(prog_var). % Get the call argument information for the current procedure % :- func get_arginfo(code_info) = list(arg_info). % Get the call argument info for a given mode of a given predicate % :- func 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 current_resume_point_vars(code_info) = set_of_progvar. :- func variable_name(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 make_proc_entry_label(code_info, module_info, pred_id, proc_id, bool) = code_addr. % Generate the next local label in sequence. % :- pred 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 succip_is_used(code_info::in, code_info::out) is det. :- pred add_trace_layout_for_label(label::in, term.context::in, trace_port::in, bool::in, forward_goal_path::in, maybe(user_event_info)::in, layout_label_info::in, code_info::in, code_info::out) is det. :- pred get_cur_proc_label(code_info::in, proc_label::out) is det. :- pred get_next_closure_seq_no(int::out, code_info::in, code_info::out) is det. :- pred add_closure_layout(closure_proc_id_data::in, code_info::in, code_info::out) is det. :- pred add_threadscope_string(string::in, int::out, code_info::in, code_info::out) is det. :- pred get_threadscope_rev_string_table(code_info::in, list(string)::out, int::out) is det. :- pred add_scalar_static_cell(list(typed_rval)::in, data_id::out, code_info::in, code_info::out) is det. :- pred add_scalar_static_cell_natural_types(list(rval)::in, data_id::out, code_info::in, code_info::out) is det. :- pred add_vector_static_cell(list(llds_type)::in, list(list(rval))::in, data_id::out, code_info::in, code_info::out) is det. :- pred add_alloc_site_info(prog_context::in, string::in, int::in, alloc_site_id::out, code_info::in, code_info::out) is det. %---------------------------------------------------------------------------% :- implementation. :- pred add_resume_layout_for_label(label::in, layout_label_info::in, code_info::in, code_info::out) is det. get_stack_slots(CI, StackSlots) :- get_var_locn_info(CI, VarLocnInfo), var_locn_get_stack_slots(VarLocnInfo, StackSlots). get_follow_var_map(CI, FollowVarMap) :- get_var_locn_info(CI, VarLocnInfo), var_locn_get_follow_var_map(VarLocnInfo, FollowVarMap). get_next_non_reserved(CI, RegType, NextNonReserved) :- get_var_locn_info(CI, VarLocnInfo), var_locn_get_next_non_reserved(VarLocnInfo, RegType, NextNonReserved). set_follow_vars(FollowVars, !CI) :- get_var_locn_info(!.CI, VarLocnInfo0), var_locn_set_follow_vars(FollowVars, VarLocnInfo0, VarLocnInfo), set_var_locn_info(VarLocnInfo, !CI). %-----------------------------------------------------------------------------% pre_goal_update(GoalInfo, HasSubGoals, !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(_, _), unexpected($module, $pred, "pre_goal_update with resume point") ), goal_info_get_follow_vars(GoalInfo, MaybeFollowVars), ( MaybeFollowVars = yes(FollowVars), set_follow_vars(FollowVars, !CI) ; MaybeFollowVars = no ), % NOTE: we must be careful to apply deaths before births goal_info_get_pre_deaths(GoalInfo, PreDeaths), rem_forward_live_vars(PreDeaths, !CI), maybe_make_vars_forward_dead(PreDeaths, no, !CI), goal_info_get_pre_births(GoalInfo, PreBirths), add_forward_live_vars(PreBirths, !CI), ( HasSubGoals = does_not_have_subgoals, goal_info_get_post_deaths(GoalInfo, PostDeaths), rem_forward_live_vars(PostDeaths, !CI) ; HasSubGoals = has_subgoals ). post_goal_update(GoalInfo, !CI) :- % note: we must be careful to apply deaths before births goal_info_get_post_deaths(GoalInfo, PostDeaths), rem_forward_live_vars(PostDeaths, !CI), maybe_make_vars_forward_dead(PostDeaths, no, !CI), goal_info_get_post_births(GoalInfo, PostBirths), add_forward_live_vars(PostBirths, !CI), make_vars_forward_live(PostBirths, !CI), InstMapDelta = goal_info_get_instmap_delta(GoalInfo), get_instmap(!.CI, InstMap0), instmap.apply_instmap_delta(InstMap0, InstMapDelta, InstMap), set_instmap(InstMap, !CI). %---------------------------------------------------------------------------% body_typeinfo_liveness(CI) = TypeInfoLiveness :- get_module_info(CI, ModuleInfo), get_pred_id(CI, PredId), module_info_pred_info(ModuleInfo, PredId, PredInfo), get_globals(CI, Globals), body_should_use_typeinfo_liveness(PredInfo, Globals, TypeInfoLiveness). get_var_types(CI) = VarTypes :- get_proc_info(CI, ProcInfo), proc_info_get_vartypes(ProcInfo, VarTypes). variable_type(CI, Var) = Type :- lookup_var_type(get_var_types(CI), Var, Type). variable_is_of_dummy_type(CI, Var) = IsDummy :- VarType = variable_type(CI, Var), get_module_info(CI, ModuleInfo), IsDummy = check_dummy_type(ModuleInfo, VarType). search_type_defn(CI, Type, TypeDefn) :- get_module_info(CI, ModuleInfo), type_to_ctor_det(Type, TypeCtor), module_info_get_type_table(ModuleInfo, TypeTable), search_type_ctor_defn(TypeTable, TypeCtor, TypeDefn). lookup_type_defn(CI, Type) = TypeDefn :- ( search_type_defn(CI, Type, TypeDefnPrime) -> TypeDefn = TypeDefnPrime ; unexpected($module, $pred, "type ctor has no definition") ). lookup_cheaper_tag_test(CI, Type) = CheaperTagTest :- ( search_type_defn(CI, Type, TypeDefn), get_type_defn_body(TypeDefn, TypeBody), TypeBody = hlds_du_type(_, _, CheaperTagTestPrime, _, _, _, _, _, _) -> CheaperTagTest = CheaperTagTestPrime ; CheaperTagTest = no_cheaper_tag_test ). filter_region_vars(CI, ForwardLiveVarsBeforeGoal) = RegionVars :- VarTypes = code_info.get_var_types(CI), RegionVars = set_of_var.filter(is_region_var(VarTypes), ForwardLiveVarsBeforeGoal). %---------------------------------------------------------------------------% get_proc_model(CI) = CodeModel :- get_proc_info(CI, ProcInfo), CodeModel = proc_info_interface_code_model(ProcInfo). get_headvars(CI) = HeadVars :- get_module_info(CI, ModuleInfo), get_pred_id(CI, PredId), get_proc_id(CI, ProcId), module_info_pred_proc_info(ModuleInfo, PredId, ProcId, _, ProcInfo), proc_info_get_headvars(ProcInfo, HeadVars). get_arginfo(CI) = ArgInfo :- get_pred_id(CI, PredId), get_proc_id(CI, ProcId), ArgInfo = get_pred_proc_arginfo(CI, PredId, ProcId). get_pred_proc_arginfo(CI, PredId, ProcId) = ArgInfo :- get_module_info(CI, ModuleInfo), module_info_pred_proc_info(ModuleInfo, PredId, ProcId, _, ProcInfo), proc_info_arg_info(ProcInfo, ArgInfo). current_resume_point_vars(CI) = ResumeVars :- get_fail_info(CI, FailInfo), FailInfo = fail_info(ResumePointStack, _, _, _, _), stack.det_top(ResumePointStack, ResumePointInfo), pick_first_resume_point(ResumePointInfo, ResumeMap, _), map.keys(ResumeMap, ResumeMapVarList), ResumeVars = set_of_var.list_to_set(ResumeMapVarList). variable_name(CI, Var) = Name :- get_varset(CI, Varset), varset.lookup_name(Varset, Var, Name). %---------------------------------------------------------------------------% make_proc_entry_label(CI, ModuleInfo, PredId, ProcId, Immed0) = CodeAddr :- ( Immed0 = no, Immed = no ; Immed0 = yes, get_globals(CI, Globals), globals.lookup_int_option(Globals, procs_per_c_function, ProcsPerFunc), get_pred_id(CI, CurPredId), get_proc_id(CI, CurProcId), Immed = yes(ProcsPerFunc - proc(CurPredId, CurProcId)) ), CodeAddr = make_entry_label(ModuleInfo, PredId, ProcId, Immed). get_next_label(Label, !CI) :- get_cur_proc_label(!.CI, ProcLabel), get_label_counter(!.CI, C0), counter.allocate(N, C0, C), set_label_counter(C, !CI), Label = internal_label(N, ProcLabel). succip_is_used(!CI) :- set_succip_used(yes, !CI). add_trace_layout_for_label(Label, Context, Port, IsHidden, GoalPath, MaybeSolverEventInfo, Layout, !CI) :- get_layout_info(!.CI, Internals0), Exec = yes(trace_port_layout_info(Context, Port, IsHidden, GoalPath, MaybeSolverEventInfo, Layout)), ( Label = internal_label(LabelNum, _) ; Label = entry_label(_, _), unexpected($module, $pred, "entry") ), ( map.search(Internals0, LabelNum, Internal0) -> Internal0 = internal_layout_info(Exec0, Resume, Return), ( Exec0 = no ; Exec0 = yes(_), unexpected($module, $pred, "already known label") ), Internal = internal_layout_info(Exec, Resume, Return), map.det_update(LabelNum, Internal, Internals0, Internals) ; Internal = internal_layout_info(Exec, no, no), map.det_insert(LabelNum, Internal, Internals0, Internals) ), set_layout_info(Internals, !CI). add_resume_layout_for_label(Label, LayoutInfo, !CI) :- get_layout_info(!.CI, Internals0), Resume = yes(LayoutInfo), ( Label = internal_label(LabelNum, _) ; Label = entry_label(_, _), unexpected($module, $pred, "entry") ), ( map.search(Internals0, LabelNum, Internal0) -> Internal0 = internal_layout_info(Exec, Resume0, Return), ( Resume0 = no ; Resume0 = yes(_), unexpected($module, $pred, "already known label") ), Internal = internal_layout_info(Exec, Resume, Return), map.det_update(LabelNum, Internal, Internals0, Internals) ; Internal = internal_layout_info(no, Resume, no), map.det_insert(LabelNum, Internal, Internals0, Internals) ), set_layout_info(Internals, !CI). :- pred get_active_temps_data(code_info::in, assoc_list(lval, slot_contents)::out) is det. get_active_temps_data(CI, Temps) :- get_temps_in_use(CI, TempsInUse), get_temp_content_map(CI, TempContentMap), map.select(TempContentMap, TempsInUse, TempsInUseContentMap), map.to_assoc_list(TempsInUseContentMap, Temps). get_cur_proc_label(CI, ProcLabel) :- ProcLabel = CI ^ code_info_static ^ cis_proc_label. get_next_closure_seq_no(SeqNo, !CI) :- get_closure_seq_counter(!.CI, C0), counter.allocate(SeqNo, C0, C), set_closure_seq_counter(C, !CI). add_closure_layout(ClosureLayout, !CI) :- get_closure_layouts(!.CI, ClosureLayouts), set_closure_layouts([ClosureLayout | ClosureLayouts], !CI). add_threadscope_string(String, SlotNum, !CI) :- Size0 = !.CI ^ code_info_persistent ^ cip_ts_string_table_size, RevTable0 = !.CI ^ code_info_persistent ^ cip_ts_rev_string_table, SlotNum = Size0, Size = Size0 + 1, RevTable = [String | RevTable0], !CI ^ code_info_persistent ^ cip_ts_string_table_size := Size, !CI ^ code_info_persistent ^ cip_ts_rev_string_table := RevTable. get_threadscope_rev_string_table(CI, RevTable, TableSize) :- RevTable = CI ^ code_info_persistent ^ cip_ts_rev_string_table, TableSize = CI ^ code_info_persistent ^ cip_ts_string_table_size. add_scalar_static_cell(RvalsTypes, DataAddr, !CI) :- get_static_cell_info(!.CI, StaticCellInfo0), global_data.add_scalar_static_cell(RvalsTypes, DataAddr, StaticCellInfo0, StaticCellInfo), set_static_cell_info(StaticCellInfo, !CI). add_scalar_static_cell_natural_types(Rvals, DataAddr, !CI) :- get_static_cell_info(!.CI, StaticCellInfo0), global_data.add_scalar_static_cell_natural_types(Rvals, DataAddr, StaticCellInfo0, StaticCellInfo), set_static_cell_info(StaticCellInfo, !CI). add_vector_static_cell(Types, Vector, DataAddr, !CI) :- get_static_cell_info(!.CI, StaticCellInfo0), global_data.add_vector_static_cell(Types, Vector, DataAddr, StaticCellInfo0, StaticCellInfo), set_static_cell_info(StaticCellInfo, !CI). add_alloc_site_info(Context, Type, Size, AllocId, !CI) :- get_cur_proc_label(!.CI, ProcLabel), AllocSite = alloc_site_info(ProcLabel, Context, Type, Size), AllocId = alloc_site_id(AllocSite), get_alloc_sites(!.CI, AllocSites0), set_tree234.insert(AllocSite, AllocSites0, AllocSites), set_alloc_sites(AllocSites, !CI). %---------------------------------------------------------------------------% %---------------------------------------------------------------------------% % Submodule for handling branched control structures. :- interface. :- type position_info. :- type branch_end_info. :- type branch_end == maybe(branch_end_info). :- pred remember_position(code_info::in, position_info::out) is det. :- pred reset_to_position(position_info::in, code_info::in, code_info::out) is det. :- pred reset_resume_known(position_info::in, code_info::in, code_info::out) is det. :- pred generate_branch_end(abs_store_map::in, branch_end::in, branch_end::out, llds_code::out, code_info::in, code_info::out) is det. :- pred after_all_branches(abs_store_map::in, branch_end::in, code_info::in, code_info::out) is det. :- pred save_hp_in_branch(llds_code::out, lval::out, position_info::in, position_info::out, code_info::in, code_info::out) is det. :- implementation. :- type position_info ---> position_info( % The location-dependent part of the code_info % at a given position. code_info_loc_dep ). :- type branch_end_info ---> branch_end_info( % The code_info at the end of a branch. code_info ). :- func pos_get_fail_info(position_info) = fail_info. pos_get_fail_info(position_info(LocDep)) = LocDep ^ cild_fail_info. remember_position(CI, position_info(CI ^ code_info_loc_dep)). reset_to_position(position_info(LocDep), CurCI, NextCI) :- CurCI = code_info(Static, _, Persistent), NextCI0 = code_info(Static, LocDep, Persistent), get_persistent_temps(NextCI0, PersistentTemps), get_temps_in_use(NextCI0, TempsInUse0), set.union(PersistentTemps, TempsInUse0, TempsInUse), set_temps_in_use(TempsInUse, NextCI0, NextCI). reset_resume_known(BranchStart, !CI) :- BranchStartFailInfo = pos_get_fail_info(BranchStart), BranchStartFailInfo = fail_info(_, BSResumeKnown, _, _, _), get_fail_info(!.CI, CurFailInfo), CurFailInfo = fail_info(CurFailStack, _, CurCurfMaxfr, CurCondEnv, CurHijack), NewFailInfo = fail_info(CurFailStack, BSResumeKnown, CurCurfMaxfr, CurCondEnv, CurHijack), set_fail_info(NewFailInfo, !CI). 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, AbsVarLocs), assoc_list.values(AbsVarLocs, AbsLocs), code_util.max_mentioned_abs_regs(AbsLocs, MaxRegR, MaxRegF), set_follow_vars(abs_follow_vars(StoreMap, MaxRegR + 1, MaxRegF + 1), !CI), get_instmap(!.CI, InstMap), ( instmap_is_reachable(InstMap) -> VarLocs = assoc_list.map_values_only(abs_locn_to_lval, AbsVarLocs), 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. remake_with_store_map(StoreMap, !CI), Code = empty ), EndCodeInfo1 = !.CI, ( MaybeEnd0 = no, MaybeEnd = yes(branch_end_info(EndCodeInfo1)) ; MaybeEnd0 = yes(branch_end_info(EndCodeInfo0)), % Make sure the left context we leave the branched structure with % is valid for all branches. get_fail_info(EndCodeInfo0, FailInfo0), 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), expect(unify(Redoip0, Redoip1), $module, $pred, "redoip mismatch") ; 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 ), expect(unify(CondEnv0, CondEnv1), $module, $pred, "some but not all branches inside a non condition"), FailInfo = fail_info(R, ResumeKnown, CurfrMaxfr, CondEnv0, Hijack), 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. get_temps_in_use(EndCodeInfo0, TempsInUse0), get_temps_in_use(EndCodeInfo1, TempsInUse1), set.union(TempsInUse0, TempsInUse1, TempsInUse), set_temps_in_use(TempsInUse, EndCodeInfoA, EndCodeInfo), MaybeEnd = yes(branch_end_info(EndCodeInfo)) ). after_all_branches(StoreMap, MaybeEnd, !CI) :- ( MaybeEnd = yes(BranchEnd), BranchEnd = branch_end_info(BranchEndCodeInfo), BranchEndLocDep = BranchEndCodeInfo ^ code_info_loc_dep, reset_to_position(position_info(BranchEndLocDep), !CI), remake_with_store_map(StoreMap, !CI) ; MaybeEnd = no, unexpected($module, $pred, "no branches in branched control structure") ). % 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 remake_with_store_map(abs_store_map::in, code_info::in, code_info::out) is det. remake_with_store_map(StoreMap, !CI) :- map.to_assoc_list(StoreMap, VarLocns), VarLvals = assoc_list.map_values_only(abs_locn_to_lval, VarLocns), get_var_locn_info(!.CI, VarLocnInfo0), reinit_var_locn_state(VarLvals, VarLocnInfo0, VarLocnInfo), set_var_locn_info(VarLocnInfo, !CI). save_hp_in_branch(Code, Slot, Pos0, Pos, CI0, CI) :- CI0 = code_info(CIStatic0, CILocDep0, CIPersistent0), Pos0 = position_info(LocDep0), CI1 = code_info(CIStatic0, LocDep0, CIPersistent0), save_hp(Code, Slot, CI1, CI2), CI2 = code_info(CIStatic, LocDep, CIPersistent), Pos = position_info(LocDep), % Reset the location dependent part to the original. CI = code_info(CIStatic, CILocDep0, CIPersistent). %---------------------------------------------------------------------------% %---------------------------------------------------------------------------% % 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 prepare_for_disj_hijack(code_model::in, disj_hijack_info::out, llds_code::out, code_info::in, code_info::out) is det. :- pred undo_disj_hijack(disj_hijack_info::in, llds_code::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. % The maybe(lval) argument, if set to `yes', specifies the slot % holding the success record to use in deciding whether to execute % a region_ite_nondet_cond_fail operation at the start of the else branch. % % `ite_enter_then', which should be called after 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 prepare_for_ite_hijack(code_model::in, maybe(embedded_stack_frame_id)::in, ite_hijack_info::out, llds_code::out, code_info::in, code_info::out) is det. :- pred ite_enter_then(ite_hijack_info::in, resume_point_info::in, llds_code::out, llds_code::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 enter_simple_neg(list(prog_var)::in, hlds_goal_info::in, simple_neg_info::out, code_info::in, code_info::out) is det. :- pred 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 away % any choice points generated in the goal. % % The set_of_progvar should be the set of variables live before % the scope goal. % :- type det_commit_info. :- pred prepare_for_det_commit(add_trail_ops::in, add_region_ops::in, set_of_progvar::in, hlds_goal_info::in, det_commit_info::out, llds_code::out, code_info::in, code_info::out) is det. :- pred generate_det_commit(det_commit_info::in, llds_code::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. % % The set_of_progvar should be the set of variables live before % the scope goal. % :- type semi_commit_info. :- pred prepare_for_semi_commit(add_trail_ops::in, add_region_ops::in, set_of_progvar::in, hlds_goal_info::in, semi_commit_info::out, llds_code::out, code_info::in, code_info::out) is det. :- pred generate_semi_commit(semi_commit_info::in, llds_code::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 effect_resume_point(resume_point_info::in, code_model::in, llds_code::out, code_info::in, code_info::out) is det. :- pred 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 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 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 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 generate_failure(llds_code::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 fail_if_rval_is_false(rval::in, llds_code::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 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 may_use_nondet_tailcall(code_info::in, nondet_tail_call::out) is det. % Materialize the given variables into registers or stack slots. % :- pred produce_vars(list(prog_var)::in, resume_map::out, llds_code::out, code_info::in, code_info::out) is det. % Put the variables needed in enclosing failure continuations % into their stack slots. % :- pred flush_resume_vars_to_stack(llds_code::out, code_info::in, code_info::out) is det. % Set up the resume_point_info structure. % The ResumeVars passed as the first arguments should be a sorted list % without duplicates. % :- pred make_resume_point(list(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 generate_resume_point(resume_point_info::in, llds_code::out, code_info::in, code_info::out) is det. % List the variables that need to be preserved for the given resume point. % :- pred 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 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_redo or do_fail, which indicate that % the resumption point is either unknown or 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( % The stack slot in which we saved the value % of the hijacked redoip. lval ) ; disj_full_hijack( % 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 ). prepare_for_disj_hijack(CodeModel, HijackInfo, Code, !CI) :- get_fail_info(!.CI, FailInfo), FailInfo = fail_info(ResumePoints, ResumeKnown, CurfrMaxfr, CondEnv, Allow), ( ( CodeModel = model_det ; CodeModel = model_semi ), HijackInfo = disj_no_hijack, Code = singleton( llds_instr(comment("disj no hijack"), "") ) ; CodeModel = model_non, ( CondEnv = inside_non_condition, HijackInfo = disj_temp_frame, create_temp_frame(do_fail, "prepare for disjunction", Code, !CI) ; CondEnv = not_inside_non_condition, ( Allow = not_allowed, ( CurfrMaxfr = must_be_equal, ResumeKnown = resume_point_known(has_been_done), stack.pop(TopResumePoint, ResumePoints, RestResumePoints), stack.is_empty(RestResumePoints), TopResumePoint = stack_only(_, do_fail) -> HijackInfo = disj_quarter_hijack, Code = singleton( llds_instr(comment("disj quarter hijack of do_fail"), "") ) ; HijackInfo = disj_temp_frame, create_temp_frame(do_fail, "prepare for disjunction", Code, !CI) ) ; Allow = allowed, ( CurfrMaxfr = must_be_equal, ( ResumeKnown = resume_point_known(has_been_done), HijackInfo = disj_quarter_hijack, Code = singleton( llds_instr(comment("disj quarter hijack"), "") ) ; ( ResumeKnown = resume_point_known(wont_be_done) ; ResumeKnown = resume_point_unknown ), acquire_temp_slot(slot_lval(redoip_slot(lval(curfr))), non_persistent_temp_slot, RedoipSlot, !CI), HijackInfo = disj_half_hijack(RedoipSlot), Code = singleton( llds_instr(assign(RedoipSlot, lval(redoip_slot(lval(curfr)))), "prepare for half disj hijack") ) ) ; CurfrMaxfr = may_be_different, acquire_temp_slot(slot_lval(redoip_slot(lval(maxfr))), non_persistent_temp_slot, RedoipSlot, !CI), acquire_temp_slot(slot_lval(redofr_slot(lval(maxfr))), non_persistent_temp_slot, RedofrSlot, !CI), HijackInfo = disj_full_hijack(RedoipSlot, RedofrSlot), Code = from_list([ llds_instr( assign(RedoipSlot, lval(redoip_slot(lval(maxfr)))), "prepare for full disj hijack"), llds_instr( assign(RedofrSlot, lval(redofr_slot(lval(maxfr)))), "prepare for full disj hijack"), llds_instr( assign(redofr_slot(lval(maxfr)), lval(curfr)), "prepare for full disj hijack") ]) ) ) ) ). undo_disj_hijack(HijackInfo, Code, !CI) :- get_fail_info(!.CI, FailInfo0), FailInfo0 = fail_info(ResumePoints, ResumeKnown, CurfrMaxfr, CondEnv, Allow), ( HijackInfo = disj_no_hijack, Code = empty ; HijackInfo = disj_temp_frame, Code = singleton( llds_instr(assign(maxfr, lval(prevfr_slot(lval(maxfr)))), "restore maxfr for temp frame disj") ) ; HijackInfo = disj_quarter_hijack, expect(unify(CurfrMaxfr, must_be_equal), $module, $pred, "maxfr may differ from curfr in disj_quarter_hijack"), stack.det_top(ResumePoints, ResumePoint), pick_stack_resume_point(ResumePoint, _, StackLabel), LabelConst = const(llconst_code_addr(StackLabel)), % peephole.m looks for the "curfr==maxfr" pattern in the comment. Code = singleton( llds_instr(assign(redoip_slot(lval(curfr)), LabelConst), "restore redoip for quarter disj hijack (curfr==maxfr)") ) ; HijackInfo = disj_half_hijack(RedoipSlot), expect(unify(ResumeKnown, resume_point_unknown), $module, $pred, "resume point known in disj_half_hijack"), expect(unify(CurfrMaxfr, must_be_equal), $module, $pred, "maxfr may differ from curfr in disj_half_hijack"), % peephole.m looks for the "curfr==maxfr" pattern in the comment. Code = singleton( llds_instr(assign(redoip_slot(lval(curfr)), lval(RedoipSlot)), "restore redoip for half disj hijack (curfr==maxfr)") ) ; HijackInfo = disj_full_hijack(RedoipSlot, RedofrSlot), expect(unify(CurfrMaxfr, may_be_different), $module, $pred, "maxfr same as curfr in disj_full_hijack"), Code = from_list([ llds_instr(assign(redoip_slot(lval(maxfr)), lval(RedoipSlot)), "restore redoip for full disj hijack"), llds_instr(assign(redofr_slot(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), set_fail_info(FailInfo, !CI) ; true ). %---------------------------------------------------------------------------% % For model_non if-then-elses, we need to clean up the embedded stack frame % we create for the if-then-else when the condition fails after succeeding. % For such if-then-elses, we record the id of the embedded frame we need to % clean up, and the id of the slot that is initialized to false, and set to % true each time the condition succeeds. :- type ite_region_info ---> ite_region_info( embedded_stack_frame_id, lval ). :- type ite_hijack_info ---> ite_info( resume_point_known, condition_env, ite_hijack_type, maybe(ite_region_info) ). :- type ite_hijack_type ---> ite_no_hijack ; ite_temp_frame( % The stack slot in which we saved the value of maxfr. lval ) ; ite_quarter_hijack ; ite_half_hijack( % The stack slot in which we saved the value % of the hijacked redoip. lval ) ; ite_full_hijack( % 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. lval ). prepare_for_ite_hijack(CondCodeModel, MaybeEmbeddedFrameId, HijackInfo, Code, !CI) :- get_fail_info(!.CI, FailInfo), FailInfo = fail_info(_, ResumeKnown, CurfrMaxfr, CondEnv, Allow), ( % It is possible for a negated goal (which is the "Condition" of the % equivalent if-then-else) to be det, if it is also impure. ( CondCodeModel = model_det ; CondCodeModel = model_semi ), expect(unify(MaybeEmbeddedFrameId, no), $module, $pred, "MaybeEmbeddedFrameId in model_semi"), HijackType = ite_no_hijack, Code = singleton( llds_instr(comment("ite no hijack"), "") ), MaybeRegionInfo = no ; CondCodeModel = model_non, ( ( Allow = not_allowed ; CondEnv = inside_non_condition ; MaybeEmbeddedFrameId = yes(_) ) -> acquire_temp_slot(slot_lval(maxfr), non_persistent_temp_slot, MaxfrSlot, !CI), HijackType = ite_temp_frame(MaxfrSlot), create_temp_frame(do_fail, "prepare for ite", TempFrameCode, !CI), MaxfrCode = singleton( llds_instr(assign(MaxfrSlot, lval(maxfr)), "prepare for ite") ), ( MaybeEmbeddedFrameId = yes(EmbeddedFrameId), % Note that this slot is intentionally not released anywhere. acquire_temp_slot(slot_success_record, persistent_temp_slot, SuccessRecordSlot, !CI), InitSuccessCode = singleton( llds_instr( assign(SuccessRecordSlot, const(llconst_false)), "record no success of the condition yes") ), MaybeRegionInfo = yes(ite_region_info(EmbeddedFrameId, SuccessRecordSlot)) ; MaybeEmbeddedFrameId = no, InitSuccessCode = empty, MaybeRegionInfo = no ), Code = TempFrameCode ++ MaxfrCode ++ InitSuccessCode ; ( CurfrMaxfr = must_be_equal, ( ResumeKnown = resume_point_known(_), HijackType = ite_quarter_hijack, Code = singleton( llds_instr(comment("ite quarter hijack"), "") ) ; ResumeKnown = resume_point_unknown, acquire_temp_slot(slot_lval(redoip_slot(lval(curfr))), non_persistent_temp_slot, RedoipSlot, !CI), HijackType = ite_half_hijack(RedoipSlot), Code = singleton( llds_instr( assign(RedoipSlot, lval(redoip_slot(lval(curfr)))), "prepare for half ite hijack") ) ) ; CurfrMaxfr = may_be_different, acquire_temp_slot(slot_lval(redoip_slot(lval(maxfr))), non_persistent_temp_slot, RedoipSlot, !CI), acquire_temp_slot(slot_lval(redofr_slot(lval(maxfr))), non_persistent_temp_slot, RedofrSlot, !CI), acquire_temp_slot(slot_lval(maxfr), non_persistent_temp_slot, MaxfrSlot, !CI), HijackType = ite_full_hijack(RedoipSlot, RedofrSlot, MaxfrSlot), Code = from_list([ llds_instr( assign(MaxfrSlot, lval(maxfr)), "prepare for full ite hijack"), llds_instr( assign(RedoipSlot, lval(redoip_slot(lval(maxfr)))), "prepare for full ite hijack"), llds_instr( assign(RedofrSlot, lval(redofr_slot(lval(maxfr)))), "prepare for full ite hijack"), llds_instr( assign(redofr_slot(lval(maxfr)), lval(curfr)), "prepare for full ite hijack") ]) ), MaybeRegionInfo = no ), inside_non_condition(!CI) ), HijackInfo = ite_info(ResumeKnown, CondEnv, HijackType, MaybeRegionInfo). ite_enter_then(HijackInfo, ITEResumePoint, ThenCode, ElseCode, !CI) :- get_fail_info(!.CI, FailInfo0), FailInfo0 = fail_info(ResumePoints0, ResumeKnown0, CurfrMaxfr, _, Allow), stack.det_pop(_, ResumePoints0, ResumePoints), HijackInfo = ite_info(HijackResumeKnown, OldCondEnv, HijackType, MaybeRegionInfo), ( HijackType = ite_no_hijack, expect(unify(MaybeRegionInfo, no), $module, $pred, "MaybeRegionInfo ite_no_hijack"), ThenCode = empty, ElseCode = empty ; HijackType = ite_temp_frame(MaxfrSlot), ( MaybeRegionInfo = no, ThenCode = singleton( % We can't remove the frame, it may not be on top. llds_instr(assign(redoip_slot(lval(MaxfrSlot)), const(llconst_code_addr(do_fail))), "soft cut for temp frame ite") ), ElseCode = singleton( % XXX search for assignments to maxfr llds_instr(assign(maxfr, lval(prevfr_slot(lval(MaxfrSlot)))), "restore maxfr for temp frame ite") ) ; MaybeRegionInfo = yes(RegionInfo), RegionInfo = ite_region_info(EmbeddedStackFrameId, SuccessRecordSlot), % XXX replace do_fail with ref to ResumePoint stack label resume_point_stack_addr(ITEResumePoint, ITEStackResumeCodeAddr), ThenCode = from_list([ llds_instr(assign(SuccessRecordSlot, const(llconst_true)), "record success of the condition"), llds_instr(assign(redoip_slot(lval(MaxfrSlot)), const(llconst_code_addr(ITEStackResumeCodeAddr))), "redirect to cut for temp frame ite") ]), get_next_label(AfterRegionOp, !CI), ElseCode = from_list([ llds_instr(assign(maxfr, lval(prevfr_slot(lval(MaxfrSlot)))), "restore maxfr for temp frame ite"), llds_instr(if_val(unop(logical_not, lval(SuccessRecordSlot)), code_label(AfterRegionOp)), "jump around if the condition never succeeded"), llds_instr(use_and_maybe_pop_region_frame( region_ite_nondet_cond_fail, EmbeddedStackFrameId), "cleanup after the post-success failure of the condition"), llds_instr(goto(do_fail), "the condition succeeded, so don't execute else branch"), llds_instr(label(AfterRegionOp), "after region op") ]) ) ; HijackType = ite_quarter_hijack, expect(unify(MaybeRegionInfo, no), $module, $pred, "MaybeRegionInfo ite_quarter_hijack"), stack.det_top(ResumePoints, ResumePoint), ( maybe_pick_stack_resume_point(ResumePoint, _, StackLabel) -> LabelConst = const(llconst_code_addr(StackLabel)), ThenCode = singleton( llds_instr(assign(redoip_slot(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), expect(unify(MaybeRegionInfo, no), $module, $pred, "MaybeRegionInfo ite_half_hijack"), ThenCode = singleton( llds_instr(assign(redoip_slot(lval(curfr)), lval(RedoipSlot)), "restore redoip for half ite hijack") ), ElseCode = ThenCode ; HijackType = ite_full_hijack(RedoipSlot, RedofrSlot, MaxfrSlot), expect(unify(MaybeRegionInfo, no), $module, $pred, "MaybeRegionInfo ite_full_hijack"), ThenCode = from_list([ llds_instr(assign(redoip_slot(lval(MaxfrSlot)), lval(RedoipSlot)), "restore redoip for full ite hijack"), llds_instr(assign(redofr_slot(lval(MaxfrSlot)), lval(RedofrSlot)), "restore redofr for full ite hijack") ]), ElseCode = from_list([ llds_instr(assign(redoip_slot(lval(maxfr)), lval(RedoipSlot)), "restore redoip for full ite hijack"), llds_instr(assign(redofr_slot(lval(maxfr)), lval(RedofrSlot)), "restore redofr for full ite hijack") ]) ), ( ResumeKnown0 = resume_point_unknown, ResumeKnown = resume_point_unknown ; ResumeKnown0 = resume_point_known(_), ResumeKnown = HijackResumeKnown ), FailInfo = fail_info(ResumePoints, ResumeKnown, CurfrMaxfr, OldCondEnv, Allow), set_fail_info(FailInfo, !CI). %---------------------------------------------------------------------------% :- type simple_neg_info == fail_info. enter_simple_neg(ResumeVars, GoalInfo, FailInfo0, !CI) :- 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. map.init(ResumeMap0), make_fake_resume_map(ResumeVars, ResumeMap0, ResumeMap), ResumePoint = orig_only(ResumeMap, do_redo), effect_resume_point(ResumePoint, model_semi, Code, !CI), expect(is_empty(Code), $module, $pred, "nonempty code for simple neg"), pre_goal_update(GoalInfo, does_not_have_subgoals, !CI). leave_simple_neg(GoalInfo, FailInfo, !CI) :- post_goal_update(GoalInfo, !CI), set_fail_info(FailInfo, !CI). :- pred make_fake_resume_map(list(prog_var)::in, map(prog_var, set(lval))::in, map(prog_var, set(lval))::out) is det. make_fake_resume_map([], !ResumeMap). make_fake_resume_map([Var | Vars], !ResumeMap) :- % A visibly fake location. Locns = set.make_singleton_set(reg(reg_r, -1)), map.det_insert(Var, Locns, !ResumeMap), make_fake_resume_map(Vars, !ResumeMap). %---------------------------------------------------------------------------% :- type det_commit_info ---> det_commit_info( % Location of saved maxfr. maybe(lval), % Location of saved ticket % counter and trail pointer. maybe(pair(lval)), maybe(region_commit_stack_frame) ). :- type region_commit_stack_frame ---> region_commit_stack_frame( % The id of the region commit stack frame, which is emdedded % in the current procedure's stack frame, and whose layout is: % saved region_commit_stack_pointer % saved region sequence number % number of live nonprotected regions % space reserved for the ids of live nonprotected regions embedded_stack_frame_id, % The list of temporary slots that constitute % this embedded stack frame. list(lval) ). prepare_for_det_commit(AddTrailOps, AddRegionOps, ForwardLiveVarsBeforeGoal, CommitGoalInfo, DetCommitInfo, Code, !CI) :- get_fail_info(!.CI, FailInfo0), FailInfo0 = fail_info(_, _, CurfrMaxfr, _, _), ( CurfrMaxfr = may_be_different, acquire_temp_slot(slot_lval(maxfr), non_persistent_temp_slot, MaxfrSlot, !CI), SaveMaxfrCode = singleton( llds_instr(save_maxfr(MaxfrSlot), "save the value of maxfr") ), MaybeMaxfrSlot = yes(MaxfrSlot) ; CurfrMaxfr = must_be_equal, SaveMaxfrCode = empty, MaybeMaxfrSlot = no ), maybe_save_trail_info(AddTrailOps, MaybeTrailSlots, SaveTrailCode, !CI), maybe_save_region_commit_frame(AddRegionOps, ForwardLiveVarsBeforeGoal, CommitGoalInfo, MaybeRegionCommitFrameInfo, SaveRegionCommitFrameCode, !CI), DetCommitInfo = det_commit_info(MaybeMaxfrSlot, MaybeTrailSlots, MaybeRegionCommitFrameInfo), Code = SaveMaxfrCode ++ SaveTrailCode ++ SaveRegionCommitFrameCode. generate_det_commit(DetCommitInfo, Code, !CI) :- DetCommitInfo = det_commit_info(MaybeMaxfrSlot, MaybeTrailSlots, MaybeRegionCommitFrameInfo), ( MaybeMaxfrSlot = yes(MaxfrSlot), RestoreMaxfrCode = singleton( llds_instr(restore_maxfr(MaxfrSlot), "restore the value of maxfr - perform commit") ), release_temp_slot(MaxfrSlot, non_persistent_temp_slot, !CI) ; MaybeMaxfrSlot = no, RestoreMaxfrCode = singleton( llds_instr(assign(maxfr, lval(curfr)), "restore the value of maxfr - perform commit") ) ), maybe_restore_trail_info(MaybeTrailSlots, CommitTrailCode, _, !CI), maybe_restore_region_commit_frame(MaybeRegionCommitFrameInfo, SuccessRegionCode, _FailureRegionCode, !CI), Code = RestoreMaxfrCode ++ CommitTrailCode ++ SuccessRegionCode. %---------------------------------------------------------------------------% :- type semi_commit_info ---> semi_commit_info( % Fail_info on entry. fail_info, resume_point_info, commit_hijack_info, % Location of saved ticket counter and trail pointer. maybe(pair(lval)), maybe(region_commit_stack_frame) ). :- type commit_hijack_info ---> commit_temp_frame( % The stack slot in which we saved the old value of maxfr. lval, % Do we bracket the goal with MR_commit_mark and MR_commit_cut? bool ) ; commit_quarter_hijack ; commit_half_hijack( % The stack slot in which we saved the value % of the hijacked redoip. lval ) ; commit_full_hijack( % 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. lval ). prepare_for_semi_commit(AddTrailOps, AddRegionOps, ForwardLiveVarsBeforeGoal, CommitGoalInfo, SemiCommitInfo, Code, !CI) :- get_fail_info(!.CI, FailInfo0), FailInfo0 = fail_info(ResumePoints0, ResumeKnown, CurfrMaxfr, CondEnv, Allow), stack.det_top(ResumePoints0, TopResumePoint), clone_resume_point(TopResumePoint, NewResumePoint, !CI), stack.push(NewResumePoint, ResumePoints0, ResumePoints), FailInfo = fail_info(ResumePoints, resume_point_known(has_been_done), CurfrMaxfr, CondEnv, Allow), set_fail_info(FailInfo, !CI), pick_stack_resume_point(NewResumePoint, _, StackLabel), StackLabelConst = const(llconst_code_addr(StackLabel)), ( ( Allow = not_allowed ; CondEnv = inside_non_condition ) -> acquire_temp_slot(slot_lval(maxfr), non_persistent_temp_slot, MaxfrSlot, !CI), MaxfrCode = singleton( llds_instr(save_maxfr(MaxfrSlot), "prepare for temp frame commit") ), create_temp_frame(StackLabel, "prepare for temp frame commit", TempFrameCode, !CI), get_globals(!.CI, Globals), globals.lookup_bool_option(Globals, use_minimal_model_stack_copy_cut, UseMinimalModelStackCopyCut), HijackInfo = commit_temp_frame(MaxfrSlot, UseMinimalModelStackCopyCut), ( UseMinimalModelStackCopyCut = 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 = [ foreign_proc_raw_code(cannot_branch_away, proc_affects_liveness, live_lvals_info(set.init), "\t\tMR_save_transient_registers();\n"), foreign_proc_raw_code(cannot_branch_away, proc_does_not_affect_liveness, live_lvals_info(set.init), "\t\tMR_commit_mark();\n"), foreign_proc_raw_code(cannot_branch_away, proc_affects_liveness, live_lvals_info(set.init), "\t\tMR_restore_transient_registers();\n") ], MD = proc_may_duplicate, MarkCode = singleton( llds_instr(foreign_proc_code([], Components, proc_will_not_call_mercury, no, no, no, no, no, no, MD), "") ) ; UseMinimalModelStackCopyCut = no, MarkCode = empty ), HijackCode = MaxfrCode ++ TempFrameCode ++ MarkCode ; ( CurfrMaxfr = must_be_equal, ( ResumeKnown = resume_point_known(has_been_done), HijackInfo = commit_quarter_hijack, HijackCode = singleton( llds_instr(assign(redoip_slot(lval(curfr)), StackLabelConst), "hijack the redofr slot") ) ; ( ResumeKnown = resume_point_known(wont_be_done) ; ResumeKnown = resume_point_unknown ), acquire_temp_slot(slot_lval(redoip_slot(lval(curfr))), non_persistent_temp_slot, RedoipSlot, !CI), HijackInfo = commit_half_hijack(RedoipSlot), HijackCode = from_list([ llds_instr(assign(RedoipSlot, lval(redoip_slot(lval(curfr)))), "prepare for half commit hijack"), llds_instr(assign(redoip_slot(lval(curfr)), StackLabelConst), "hijack the redofr slot") ]) ) ; CurfrMaxfr = may_be_different, acquire_temp_slot(slot_lval(redoip_slot(lval(maxfr))), non_persistent_temp_slot, RedoipSlot, !CI), acquire_temp_slot(slot_lval(redofr_slot(lval(maxfr))), non_persistent_temp_slot, RedofrSlot, !CI), acquire_temp_slot(slot_lval(maxfr), non_persistent_temp_slot, MaxfrSlot, !CI), HijackInfo = commit_full_hijack(RedoipSlot, RedofrSlot, MaxfrSlot), HijackCode = from_list([ llds_instr(assign(RedoipSlot, lval(redoip_slot(lval(maxfr)))), "prepare for full commit hijack"), llds_instr(assign(RedofrSlot, lval(redofr_slot(lval(maxfr)))), "prepare for full commit hijack"), llds_instr(save_maxfr(MaxfrSlot), "prepare for full commit hijack"), llds_instr(assign(redofr_slot(lval(maxfr)), lval(curfr)), "hijack the redofr slot"), llds_instr(assign(redoip_slot(lval(maxfr)), StackLabelConst), "hijack the redoip slot") ]) ) ), maybe_save_trail_info(AddTrailOps, MaybeTrailSlots, SaveTrailCode, !CI), maybe_save_region_commit_frame(AddRegionOps, ForwardLiveVarsBeforeGoal, CommitGoalInfo, MaybeRegionCommitFrameInfo, SaveRegionCommitFrameCode, !CI), SemiCommitInfo = semi_commit_info(FailInfo0, NewResumePoint, HijackInfo, MaybeTrailSlots, MaybeRegionCommitFrameInfo), Code = HijackCode ++ SaveTrailCode ++ SaveRegionCommitFrameCode. generate_semi_commit(SemiCommitInfo, Code, !CI) :- SemiCommitInfo = semi_commit_info(FailInfo, ResumePoint, HijackInfo, MaybeTrailSlots, MaybeRegionCommitFrameInfo), set_fail_info(FailInfo, !CI), % XXX Should release the temp slots in each arm of the switch. ( HijackInfo = commit_temp_frame(MaxfrSlot, UseMinimalModel), MaxfrCode = singleton( llds_instr(restore_maxfr(MaxfrSlot), "restore maxfr for temp frame hijack") ), ( UseMinimalModel = yes, % See the comment in prepare_for_semi_commit above. Components = [ foreign_proc_raw_code(cannot_branch_away, proc_does_not_affect_liveness, live_lvals_info(set.init), "\t\tMR_commit_cut();\n") ], MD = proc_may_duplicate, CutCode = singleton( llds_instr(foreign_proc_code([], Components, proc_will_not_call_mercury, no, no, no, no, no, no, MD), "commit for temp frame hijack") ) ; UseMinimalModel = no, CutCode = empty ), SuccessUndoCode = MaxfrCode ++ CutCode, FailureUndoCode = MaxfrCode ++ CutCode ; HijackInfo = commit_quarter_hijack, FailInfo = fail_info(ResumePoints, _, _, _, _), stack.det_top(ResumePoints, TopResumePoint), pick_stack_resume_point(TopResumePoint, _, StackLabel), StackLabelConst = const(llconst_code_addr(StackLabel)), SuccessUndoCode = from_list([ llds_instr(assign(maxfr, lval(curfr)), "restore maxfr for quarter commit hijack"), llds_instr(assign(redoip_slot(lval(maxfr)), StackLabelConst), "restore redoip for quarter commit hijack") ]), FailureUndoCode = singleton( llds_instr(assign(redoip_slot(lval(maxfr)), StackLabelConst), "restore redoip for quarter commit hijack") ) ; HijackInfo = commit_half_hijack(RedoipSlot), SuccessUndoCode = from_list([ llds_instr(assign(maxfr, lval(curfr)), "restore maxfr for half commit hijack"), llds_instr(assign(redoip_slot(lval(maxfr)), lval(RedoipSlot)), "restore redoip for half commit hijack") ]), FailureUndoCode = singleton( llds_instr(assign(redoip_slot(lval(maxfr)), lval(RedoipSlot)), "restore redoip for half commit hijack") ) ; HijackInfo = commit_full_hijack(RedoipSlot, RedofrSlot, MaxfrSlot), SuccessUndoCode = from_list([ llds_instr(restore_maxfr(MaxfrSlot), "restore maxfr for full commit hijack"), llds_instr(assign(redoip_slot(lval(maxfr)), lval(RedoipSlot)), "restore redoip for full commit hijack"), llds_instr(assign(redofr_slot(lval(maxfr)), lval(RedofrSlot)), "restore redofr for full commit hijack") ]), FailureUndoCode = from_list([ llds_instr(assign(redoip_slot(lval(maxfr)), lval(RedoipSlot)), "restore redoip for full commit hijack"), llds_instr(assign(redofr_slot(lval(maxfr)), lval(RedofrSlot)), "restore redofr for full commit hijack") ]) ), remember_position(!.CI, AfterCommit), generate_resume_point(ResumePoint, ResumePointCode, !CI), generate_failure(FailCode, !CI), reset_to_position(AfterCommit, !CI), maybe_restore_trail_info(MaybeTrailSlots, CommitTrailCode, RestoreTrailCode, !CI), maybe_restore_region_commit_frame(MaybeRegionCommitFrameInfo, SuccessRegionCode, FailureRegionCode, !CI), get_next_label(SuccLabel, !CI), GotoSuccLabel = singleton( llds_instr(goto(code_label(SuccLabel)), "Jump to success continuation") ), SuccLabelCode = singleton( llds_instr(label(SuccLabel), "Success continuation") ), SuccessCode = SuccessUndoCode ++ CommitTrailCode ++ SuccessRegionCode, FailureCode = ResumePointCode ++ FailureUndoCode ++ RestoreTrailCode ++ FailureRegionCode ++ FailCode, Code = SuccessCode ++ GotoSuccLabel ++ FailureCode ++ SuccLabelCode. %---------------------------------------------------------------------------% :- pred maybe_save_region_commit_frame(add_region_ops::in, set_of_progvar::in, hlds_goal_info::in, maybe(region_commit_stack_frame)::out, llds_code::out, code_info::in, code_info::out) is det. maybe_save_region_commit_frame(AddRegionOps, _ForwardLiveVarsBeforeGoal, CommitGoalInfo, MaybeRegionCommitFrameInfo, Code, !CI) :- ( AddRegionOps = do_not_add_region_ops, MaybeRegionCommitFrameInfo = no, Code = empty ; AddRegionOps = add_region_ops, MaybeRbmmInfo = goal_info_get_maybe_rbmm(CommitGoalInfo), ( MaybeRbmmInfo = no, MaybeRegionCommitFrameInfo = no, Code = empty ; MaybeRbmmInfo = yes(RbmmInfo), RbmmInfo = rbmm_goal_info(_, CommitRemovedRegionVars, _, _, _), RemovedRegionVarList = set.to_sorted_list(CommitRemovedRegionVars), NumRemovedRegionVars = list.length(RemovedRegionVarList), code_info.get_globals(!.CI, Globals), globals.lookup_int_option(Globals, size_region_commit_fixed, FixedSize), globals.lookup_int_option(Globals, size_region_commit_entry, EntrySize), FrameSize = FixedSize + EntrySize * NumRemovedRegionVars, Items = list.duplicate(FrameSize, slot_region_commit), acquire_several_temp_slots(Items, non_persistent_temp_slot, StackVars, MainStackId, FirstSlotNum, LastSlotNum, !CI), EmbeddedStackFrame = embedded_stack_frame_id(MainStackId, FirstSlotNum, LastSlotNum), FirstSavedRegionAddr = first_nonfixed_embedded_slot_addr( EmbeddedStackFrame, FixedSize), acquire_reg(reg_r, NumRegLval, !CI), acquire_reg(reg_r, AddrRegLval, !CI), PushInitCode = from_list([ llds_instr( push_region_frame(region_stack_commit, EmbeddedStackFrame), "Save stack pointer of embedded region commit stack"), llds_instr( assign(NumRegLval, const(llconst_int(0))), "Initialize number of unprotected live regions"), llds_instr( assign(AddrRegLval, FirstSavedRegionAddr), "Initialize pointer to the next unprotected live" ++ " region slot") ]), save_unprotected_live_regions(NumRegLval, AddrRegLval, EmbeddedStackFrame, RemovedRegionVarList, FillCode, !CI), SetCode = singleton( llds_instr( region_set_fixed_slot(region_set_commit_num_entries, EmbeddedStackFrame, lval(NumRegLval)), "Store the number of unprotected live regions") ), release_reg(NumRegLval, !CI), release_reg(AddrRegLval, !CI), RegionCommitFrameInfo = region_commit_stack_frame(EmbeddedStackFrame, StackVars), MaybeRegionCommitFrameInfo = yes(RegionCommitFrameInfo), Code = PushInitCode ++ FillCode ++ SetCode ) ). :- pred save_unprotected_live_regions(lval::in, lval::in, embedded_stack_frame_id::in, list(prog_var)::in, llds_code::out, code_info::in, code_info::out) is det. save_unprotected_live_regions(_, _, _, [], empty, !CI). save_unprotected_live_regions(NumLval, AddrLval, EmbeddedStackFrame, [RegionVar | RegionVars], Code ++ Codes, !CI) :- produce_variable(RegionVar, ProduceVarCode, RegionVarRval, !CI), SaveCode = singleton( llds_instr( region_fill_frame(region_fill_commit, EmbeddedStackFrame, RegionVarRval, NumLval, AddrLval), "Save the region in the commit stack frame if it is unprotected") ), Code = ProduceVarCode ++ SaveCode, save_unprotected_live_regions(NumLval, AddrLval, EmbeddedStackFrame, RegionVars, Codes, !CI). :- pred maybe_restore_region_commit_frame(maybe(region_commit_stack_frame)::in, llds_code::out, llds_code::out, code_info::in, code_info::out) is det. maybe_restore_region_commit_frame(MaybeRegionCommitFrameInfo, SuccessCode, FailureCode, !CI) :- ( MaybeRegionCommitFrameInfo = no, SuccessCode = empty, FailureCode = empty ; MaybeRegionCommitFrameInfo = yes(RegionCommitFrameInfo), RegionCommitFrameInfo = region_commit_stack_frame(EmbeddedStackFrame, StackVars), SuccessCode = singleton( llds_instr( use_and_maybe_pop_region_frame(region_commit_success, EmbeddedStackFrame), "Destroy removed regions protected by cut away disjunctions") ), FailureCode = singleton( llds_instr( use_and_maybe_pop_region_frame(region_commit_failure, EmbeddedStackFrame), "Undo the creation of the commit frame") ), release_several_temp_slots(StackVars, non_persistent_temp_slot, !CI) ). %---------------------------------------------------------------------------% :- pred inside_non_condition(code_info::in, code_info::out) is det. inside_non_condition(!CI) :- get_fail_info(!.CI, FailInfo0), FailInfo0 = fail_info(ResumePoints, ResumeKnown, CurfrMaxfr, _, Allow), FailInfo = fail_info(ResumePoints, ResumeKnown, CurfrMaxfr, inside_non_condition, Allow), set_fail_info(FailInfo, !CI). :- pred create_temp_frame(code_addr::in, string::in, llds_code::out, code_info::in, code_info::out) is det. create_temp_frame(Redoip, Comment, Code, !CI) :- ( get_proc_model(!.CI) = model_non -> Kind = nondet_stack_proc ; Kind = det_stack_proc ), Code = singleton( llds_instr(mkframe(temp_frame(Kind), yes(Redoip)), Comment) ), set_created_temp_frame(yes, !CI), get_fail_info(!.CI, FailInfo0), FailInfo0 = fail_info(ResumePoints, ResumeKnown, _, CondEnv, Allow), FailInfo = fail_info(ResumePoints, ResumeKnown, may_be_different, CondEnv, Allow), set_fail_info(FailInfo, !CI). %---------------------------------------------------------------------------% effect_resume_point(ResumePoint, CodeModel, Code, !CI) :- get_fail_info(!.CI, FailInfo0), FailInfo0 = fail_info(ResumePoints0, _ResumeKnown, CurfrMaxfr, CondEnv, Allow), ( stack.top(ResumePoints0, OldResumePoint) -> pick_first_resume_point(OldResumePoint, OldMap, _), 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), expect(set.subset(OldKeySet, NewKeySet), $module, $pred, "non-nested resume point variable sets") ; true ), stack.push(ResumePoint, ResumePoints0, ResumePoints), ( CodeModel = model_non, pick_stack_resume_point(ResumePoint, _, StackLabel), LabelConst = const(llconst_code_addr(StackLabel)), Code = singleton( llds_instr(assign(redoip_slot(lval(maxfr)), LabelConst), "hijack redoip to effect resume point") ), RedoipUpdate = has_been_done ; ( CodeModel = model_det ; CodeModel = model_semi ), Code = empty, RedoipUpdate = wont_be_done ), FailInfo = fail_info(ResumePoints, resume_point_known(RedoipUpdate), CurfrMaxfr, CondEnv, Allow), set_fail_info(FailInfo, !CI). pop_resume_point(!CI) :- get_fail_info(!.CI, FailInfo0), FailInfo0 = fail_info(ResumePoints0, ResumeKnown, CurfrMaxfr, CondEnv, Allow), stack.det_pop(_, ResumePoints0, ResumePoints), FailInfo = fail_info(ResumePoints, ResumeKnown, CurfrMaxfr, CondEnv, Allow), set_fail_info(FailInfo, !CI). %---------------------------------------------------------------------------% top_resume_point(CI, ResumePoint) :- get_fail_info(CI, FailInfo), FailInfo = fail_info(ResumePoints, _, _, _, _), stack.det_top(ResumePoints, ResumePoint). set_resume_point_to_unknown(!CI) :- get_fail_info(!.CI, FailInfo0), FailInfo0 = fail_info(ResumePoints, _, CurfrMaxfr, CondEnv, Allow), FailInfo = fail_info(ResumePoints, resume_point_unknown, CurfrMaxfr, CondEnv, Allow), set_fail_info(FailInfo, !CI). set_resume_point_and_frame_to_unknown(!CI) :- get_fail_info(!.CI, FailInfo0), FailInfo0 = fail_info(ResumePoints, _, _, CondEnv, Allow), FailInfo = fail_info(ResumePoints, resume_point_unknown, may_be_different, CondEnv, Allow), set_fail_info(FailInfo, !CI). %---------------------------------------------------------------------------% generate_failure(Code, !CI) :- get_fail_info(!.CI, FailInfo), FailInfo = fail_info(ResumePoints, ResumeKnown, _, _, _), ( ResumeKnown = resume_point_known(_), stack.det_top(ResumePoints, TopResumePoint), ( pick_matching_resume_addr(!.CI, TopResumePoint, FailureAddress0) -> FailureAddress = FailureAddress0, PlaceCode = empty ; pick_first_resume_point(TopResumePoint, Map, FailureAddress), map.to_assoc_list(Map, AssocList), remember_position(!.CI, CurPos), pick_and_place_vars(AssocList, _, PlaceCode, !CI), reset_to_position(CurPos, !CI) ), BranchCode = singleton(llds_instr(goto(FailureAddress), "fail")), Code = PlaceCode ++ BranchCode ; ResumeKnown = resume_point_unknown, Code = singleton(llds_instr(goto(do_redo), "fail")) ). fail_if_rval_is_false(Rval0, Code, !CI) :- get_fail_info(!.CI, FailInfo), FailInfo = fail_info(ResumePoints, ResumeKnown, _, _, _), ( ResumeKnown = resume_point_known(_), stack.det_top(ResumePoints, TopResumePoint), ( pick_matching_resume_addr(!.CI, TopResumePoint, FailureAddress0) -> % We branch away if the test *fails* code_util.neg_rval(Rval0, Rval), Code = singleton( llds_instr(if_val(Rval, FailureAddress0), "Test for failure") ) ; pick_first_resume_point(TopResumePoint, Map, FailureAddress), map.to_assoc_list(Map, AssocList), get_next_label(SuccessLabel, !CI), remember_position(!.CI, CurPos), pick_and_place_vars(AssocList, _, PlaceCode, !CI), reset_to_position(CurPos, !CI), SuccessAddress = code_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 = singleton( llds_instr(if_val(Rval0, SuccessAddress), "Test for failure") ), TailCode = from_list([ llds_instr(goto(FailureAddress), "Goto failure"), llds_instr(label(SuccessLabel), "Success continuation") ]), Code = TestCode ++ PlaceCode ++ TailCode ) ; ResumeKnown = resume_point_unknown, % We branch away if the test *fails* code_util.neg_rval(Rval0, Rval), Code = singleton( llds_instr(if_val(Rval, do_redo), "Test for failure") ) ). %---------------------------------------------------------------------------% failure_is_direct_branch(CI, CodeAddr) :- get_fail_info(CI, FailInfo), FailInfo = fail_info(ResumePoints, resume_point_known(_), _, _, _), stack.top(ResumePoints, TopResumePoint), pick_matching_resume_addr(CI, TopResumePoint, CodeAddr). may_use_nondet_tailcall(CI, TailCallStatus) :- get_fail_info(CI, FailInfo), FailInfo = fail_info(ResumePoints0, ResumeKnown, _, _, _), ( stack.pop(ResumePoint1, ResumePoints0, 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 pick_matching_resume_addr(code_info::in, resume_point_info::in, code_addr::out) is semidet. pick_matching_resume_addr(CI, ResumeMaps, Addr) :- variable_locations(CI, CurLocs), ( ResumeMaps = orig_only(Map1, Addr1), ( match_resume_loc(Map1, CurLocs) -> Addr = Addr1 ; fail ) ; ResumeMaps = stack_only(Map1, Addr1), ( match_resume_loc(Map1, CurLocs) -> Addr = Addr1 ; fail ) ; ResumeMaps = orig_and_stack(Map1, Addr1, Map2, Addr2), ( match_resume_loc(Map1, CurLocs) -> Addr = Addr1 ; match_resume_loc(Map2, CurLocs) -> Addr = Addr2 ; fail ) ; ResumeMaps = stack_and_orig(Map1, Addr1, Map2, Addr2), ( match_resume_loc(Map1, CurLocs) -> Addr = Addr1 ; match_resume_loc(Map2, CurLocs) -> Addr = Addr2 ; fail ) ). :- pred match_resume_loc(resume_map::in, resume_map::in) is semidet. 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), all_vars_match_resume_map(Map, List). :- pred all_vars_match_resume_map(resume_map::in, assoc_list(prog_var, set(lval))::in) is semidet. all_vars_match_resume_map(_Map, []). all_vars_match_resume_map(Map, [Var - Actual | VarsActuals]) :- map.search(Map, Var, Lvals), set.subset(Lvals, Actual), all_vars_match_resume_map(Map, VarsActuals). :- pred pick_first_resume_point(resume_point_info::in, resume_map::out, code_addr::out) is det. pick_first_resume_point(orig_only(Map, Addr), Map, Addr). pick_first_resume_point(stack_only(Map, Addr), Map, Addr). pick_first_resume_point(orig_and_stack(Map, Addr, _, _), Map, Addr). pick_first_resume_point(stack_and_orig(Map, Addr, _, _), Map, Addr). :- pred pick_stack_resume_point(resume_point_info::in, resume_map::out, code_addr::out) is det. pick_stack_resume_point(ResumePoint, Map, Addr) :- ( maybe_pick_stack_resume_point(ResumePoint, Map1, Addr1) -> Map = Map1, Addr = Addr1 ; unexpected($module, $pred, "no stack resume point") ). :- pred maybe_pick_stack_resume_point(resume_point_info::in, resume_map::out, code_addr::out) is semidet. maybe_pick_stack_resume_point(stack_only(Map, Addr), Map, Addr). maybe_pick_stack_resume_point(orig_and_stack(_, _, Map, Addr), Map, Addr). maybe_pick_stack_resume_point(stack_and_orig(Map, Addr, _, _), Map, Addr). %---------------------------------------------------------------------------% produce_vars([], Map, empty, !CI) :- map.init(Map). produce_vars([Var | Vars], Map, Code, !CI) :- produce_vars(Vars, Map0, CodeVars, !CI), produce_variable_in_reg_or_stack(Var, CodeVar, Lval, !CI), Lvals = set.make_singleton_set(Lval), map.set(Var, Lvals, Map0, Map), Code = CodeVars ++ CodeVar. flush_resume_vars_to_stack(Code, !CI) :- compute_resume_var_stack_locs(!.CI, VarLocs), place_vars(VarLocs, Code, !CI). :- pred compute_resume_var_stack_locs(code_info::in, assoc_list(prog_var, lval)::out) is det. compute_resume_var_stack_locs(CI, VarLocs) :- get_fail_info(CI, FailInfo), FailInfo = fail_info(ResumePointStack, _, _, _, _), stack.det_top(ResumePointStack, ResumePoint), pick_stack_resume_point(ResumePoint, StackMap, _), map.to_assoc_list(StackMap, VarLocSets), pick_var_places(VarLocSets, VarLocs). %---------------------------------------------------------------------------% :- pred init_fail_info(code_model::in, maybe(set_of_progvar)::in, resume_point_info::out, code_info::in, code_info::out) is det. init_fail_info(CodeModel, MaybeFailVars, ResumePoint, !CI) :- ( CodeModel = model_det, get_next_label(ResumeLabel, !CI), ResumeAddress = code_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. get_next_label(ResumeLabel, !CI), ResumeAddress = code_label(ResumeLabel), ResumeKnown = resume_point_known(wont_be_done), CurfrMaxfr = may_be_different ; CodeModel = model_non, ( MaybeFailVars = yes(_), get_next_label(ResumeLabel, !CI), ResumeAddress = code_label(ResumeLabel) ; MaybeFailVars = no, ResumeAddress = do_fail ), ResumeKnown = resume_point_known(has_been_done), CurfrMaxfr = must_be_equal ), ( MaybeFailVars = yes(FailVars), get_stack_slots(!.CI, StackSlots), map.select_sorted_list(StackSlots, set_of_var.to_sorted_list(FailVars), AbsStackMap), map.to_assoc_list(AbsStackMap, AbsStackList), StackList0 = assoc_list.map_values_only(stack_slot_to_lval, AbsStackList), make_singleton_sets(StackList0, StackList), map.from_sorted_assoc_list(StackList, StackMap) ; MaybeFailVars = no, map.init(StackMap) ), ResumePoint = stack_only(StackMap, ResumeAddress), stack.init(ResumeStack0), stack.push(ResumePoint, ResumeStack0, ResumeStack), get_fail_info(!.CI, FailInfo0), FailInfo0 = fail_info(_, _, _, _, Allow), FailInfo = fail_info(ResumeStack, ResumeKnown, CurfrMaxfr, not_inside_non_condition, Allow), set_fail_info(FailInfo, !CI). %---------------------------------------------------------------------------% make_resume_point(ResumeVars, ResumeLocs, FullMap, ResumePoint, !CI) :- get_stack_slots(!.CI, StackSlots), map.select_sorted_list(FullMap, ResumeVars, OrigMap), ( ResumeLocs = resume_locs_orig_only, get_next_label(OrigLabel, !CI), OrigAddr = code_label(OrigLabel), ResumePoint = orig_only(OrigMap, OrigAddr), trace [compiletime(flag("codegen_goal")), io(!IO)] ( ( should_trace_code_gen(!.CI) -> code_info.get_varset(!.CI, VarSet), io.write_string("orig_only\n", !IO), output_resume_map(VarSet, OrigMap, !IO), io.flush_output(!IO) ; true ) ) ; ResumeLocs = resume_locs_stack_only, make_stack_resume_map(ResumeVars, StackSlots, StackMap), get_next_label(StackLabel, !CI), StackAddr = code_label(StackLabel), ResumePoint = stack_only(StackMap, StackAddr), trace [compiletime(flag("codegen_goal")), io(!IO)] ( ( should_trace_code_gen(!.CI) -> code_info.get_varset(!.CI, VarSet), io.write_string("stack_only\n", !IO), output_resume_map(VarSet, StackMap, !IO), io.flush_output(!IO) ; true ) ) ; ResumeLocs = resume_locs_orig_and_stack, make_stack_resume_map(ResumeVars, StackSlots, StackMap), get_next_label(OrigLabel, !CI), OrigAddr = code_label(OrigLabel), get_next_label(StackLabel, !CI), StackAddr = code_label(StackLabel), ResumePoint = orig_and_stack(OrigMap, OrigAddr, StackMap, StackAddr), trace [compiletime(flag("codegen_goal")), io(!IO)] ( ( should_trace_code_gen(!.CI) -> code_info.get_varset(!.CI, VarSet), io.write_string("stack_and_orig\n", !IO), io.write_string("orig:\n", !IO), output_resume_map(VarSet, OrigMap, !IO), io.write_string("stack:\n", !IO), output_resume_map(VarSet, StackMap, !IO), io.flush_output(!IO) ; true ) ) ; ResumeLocs = resume_locs_stack_and_orig, make_stack_resume_map(ResumeVars, StackSlots, StackMap), get_next_label(StackLabel, !CI), StackAddr = code_label(StackLabel), get_next_label(OrigLabel, !CI), OrigAddr = code_label(OrigLabel), ResumePoint = stack_and_orig(StackMap, StackAddr, OrigMap, OrigAddr), trace [compiletime(flag("codegen_goal")), io(!IO)] ( ( should_trace_code_gen(!.CI) -> code_info.get_varset(!.CI, VarSet), io.write_string("stack_and_orig\n", !IO), io.write_string("stack:\n", !IO), output_resume_map(VarSet, StackMap, !IO), io.write_string("orig:\n", !IO), output_resume_map(VarSet, OrigMap, !IO), io.flush_output(!IO) ; true ) ) ). :- pred make_stack_resume_map(list(prog_var)::in, stack_slots::in, map(prog_var, set(lval))::out) is det. make_stack_resume_map(ResumeVars, StackSlots, StackMap) :- map.select_sorted_list(StackSlots, ResumeVars, StackMap0), map.to_assoc_list(StackMap0, AbsStackList), StackList0 = assoc_list.map_values_only(stack_slot_to_lval, AbsStackList), make_singleton_sets(StackList0, StackList), map.from_sorted_assoc_list(StackList, StackMap). :- pred make_singleton_sets(assoc_list(prog_var, lval)::in, assoc_list(prog_var, set(lval))::out) is det. make_singleton_sets([], []). make_singleton_sets([Var - Lval | Tail], [Var - Lvals | SetTail]) :- Lvals = set.make_singleton_set(Lval), make_singleton_sets(Tail, SetTail). %---------------------------------------------------------------------------% % 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). generate_resume_point(ResumePoint, Code, !CI) :- ( ResumePoint = orig_only(Map1, Addr1), extract_label_from_code_addr(Addr1, Label1), Code = singleton( llds_instr(label(Label1), "orig only failure continuation") ), set_var_locations(Map1, !CI) ; ResumePoint = stack_only(Map1, Addr1), extract_label_from_code_addr(Addr1, Label1), Code = singleton( llds_instr(label(Label1), "stack only failure continuation") ), set_var_locations(Map1, !CI), 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 = singleton( llds_instr(label(Label1), "stack failure continuation before orig") ), set_var_locations(Map1, !CI), generate_resume_layout(Label1, Map1, !CI), map.to_assoc_list(Map2, AssocList2), place_resume_vars(AssocList2, PlaceCode, !CI), Label2Code = singleton( llds_instr(label(Label2), "orig failure continuation after stack") ), set_var_locations(Map2, !CI), Code = Label1Code ++ 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 = singleton( llds_instr(label(Label1), "orig failure continuation before stack") ), set_var_locations(Map1, !CI), map.to_assoc_list(Map2, AssocList2), place_resume_vars(AssocList2, PlaceCode, !CI), Label2Code = singleton( llds_instr(label(Label2), "stack failure continuation after orig") ), set_var_locations(Map2, !CI), generate_resume_layout(Label2, Map2, !CI), Code = Label1Code ++ PlaceCode ++ Label2Code ). :- pred extract_label_from_code_addr(code_addr::in, label::out) is det. extract_label_from_code_addr(CodeAddr, Label) :- ( CodeAddr = code_label(Label0) -> Label = Label0 ; unexpected($module, $pred, "non-label") ). :- pred place_resume_vars(assoc_list(prog_var, set(lval))::in, llds_code::out, code_info::in, code_info::out) is det. place_resume_vars([], empty, !CI). place_resume_vars([Var - TargetSet | Rest], Code, !CI) :- set.to_sorted_list(TargetSet, Targets), place_resume_var(Var, Targets, FirstCode, !CI), place_resume_vars(Rest, RestCode, !CI), Code = FirstCode ++ RestCode. :- pred place_resume_var(prog_var::in, list(lval)::in, llds_code::out, code_info::in, code_info::out) is det. place_resume_var(_Var, [], empty, !CI). place_resume_var(Var, [Target | Targets], Code, !CI) :- place_var(Var, Target, FirstCode, !CI), place_resume_var(Var, Targets, RestCode, !CI), Code = 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 set_var_locations(resume_map::in, code_info::in, code_info::out) is det. set_var_locations(Map, !CI) :- map.to_assoc_list(Map, LvalList0), flatten_varlval_list(LvalList0, LvalList), get_var_locn_info(!.CI, VarLocnInfo0), reinit_var_locn_state(LvalList, VarLocnInfo0, VarLocnInfo), set_var_locn_info(VarLocnInfo, !CI). :- pred flatten_varlval_list(assoc_list(prog_var, set(lval))::in, assoc_list(prog_var, lval)::out) is det. flatten_varlval_list([], []). flatten_varlval_list([V - Rvals | Rest0], All) :- flatten_varlval_list(Rest0, Rest), set.to_sorted_list(Rvals, RvalList), flatten_varlval_list_2(RvalList, V, Rest1), list.append(Rest1, Rest, All). :- pred flatten_varlval_list_2(list(lval)::in, prog_var::in, assoc_list(prog_var, lval)::out) is det. flatten_varlval_list_2([], _V, []). flatten_varlval_list_2([R | Rs], V, [V - R | Rest]) :- flatten_varlval_list_2(Rs, V, Rest). resume_point_vars(ResumePoint, Vars) :- pick_first_resume_point(ResumePoint, ResumeMap, _), map.keys(ResumeMap, Vars). resume_point_stack_addr(ResumePoint, StackAddr) :- pick_stack_resume_point(ResumePoint, _, StackAddr). %---------------------------------------------------------------------------% :- pred maybe_save_trail_info(add_trail_ops::in, maybe(pair(lval))::out, llds_code::out, code_info::in, code_info::out) is det. maybe_save_trail_info(AddTrailOps, MaybeTrailSlots, SaveTrailCode, !CI) :- ( AddTrailOps = add_trail_ops, acquire_temp_slot(slot_ticket_counter, non_persistent_temp_slot, CounterSlot, !CI), acquire_temp_slot(slot_ticket, non_persistent_temp_slot, TrailPtrSlot, !CI), MaybeTrailSlots = yes(CounterSlot - TrailPtrSlot), SaveTrailCode = from_list([ llds_instr(mark_ticket_stack(CounterSlot), "save the ticket counter"), llds_instr(store_ticket(TrailPtrSlot), "save the trail pointer") ]) ; AddTrailOps = do_not_add_trail_ops, MaybeTrailSlots = no, SaveTrailCode = empty ). :- pred maybe_restore_trail_info(maybe(pair(lval))::in, llds_code::out, llds_code::out, code_info::in, code_info::out) is det. maybe_restore_trail_info(MaybeTrailSlots, CommitCode, RestoreCode, !CI) :- ( MaybeTrailSlots = no, CommitCode = empty, RestoreCode = empty ; MaybeTrailSlots = yes(CounterSlot - TrailPtrSlot), CommitCode = from_list([ llds_instr(reset_ticket(lval(TrailPtrSlot), reset_reason_commit), "discard trail entries and restore trail ptr"), llds_instr(prune_tickets_to(lval(CounterSlot)), "restore ticket counter (but not high water mark)") ]), RestoreCode = from_list([ llds_instr(reset_ticket(lval(TrailPtrSlot), reset_reason_undo), "apply trail entries and restore trail ptr"), llds_instr(discard_ticket, "restore ticket counter and high water mark") ]), release_temp_slot(CounterSlot, non_persistent_temp_slot, !CI), release_temp_slot(TrailPtrSlot, non_persistent_temp_slot, !CI) ). %---------------------------------------------------------------------------% :- pred clone_resume_point(resume_point_info::in, resume_point_info::out, code_info::in, code_info::out) is det. clone_resume_point(ResumePoint0, ResumePoint, !CI) :- ( ResumePoint0 = orig_only(_, _), unexpected($module, $pred, "cloning orig_only resume point") ; ResumePoint0 = stack_only(Map1, _), get_next_label(Label1, !CI), Addr1 = code_label(Label1), ResumePoint = stack_only(Map1, Addr1) ; ResumePoint0 = stack_and_orig(Map1, _, Map2, _), get_next_label(Label1, !CI), Addr1 = code_label(Label1), get_next_label(Label2, !CI), Addr2 = code_label(Label2), ResumePoint = stack_and_orig(Map1, Addr1, Map2, Addr2) ; ResumePoint0 = orig_and_stack(Map1, _, Map2, _), get_next_label(Label2, !CI), Addr2 = code_label(Label2), get_next_label(Label1, !CI), Addr1 = code_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 add_forward_live_vars(set_of_progvar::in, code_info::in, code_info::out) is det. :- pred get_known_variables(code_info::in, list(prog_var)::out) is det. :- pred variable_is_forward_live(code_info::in, prog_var::in) is semidet. :- pred make_vars_forward_dead(set_of_progvar::in, code_info::in, code_info::out) is det. :- pred maybe_make_vars_forward_dead(set_of_progvar::in, bool::in, code_info::in, code_info::out) is det. :- pred pickup_zombies(set_of_progvar::out, code_info::in, code_info::out) is det. %---------------------------------------------------------------------------% :- implementation. :- pred rem_forward_live_vars(set_of_progvar::in, code_info::in, code_info::out) is det. % Make these variables appear magically live. % We don't care where they are put. % :- pred make_vars_forward_live(set_of_progvar::in, code_info::in, code_info::out) is det. get_known_variables(CI, VarList) :- get_forward_live_vars(CI, ForwardLiveVars), ResumeVars = current_resume_point_vars(CI), set_of_var.union(ForwardLiveVars, ResumeVars, Vars), VarList = set_of_var.to_sorted_list(Vars). variable_is_forward_live(CI, Var) :- get_forward_live_vars(CI, Liveness), set_of_var.member(Liveness, Var). add_forward_live_vars(Births, !CI) :- get_forward_live_vars(!.CI, Liveness0), set_of_var.union(Liveness0, Births, Liveness), set_forward_live_vars(Liveness, !CI). rem_forward_live_vars(Deaths, !CI) :- get_forward_live_vars(!.CI, Liveness0), set_of_var.difference(Liveness0, Deaths, Liveness), set_forward_live_vars(Liveness, !CI). make_vars_forward_live(Vars, !CI) :- get_stack_slots(!.CI, StackSlots), get_var_locn_info(!.CI, VarLocnInfo0), VarList = set_of_var.to_sorted_list(Vars), make_vars_forward_live_2(VarList, StackSlots, 1, VarLocnInfo0, VarLocnInfo), set_var_locn_info(VarLocnInfo, !CI). :- pred make_vars_forward_live_2(list(prog_var)::in, stack_slots::in, int::in, var_locn_info::in, var_locn_info::out) is det. make_vars_forward_live_2([], _, _, !VarLocnInfo). make_vars_forward_live_2([Var | Vars], StackSlots, N0, !VarLocnInfo) :- ( map.search(StackSlots, Var, Slot) -> Lval = stack_slot_to_lval(Slot), N1 = N0 ; % reg_r is fine since we don't care where the variables are put. RegType = reg_r, find_unused_reg(!.VarLocnInfo, RegType, N0, N1), Lval = reg(RegType, N1) ), var_locn_set_magic_var_location(Var, Lval, !VarLocnInfo), make_vars_forward_live_2(Vars, StackSlots, N1, !VarLocnInfo). :- pred find_unused_reg(var_locn_info::in, reg_type::in, int::in, int::out) is det. find_unused_reg(VLI, RegType, N0, N) :- ( var_locn_lval_in_use(VLI, reg(RegType, N0)) -> find_unused_reg(VLI, RegType, N0 + 1, N) ; N = N0 ). make_vars_forward_dead(Vars, !CI) :- maybe_make_vars_forward_dead(Vars, yes, !CI). maybe_make_vars_forward_dead(Vars0, FirstTime, !CI) :- ResumeVars = current_resume_point_vars(!.CI), set_of_var.intersect(Vars0, ResumeVars, FlushVars), get_zombies(!.CI, Zombies0), set_of_var.union(Zombies0, FlushVars, Zombies), set_zombies(Zombies, !CI), set_of_var.difference(Vars0, Zombies, Vars), VarList = set_of_var.to_sorted_list(Vars), get_var_locn_info(!.CI, VarLocnInfo0), maybe_make_vars_forward_dead_2(VarList, FirstTime, VarLocnInfo0, VarLocnInfo), set_var_locn_info(VarLocnInfo, !CI). :- pred maybe_make_vars_forward_dead_2(list(prog_var)::in, bool::in, var_locn_info::in, var_locn_info::out) is det. maybe_make_vars_forward_dead_2([], _, !VLI). maybe_make_vars_forward_dead_2([Var | Vars], FirstTime, !VLI) :- var_locn_var_becomes_dead(Var, FirstTime, !VLI), maybe_make_vars_forward_dead_2(Vars, FirstTime, !VLI). pickup_zombies(Zombies, !CI) :- get_zombies(!.CI, Zombies), set_zombies(set_of_var.init, !CI). %---------------------------------------------------------------------------% %---------------------------------------------------------------------------% % Submodule for handling the saving and restoration % of trail tickets, heap pointers, stack pointers etc. :- interface. :- pred save_hp(llds_code::out, lval::out, code_info::in, code_info::out) is det. :- pred restore_hp(lval::in, llds_code::out) is det. :- pred release_hp(lval::in, code_info::in, code_info::out) is det. :- pred restore_and_release_hp(lval::in, llds_code::out, code_info::in, code_info::out) is det. :- pred maybe_save_hp(bool::in, llds_code::out, maybe(lval)::out, code_info::in, code_info::out) is det. :- pred maybe_restore_hp(maybe(lval)::in, llds_code::out) is det. :- pred maybe_release_hp(maybe(lval)::in, code_info::in, code_info::out) is det. :- pred maybe_restore_and_release_hp(maybe(lval)::in, llds_code::out, code_info::in, code_info::out) is det. :- pred save_ticket(llds_code::out, lval::out, code_info::in, code_info::out) is det. :- pred reset_ticket(lval::in, reset_trail_reason::in, llds_code::out) is det. :- pred release_ticket(lval::in, code_info::in, code_info::out) is det. :- pred reset_and_prune_ticket(lval::in, reset_trail_reason::in, llds_code::out) is det. :- pred reset_prune_and_release_ticket(lval::in, reset_trail_reason::in, llds_code::out, code_info::in, code_info::out) is det. :- pred reset_and_discard_ticket(lval::in, reset_trail_reason::in, llds_code::out) is det. :- pred reset_discard_and_release_ticket(lval::in, reset_trail_reason::in, llds_code::out, code_info::in, code_info::out) is det. :- pred discard_and_release_ticket(lval::in, llds_code::out, code_info::in, code_info::out) is det. :- pred maybe_save_ticket(add_trail_ops::in, llds_code::out, maybe(lval)::out, code_info::in, code_info::out) is det. :- pred maybe_reset_ticket(maybe(lval)::in, reset_trail_reason::in, llds_code::out) is det. :- pred maybe_release_ticket(maybe(lval)::in, code_info::in, code_info::out) is det. :- pred maybe_reset_and_prune_ticket(maybe(lval)::in, reset_trail_reason::in, llds_code::out) is det. :- pred maybe_reset_prune_and_release_ticket(maybe(lval)::in, reset_trail_reason::in, llds_code::out, code_info::in, code_info::out) is det. :- pred maybe_reset_and_discard_ticket(maybe(lval)::in, reset_trail_reason::in, llds_code::out) is det. :- pred maybe_reset_discard_and_release_ticket(maybe(lval)::in, reset_trail_reason::in, llds_code::out, code_info::in, code_info::out) is det. :- pred maybe_discard_and_release_ticket(maybe(lval)::in, llds_code::out, code_info::in, code_info::out) is det. % Should we add trail ops to the code we generate for the goal with the % given goal_info. This will be 'no' unless we are in a trailing grade. % :- func should_add_trail_ops(code_info, hlds_goal_info) = add_trail_ops. % Should we add region ops to the code we generate for the goal with the % given goal_info. This will be 'no' unless we are in a rbmm grade. % :- func should_add_region_ops(code_info, hlds_goal_info) = add_region_ops. %---------------------------------------------------------------------------% :- implementation. save_hp(Code, HpSlot, !CI) :- acquire_temp_slot(slot_lval(hp), non_persistent_temp_slot, HpSlot, !CI), Code = singleton( llds_instr(mark_hp(HpSlot), "Save heap pointer") ). restore_hp(HpSlot, Code) :- Code = singleton( llds_instr(restore_hp(lval(HpSlot)), "Restore heap pointer") ). release_hp(HpSlot, !CI) :- release_temp_slot(HpSlot, non_persistent_temp_slot, !CI). restore_and_release_hp(HpSlot, Code, !CI) :- restore_hp(HpSlot, Code), release_hp(HpSlot, !CI). %---------------------------------------------------------------------------% maybe_save_hp(Maybe, Code, MaybeHpSlot, !CI) :- ( Maybe = yes, save_hp(Code, HpSlot, !CI), MaybeHpSlot = yes(HpSlot) ; Maybe = no, Code = empty, MaybeHpSlot = no ). maybe_restore_hp(MaybeHpSlot, Code) :- ( MaybeHpSlot = yes(HpSlot), restore_hp(HpSlot, Code) ; MaybeHpSlot = no, Code = empty ). maybe_release_hp(MaybeHpSlot, !CI) :- ( MaybeHpSlot = yes(HpSlot), release_hp(HpSlot, !CI) ; MaybeHpSlot = no ). maybe_restore_and_release_hp(MaybeHpSlot, Code, !CI) :- ( MaybeHpSlot = yes(HpSlot), restore_and_release_hp(HpSlot, Code, !CI) ; MaybeHpSlot = no, Code = empty ). %---------------------------------------------------------------------------% save_ticket(Code, TicketSlot, !CI) :- acquire_temp_slot(slot_ticket, non_persistent_temp_slot, TicketSlot, !CI), Code = singleton( llds_instr(store_ticket(TicketSlot), "Save trail state") ). reset_ticket(TicketSlot, Reason, Code) :- Code = singleton( llds_instr(reset_ticket(lval(TicketSlot), Reason), "Reset trail") ). release_ticket(TicketSlot, !CI) :- release_temp_slot(TicketSlot, non_persistent_temp_slot, !CI). reset_and_prune_ticket(TicketSlot, Reason, Code) :- Code = from_list([ llds_instr(reset_ticket(lval(TicketSlot), Reason), "Restore trail"), llds_instr(prune_ticket, "Prune ticket stack") ]). reset_prune_and_release_ticket(TicketSlot, Reason, Code, !CI) :- Code = from_list([ llds_instr(reset_ticket(lval(TicketSlot), Reason), "Release trail"), llds_instr(prune_ticket, "Prune ticket stack") ]), release_temp_slot(TicketSlot, non_persistent_temp_slot, !CI). reset_and_discard_ticket(TicketSlot, Reason, Code) :- Code = from_list([ llds_instr(reset_ticket(lval(TicketSlot), Reason), "Restore trail"), llds_instr(discard_ticket, "Pop ticket stack") ]). reset_discard_and_release_ticket(TicketSlot, Reason, Code, !CI) :- Code = from_list([ llds_instr(reset_ticket(lval(TicketSlot), Reason), "Release trail"), llds_instr(discard_ticket, "Pop ticket stack") ]), release_temp_slot(TicketSlot, non_persistent_temp_slot, !CI). discard_and_release_ticket(TicketSlot, Code, !CI) :- Code = singleton( llds_instr(discard_ticket, "Pop ticket stack") ), release_temp_slot(TicketSlot, non_persistent_temp_slot, !CI). %---------------------------------------------------------------------------% maybe_save_ticket(AddTrailOps, Code, MaybeTicketSlot, !CI) :- ( AddTrailOps = add_trail_ops, save_ticket(Code, TicketSlot, !CI), MaybeTicketSlot = yes(TicketSlot) ; AddTrailOps = do_not_add_trail_ops, Code = empty, MaybeTicketSlot = no ). maybe_reset_ticket(MaybeTicketSlot, Reason, Code) :- ( MaybeTicketSlot = yes(TicketSlot), reset_ticket(TicketSlot, Reason, Code) ; MaybeTicketSlot = no, Code = empty ). maybe_release_ticket(MaybeTicketSlot, !CI) :- ( MaybeTicketSlot = yes(TicketSlot), release_ticket(TicketSlot, !CI) ; MaybeTicketSlot = no ). maybe_reset_and_prune_ticket(MaybeTicketSlot, Reason, Code) :- ( MaybeTicketSlot = yes(TicketSlot), reset_and_prune_ticket(TicketSlot, Reason, Code) ; MaybeTicketSlot = no, Code = empty ). maybe_reset_prune_and_release_ticket(MaybeTicketSlot, Reason, Code, !CI) :- ( MaybeTicketSlot = yes(TicketSlot), reset_prune_and_release_ticket(TicketSlot, Reason, Code, !CI) ; MaybeTicketSlot = no, Code = empty ). maybe_reset_and_discard_ticket(MaybeTicketSlot, Reason, Code) :- ( MaybeTicketSlot = yes(TicketSlot), reset_and_discard_ticket(TicketSlot, Reason, Code) ; MaybeTicketSlot = no, Code = empty ). maybe_reset_discard_and_release_ticket(MaybeTicketSlot, Reason, Code, !CI) :- ( MaybeTicketSlot = yes(TicketSlot), reset_discard_and_release_ticket(TicketSlot, Reason, Code, !CI) ; MaybeTicketSlot = no, Code = empty ). maybe_discard_and_release_ticket(MaybeTicketSlot, Code, !CI) :- ( MaybeTicketSlot = yes(TicketSlot), discard_and_release_ticket(TicketSlot, Code, !CI) ; MaybeTicketSlot = no, Code = empty ). % XXX We will eventually need to make use of GoalInfo here. % should_add_trail_ops(CodeInfo, _GoalInfo) = AddTrailOps :- get_emit_trail_ops(CodeInfo, AddTrailOps). % XXX We will eventually need to make use of GoalInfo here. % should_add_region_ops(CodeInfo, _GoalInfo) = AddRegionOps :- get_emit_region_ops(CodeInfo, AddRegionOps). %---------------------------------------------------------------------------% %---------------------------------------------------------------------------% % Submodule to deal with var_locn. % Most of these procedures just forward to the var_locn module. % See var_locn for documentation. :- interface. :- pred variable_locations(code_info::in, map(prog_var, set(lval))::out) is det. :- pred set_var_location(prog_var::in, lval::in, code_info::in, code_info::out) is det. :- pred assign_var_to_var(prog_var::in, prog_var::in, code_info::in, code_info::out) is det. :- pred assign_lval_to_var(prog_var::in, lval::in, llds_code::out, code_info::in, code_info::out) is det. :- pred assign_const_to_var(prog_var::in, rval::in, code_info::in, code_info::out) is det. :- pred assign_expr_to_var(prog_var::in, rval::in, llds_code::out, code_info::in, code_info::out) is det. :- pred reassign_mkword_hole_var(prog_var::in, tag::in, rval::in, llds_code::out, code_info::in, code_info::out) is det. :- pred assign_field_lval_expr_to_var(prog_var::in, list(lval)::in, rval::in, llds_code::out, code_info::in, code_info::out) is det. % assign_cell_to_var(Var, ReserveWordAtStart, Ptag, MaybeRvals, % AllFilled, MaybeSize, FieldAddrs, TypeMsg, MayUseAtomic, Where, % Code, !CI). % :- pred assign_cell_to_var(prog_var::in, bool::in, tag::in, list(cell_arg)::in, how_to_construct::in, maybe(term_size_value)::in, maybe(alloc_site_id)::in, may_use_atomic_alloc::in, llds_code::out, code_info::in, code_info::out) is det. :- pred save_reused_cell_fields(prog_var::in, lval::in, llds_code::out, list(lval)::out, code_info::in, code_info::out) is det. :- pred place_var(prog_var::in, lval::in, llds_code::out, code_info::in, code_info::out) is det. :- pred produce_variable(prog_var::in, llds_code::out, rval::out, code_info::in, code_info::out) is det. :- pred produce_variable_in_reg(prog_var::in, llds_code::out, lval::out, code_info::in, code_info::out) is det. :- pred produce_variable_in_reg_or_stack(prog_var::in, llds_code::out, lval::out, code_info::in, code_info::out) is det. :- pred materialize_vars_in_lval(lval::in, lval::out, llds_code::out, code_info::in, code_info::out) is det. :- pred acquire_reg_for_var(prog_var::in, reg_type::in, lval::out, code_info::in, code_info::out) is det. :- pred acquire_reg_not_in_storemap(abs_store_map::in, reg_type::in, lval::out, code_info::in, code_info::out) is det. :- pred acquire_reg(reg_type::in, lval::out, code_info::in, code_info::out) is det. :- pred release_reg(lval::in, code_info::in, code_info::out) is det. :- pred reserve_r1(llds_code::out, code_info::in, code_info::out) is det. :- pred clear_r1(llds_code::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 setup_call(hlds_goal_info::in, assoc_list(prog_var, arg_info)::in, set(lval)::out, llds_code::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 setup_return(assoc_list(prog_var, arg_info)::in, set(lval)::out, llds_code::out, code_info::in, code_info::out) is det. :- pred lock_regs(int::in, int::in, assoc_list(prog_var, lval)::in, code_info::in, code_info::out) is det. :- pred 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 clear_all_registers(bool::in, code_info::in, code_info::out) is det. :- pred clobber_regs(list(lval)::in, code_info::in, code_info::out) is det. :- pred save_variables(set_of_progvar::in, set(lval)::out, llds_code::out, code_info::in, code_info::out) is det. :- pred save_variables_on_stack(list(prog_var)::in, llds_code::out, code_info::in, code_info::out) is det. :- pred max_reg_in_use(code_info::in, int::out, int::out) is det. :- pred magically_put_var_in_unused_reg(prog_var::in, code_info::in, code_info::out) is det. %---------------------------------------------------------------------------% :- implementation. variable_locations(CI, Lvals) :- get_var_locn_info(CI, VarLocnInfo), var_locn_get_var_locations(VarLocnInfo, Lvals). :- func rval_map_to_lval_map(prog_var, set(rval)) = set(lval). rval_map_to_lval_map(_Var, Rvals) = set.filter_map(rval_is_lval, Rvals). :- func rval_is_lval(rval) = lval is semidet. rval_is_lval(lval(Lval)) = Lval. set_var_location(Var, Lval, !CI) :- get_var_locn_info(!.CI, VarLocnInfo0), var_locn_check_and_set_magic_var_location(Var, Lval, VarLocnInfo0, VarLocnInfo), set_var_locn_info(VarLocnInfo, !CI). assign_var_to_var(Var, AssignedVar, !CI) :- get_var_locn_info(!.CI, VarLocnInfo0), var_locn_assign_var_to_var(Var, AssignedVar, VarLocnInfo0, VarLocnInfo), set_var_locn_info(VarLocnInfo, !CI). assign_lval_to_var(Var, Lval, Code, !CI) :- get_var_locn_info(!.CI, VarLocnInfo0), get_static_cell_info(!.CI, StaticCellInfo), get_module_info(!.CI, ModuleInfo), var_locn_assign_lval_to_var(ModuleInfo, Var, Lval, StaticCellInfo, Code, VarLocnInfo0, VarLocnInfo), set_var_locn_info(VarLocnInfo, !CI). assign_const_to_var(Var, ConstRval, !CI) :- get_var_locn_info(!.CI, VarLocnInfo0), get_exprn_opts(!.CI, ExprnOpts), var_locn_assign_const_to_var(ExprnOpts, Var, ConstRval, VarLocnInfo0, VarLocnInfo), set_var_locn_info(VarLocnInfo, !CI). assign_expr_to_var(Var, Rval, Code, !CI) :- get_var_locn_info(!.CI, VarLocnInfo0), Lvals = lvals_in_rval(Rval), ( Lvals = [], var_locn_assign_expr_to_var(Var, Rval, Code, VarLocnInfo0, VarLocnInfo) ; Lvals = [_ | _], unexpected($module, $pred, "non-var lvals") ), set_var_locn_info(VarLocnInfo, !CI). reassign_mkword_hole_var(Var, Ptag, Rval, Code, !CI) :- get_var_locn_info(!.CI, VarLocnInfo0), Lvals = lvals_in_rval(Rval), ( Lvals = [], var_locn_reassign_mkword_hole_var(Var, Ptag, Rval, Code, VarLocnInfo0, VarLocnInfo) ; Lvals = [_ | _], unexpected($module, $pred, "non-var lvals") ), set_var_locn_info(VarLocnInfo, !CI). assign_field_lval_expr_to_var(Var, FieldLvals, Rval, Code, !CI) :- ( FieldLvals = [field(MaybeTag, var(BaseVar), _) | RestFieldLvals], list.all_true(is_var_field(MaybeTag, BaseVar), RestFieldLvals) -> ( Lvals = lvals_in_rval(Rval), all [Lval] ( list.member(Lval, Lvals) => list.member(Lval, FieldLvals) ) -> get_var_locn_info(!.CI, VarLocnInfo0), var_locn_assign_field_lval_expr_to_var(Var, BaseVar, Rval, Code, VarLocnInfo0, VarLocnInfo), set_var_locn_info(VarLocnInfo, !CI) ; unexpected($module, $pred, "rval contains unexpected lval") ) ; unexpected($module, $pred, "FieldLvals not all fields of the same base variable") ). :- pred is_var_field(maybe(tag)::in, prog_var::in, lval::in) is semidet. is_var_field(MaybeTag, Var, field(MaybeTag, var(Var), _)). assign_cell_to_var(Var, ReserveWordAtStart, Ptag, CellArgs, HowToConstruct, MaybeSize, MaybeAllocId, MayUseAtomic, Code, !CI) :- get_next_label(Label, !CI), get_var_locn_info(!.CI, VarLocnInfo0), get_static_cell_info(!.CI, StaticCellInfo0), get_module_info(!.CI, ModuleInfo), get_exprn_opts(!.CI, ExprnOpts), var_locn_assign_cell_to_var(ModuleInfo, ExprnOpts, Var, ReserveWordAtStart, Ptag, CellArgs, HowToConstruct, MaybeSize, MaybeAllocId, MayUseAtomic, Label, Code, StaticCellInfo0, StaticCellInfo, VarLocnInfo0, VarLocnInfo), set_static_cell_info(StaticCellInfo, !CI), set_var_locn_info(VarLocnInfo, !CI). save_reused_cell_fields(Var, Lval, Code, Regs, !CI) :- get_var_locn_info(!.CI, VarLocnInfo0), get_module_info(!.CI, ModuleInfo), var_locn_save_cell_fields(ModuleInfo, Var, Lval, Code, Regs, VarLocnInfo0, VarLocnInfo), set_var_locn_info(VarLocnInfo, !CI). place_var(Var, Lval, Code, !CI) :- get_var_locn_info(!.CI, VarLocnInfo0), get_module_info(!.CI, ModuleInfo), var_locn_place_var(ModuleInfo, Var, Lval, Code, VarLocnInfo0, VarLocnInfo), set_var_locn_info(VarLocnInfo, !CI). :- pred pick_and_place_vars(assoc_list(prog_var, set(lval))::in, set(lval)::out, llds_code::out, code_info::in, code_info::out) is det. pick_and_place_vars(VarLocSets, LiveLocs, Code, !CI) :- pick_var_places(VarLocSets, VarLocs), assoc_list.values(VarLocs, Locs), set.list_to_set(Locs, LiveLocs), place_vars(VarLocs, Code, !CI). :- pred pick_var_places(assoc_list(prog_var, set(lval))::in, assoc_list(prog_var, lval)::out) is det. pick_var_places([], []). pick_var_places([Var - LvalSet | VarLvalSets], VarLvals) :- pick_var_places(VarLvalSets, VarLvals0), ( set.to_sorted_list(LvalSet, LvalList), LvalList = [Lval | _] -> VarLvals = [Var - Lval | VarLvals0] ; VarLvals = VarLvals0 ). :- pred place_vars(assoc_list(prog_var, lval)::in, llds_code::out, code_info::in, code_info::out) is det. place_vars(VarLocs, Code, !CI) :- get_var_locn_info(!.CI, VarLocnInfo0), get_module_info(!.CI, ModuleInfo), var_locn_place_vars(ModuleInfo, VarLocs, Code, VarLocnInfo0, VarLocnInfo), set_var_locn_info(VarLocnInfo, !CI). produce_variable(Var, Code, Rval, !CI) :- get_var_locn_info(!.CI, VarLocnInfo0), get_module_info(!.CI, ModuleInfo), var_locn_produce_var(ModuleInfo, Var, Rval, Code, VarLocnInfo0, VarLocnInfo), set_var_locn_info(VarLocnInfo, !CI). produce_variable_in_reg(Var, Code, Lval, !CI) :- get_var_locn_info(!.CI, VarLocnInfo0), get_module_info(!.CI, ModuleInfo), var_locn_produce_var_in_reg(ModuleInfo, Var, Lval, Code, VarLocnInfo0, VarLocnInfo), set_var_locn_info(VarLocnInfo, !CI). produce_variable_in_reg_or_stack(Var, Code, Lval, !CI) :- get_var_locn_info(!.CI, VarLocnInfo0), get_module_info(!.CI, ModuleInfo), var_locn_produce_var_in_reg_or_stack(ModuleInfo, Var, Lval, Code, VarLocnInfo0, VarLocnInfo), set_var_locn_info(VarLocnInfo, !CI). materialize_vars_in_lval(Lval0, Lval, Code, !CI) :- get_var_locn_info(!.CI, VarLocnInfo0), get_module_info(!.CI, ModuleInfo), var_locn_materialize_vars_in_lval(ModuleInfo, Lval0, Lval, Code, VarLocnInfo0, VarLocnInfo), set_var_locn_info(VarLocnInfo, !CI). acquire_reg_for_var(Var, RegType, Lval, !CI) :- get_follow_var_map(!.CI, FollowVarsMap), get_next_non_reserved(!.CI, RegType, NextNonReserved), get_var_locn_info(!.CI, VarLocnInfo0), ( map.search(FollowVarsMap, Var, PrefLocn), PrefLocn = abs_reg(RegType, PrefRegNum), PrefRegNum >= 1 -> var_locn_acquire_reg_prefer_given(RegType, 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(RegType, NextNonReserved, Lval, VarLocnInfo0, VarLocnInfo) ), set_var_locn_info(VarLocnInfo, !CI). acquire_reg_not_in_storemap(StoreMap, RegType, Lval, !CI) :- map.foldl2(record_highest_used_reg, StoreMap, 0, HighestUsedRegR, 0, HighestUsedRegF), get_var_locn_info(!.CI, VarLocnInfo0), ( RegType = reg_r, NextRegNum = HighestUsedRegR + 1 ; RegType = reg_f, NextRegNum = HighestUsedRegF + 1 ), var_locn_acquire_reg_start_at_given(RegType, NextRegNum, Lval, VarLocnInfo0, VarLocnInfo), set_var_locn_info(VarLocnInfo, !CI). :- pred record_highest_used_reg(prog_var::in, abs_locn::in, int::in, int::out, int::in, int::out) is det. record_highest_used_reg(_, AbsLocn, !HighestUsedRegR, !HighestUsedRegF) :- ( AbsLocn = any_reg ; AbsLocn = abs_reg(reg_r, N), int.max(N, !HighestUsedRegR) ; AbsLocn = abs_reg(reg_f, N), int.max(N, !HighestUsedRegF) ; AbsLocn = abs_stackvar(_, _) ; AbsLocn = abs_parent_stackvar(_, _) ; AbsLocn = abs_framevar(_, _) ). acquire_reg(Type, Lval, !CI) :- get_var_locn_info(!.CI, VarLocnInfo0), var_locn_acquire_reg(Type, Lval, VarLocnInfo0, VarLocnInfo), set_var_locn_info(VarLocnInfo, !CI). release_reg(Lval, !CI) :- get_var_locn_info(!.CI, VarLocnInfo0), var_locn_release_reg(Lval, VarLocnInfo0, VarLocnInfo), set_var_locn_info(VarLocnInfo, !CI). reserve_r1(Code, !CI) :- get_var_locn_info(!.CI, VarLocnInfo0), get_module_info(!.CI, ModuleInfo), var_locn_clear_r1(ModuleInfo, Code, VarLocnInfo0, VarLocnInfo1), var_locn_acquire_reg_require_given(reg(reg_r, 1), VarLocnInfo1, VarLocnInfo), set_var_locn_info(VarLocnInfo, !CI). clear_r1(empty, !CI) :- get_var_locn_info(!.CI, VarLocnInfo0), var_locn_release_reg(reg(reg_r, 1), VarLocnInfo0, VarLocnInfo), set_var_locn_info(VarLocnInfo, !CI). %---------------------------------------------------------------------------% setup_return(VarArgInfos, OutLocs, Code, !CI) :- setup_call_args(VarArgInfos, callee, OutLocs, Code, !CI). setup_call(GoalInfo, ArgInfos, LiveLocs, Code, !CI) :- partition_args(ArgInfos, InArgInfos, OutArgInfos, _UnusedArgInfos), assoc_list.keys(OutArgInfos, OutVars), set.list_to_set(OutVars, OutVarSet), Detism = goal_info_get_determinism(GoalInfo), get_opt_no_return_calls(!.CI, OptNoReturnCalls), get_module_info(!.CI, ModuleInfo), VarTypes = get_var_types(!.CI), ( Detism = detism_erroneous, OptNoReturnCalls = yes -> RealStackVarLocs = [], DummyStackVarLocs = [] ; compute_forward_live_var_saves(!.CI, set_to_bitset(OutVarSet), ForwardVarLocs), CodeModel = goal_info_get_code_model(GoalInfo), ( 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. compute_resume_var_stack_locs(!.CI, ResumeVarLocs), list.append(ResumeVarLocs, ForwardVarLocs, StackVarLocs) ; ( CodeModel = model_det ; CodeModel = model_semi ), StackVarLocs = ForwardVarLocs ), list.filter(valid_stack_slot(ModuleInfo, VarTypes), StackVarLocs, RealStackVarLocs, DummyStackVarLocs) ), get_var_locn_info(!.CI, VarLocnInfo0), list.filter(key_var_is_of_non_dummy_type(ModuleInfo, VarTypes), InArgInfos, RealInArgInfos), var_arg_info_to_lval(RealInArgInfos, RealInArgLocs), AllRealLocs = RealStackVarLocs ++ RealInArgLocs, AllLocs = DummyStackVarLocs ++ AllRealLocs, var_locn_place_vars(ModuleInfo, AllLocs, Code, VarLocnInfo0, VarLocnInfo), set_var_locn_info(VarLocnInfo, !CI), assoc_list.values(AllRealLocs, LiveLocList), set.list_to_set(LiveLocList, LiveLocs). :- pred key_var_is_of_non_dummy_type(module_info::in, vartypes::in, pair(prog_var, arg_info)::in) is semidet. key_var_is_of_non_dummy_type(ModuleInfo, VarTypes, Var - _ArgInfo) :- var_is_of_non_dummy_type(ModuleInfo, VarTypes, Var). :- pred valid_stack_slot(module_info::in, vartypes::in, pair(prog_var, lval)::in) is semidet. valid_stack_slot(ModuleInfo, VarTypes, Var - Lval) :- lookup_var_type(VarTypes, Var, Type), check_dummy_type(ModuleInfo, Type) = is_not_dummy_type, ( ( Lval = stackvar(N) ; Lval = parent_stackvar(N) ; Lval = framevar(N) ), N < 0 -> unexpected($module, $pred, "nondummy var in dummy stack slot") ; true ). :- pred setup_call_args(assoc_list(prog_var, arg_info)::in, call_direction::in, set(lval)::out, llds_code::out, code_info::in, code_info::out) is det. setup_call_args(AllArgsInfos, Direction, LiveLocs, Code, !CI) :- list.filter(call_arg_in_selected_dir(Direction), AllArgsInfos, ArgsInfos), var_arg_info_to_lval(ArgsInfos, ArgsLocns), get_module_info(!.CI, ModuleInfo), get_var_locn_info(!.CI, VarLocnInfo0), var_locn_place_vars(ModuleInfo, ArgsLocns, Code, VarLocnInfo0, VarLocnInfo1), set_var_locn_info(VarLocnInfo1, !CI), assoc_list.values(ArgsLocns, LiveLocList), set.list_to_set(LiveLocList, LiveLocs), assoc_list.keys(ArgsLocns, ArgVars), which_variables_are_forward_live(!.CI, ArgVars, set_of_var.init, DeadVars), make_vars_forward_dead(DeadVars, !CI). :- pred var_arg_info_to_lval(assoc_list(prog_var, arg_info)::in, assoc_list(prog_var, lval)::out) is det. var_arg_info_to_lval([], []). var_arg_info_to_lval([Var - ArgInfo | RestInfos], [Var - Lval | RestLvals]) :- ArgInfo = arg_info(Loc, _Mode), code_util.arg_loc_to_register(Loc, Lval), var_arg_info_to_lval(RestInfos, RestLvals). :- pred which_variables_are_forward_live(code_info::in, list(prog_var)::in, set_of_progvar::in, set_of_progvar::out) is det. which_variables_are_forward_live(_, [], !DeadVars). which_variables_are_forward_live(CI, [Var | Vars], !DeadVars) :- ( variable_is_forward_live(CI, Var) -> true ; set_of_var.insert(Var, !DeadVars) ), which_variables_are_forward_live(CI, Vars, !DeadVars). :- pred call_arg_in_selected_dir(call_direction::in, pair(prog_var, arg_info)::in) is semidet. call_arg_in_selected_dir(Direction, _ - arg_info(_, Mode)) :- ( Mode = top_in, Direction = caller ; Mode = top_out, Direction = callee ). lock_regs(R, F, Exceptions, !CI) :- get_var_locn_info(!.CI, VarLocnInfo0), var_locn_lock_regs(R, F, Exceptions, VarLocnInfo0, VarLocnInfo), set_var_locn_info(VarLocnInfo, !CI). unlock_regs(!CI) :- get_var_locn_info(!.CI, VarLocnInfo0), var_locn_unlock_regs(VarLocnInfo0, VarLocnInfo), set_var_locn_info(VarLocnInfo, !CI). clear_all_registers(OkToDeleteAny, !CI) :- get_var_locn_info(!.CI, VarLocnInfo0), var_locn_clobber_all_regs(OkToDeleteAny, VarLocnInfo0, VarLocnInfo), set_var_locn_info(VarLocnInfo, !CI). clobber_regs(Regs, !CI) :- get_var_locn_info(!.CI, VarLocnInfo0), var_locn_clobber_regs(Regs, VarLocnInfo0, VarLocnInfo), set_var_locn_info(VarLocnInfo, !CI). save_variables(OutArgs, SavedLocs, Code, !CI) :- compute_forward_live_var_saves(!.CI, OutArgs, VarLocs), assoc_list.values(VarLocs, SavedLocList), set.list_to_set(SavedLocList, SavedLocs), place_vars(VarLocs, Code, !CI). save_variables_on_stack(Vars, Code, !CI) :- list.map(associate_stack_slot(!.CI), Vars, VarLocs), place_vars(VarLocs, Code, !CI). :- pred compute_forward_live_var_saves(code_info::in, set_of_progvar::in, assoc_list(prog_var, lval)::out) is det. compute_forward_live_var_saves(CI, OutArgs, VarLocs) :- get_known_variables(CI, Variables0), Vars0 = set_of_var.list_to_set(Variables0), TypeInfoLiveness = body_typeinfo_liveness(CI), get_proc_info(CI, ProcInfo), proc_info_get_vartypes(ProcInfo, VarTypes), proc_info_get_rtti_varmaps(ProcInfo, RttiVarMaps), maybe_complete_with_typeinfo_vars(Vars0, TypeInfoLiveness, VarTypes, RttiVarMaps, Vars1), set_of_var.difference(Vars1, OutArgs, Vars), Variables = set_of_var.to_sorted_list(Vars), list.map(associate_stack_slot(CI), Variables, VarLocs). :- pred associate_stack_slot(code_info::in, prog_var::in, pair(prog_var, lval)::out) is det. associate_stack_slot(CI, Var, Var - Slot) :- get_variable_slot(CI, Var, Slot). max_reg_in_use(CI, MaxR, MaxF) :- get_var_locn_info(CI, VarLocnInfo), var_locn_max_reg_in_use(VarLocnInfo, MaxR, MaxF). magically_put_var_in_unused_reg(Var, !CI) :- get_var_locn_info(!.CI, VarLocnInfo0), make_vars_forward_live_2([Var], map.init, 1, VarLocnInfo0, VarLocnInfo), set_var_locn_info(VarLocnInfo, !CI). %---------------------------------------------------------------------------% %---------------------------------------------------------------------------% % 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 generate_call_vn_livevals(code_info::in, list(arg_loc)::in, set_of_progvar::in, set(lval)::out) is det. :- pred generate_return_live_lvalues(code_info::in, assoc_list(prog_var, arg_loc)::in, instmap::in, bool::in, list(liveinfo)::out) is det. %---------------------------------------------------------------------------% :- implementation. generate_call_vn_livevals(CI, InputArgLocs, OutputArgs, LiveVals) :- generate_call_stack_vn_livevals(CI, OutputArgs, StackLiveVals), generate_input_var_vn(InputArgLocs, StackLiveVals, LiveVals). :- pred generate_call_stack_vn_livevals(code_info::in, set_of_progvar::in, set(lval)::out) is det. generate_call_stack_vn_livevals(CI, OutputArgs, LiveVals) :- get_known_variables(CI, KnownVarList0), get_module_info(CI, ModuleInfo), VarTypes = get_var_types(CI), list.filter(var_is_of_non_dummy_type(ModuleInfo, VarTypes), KnownVarList0, KnownVarList), set_of_var.list_to_set(KnownVarList, KnownVars), set_of_var.difference(KnownVars, OutputArgs, LiveVars), set_of_var.to_sorted_list(LiveVars, LiveVarList), generate_stack_var_vn(CI, LiveVarList, set.init, LiveVals1), get_active_temps_data(CI, Temps), generate_call_temp_vn(Temps, LiveVals1, LiveVals). :- pred generate_stack_var_vn(code_info::in, list(prog_var)::in, set(lval)::in, set(lval)::out) is det. generate_stack_var_vn(_, [], !Vals). generate_stack_var_vn(CI, [V | Vs], !Vals) :- get_variable_slot(CI, V, Lval), set.insert(Lval, !Vals), generate_stack_var_vn(CI, Vs, !Vals). :- pred generate_call_temp_vn(assoc_list(lval, slot_contents)::in, set(lval)::in, set(lval)::out) is det. generate_call_temp_vn([], !Vals). generate_call_temp_vn([Lval - _ | Temps], !Vals) :- set.insert(Lval, !Vals), generate_call_temp_vn(Temps, !Vals). :- pred generate_input_var_vn(list(arg_loc)::in, set(lval)::in, set(lval)::out) is det. generate_input_var_vn([], !Vals). generate_input_var_vn([InputArgLoc | InputArgLocs], !Vals) :- code_util.arg_loc_to_register(InputArgLoc, Lval), set.insert(Lval, !Vals), generate_input_var_vn(InputArgLocs, !Vals). %---------------------------------------------------------------------------% generate_return_live_lvalues(CI, OutputArgLocs, ReturnInstMap, OkToDeleteAny, LiveLvalues) :- variable_locations(CI, VarLocs), get_known_variables(CI, Vars0), get_module_info(CI, ModuleInfo), VarTypes = get_var_types(CI), list.filter(var_is_of_non_dummy_type(ModuleInfo, VarTypes), Vars0, Vars), get_active_temps_data(CI, Temps), get_proc_info(CI, ProcInfo), get_globals(CI, Globals), continuation_info.generate_return_live_lvalues(OutputArgLocs, ReturnInstMap, Vars, VarLocs, Temps, ProcInfo, ModuleInfo, Globals, OkToDeleteAny, LiveLvalues). :- pred generate_resume_layout(label::in, resume_map::in, code_info::in, code_info::out) is det. generate_resume_layout(Label, ResumeMap, !CI) :- get_globals(!.CI, Globals), globals.lookup_bool_option(Globals, agc_stack_layout, AgcStackLayout), ( AgcStackLayout = yes, get_active_temps_data(!.CI, Temps), get_instmap(!.CI, InstMap), get_proc_info(!.CI, ProcInfo), get_module_info(!.CI, ModuleInfo), continuation_info.generate_resume_layout(ResumeMap, Temps, InstMap, ProcInfo, ModuleInfo, Layout), add_resume_layout_for_label(Label, Layout, !CI) ; AgcStackLayout = no ). %---------------------------------------------------------------------------% %---------------------------------------------------------------------------% % 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. % % `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 get_total_stackslot_count(code_info::in, int::out) is det. % If a stack slot is persistent, then the stack slot is not implicitly % released when the code generator resets its location-dependent state, % usually when entering the next arm of a disjunction, switch, etc. :- type temp_slot_persistence ---> persistent_temp_slot ; non_persistent_temp_slot. % Acquire a stack slot for storing a temporary. The slot_contents % description is for accurate gc. % :- pred acquire_temp_slot(slot_contents::in, temp_slot_persistence::in, lval::out, code_info::in, code_info::out) is det. % Release a stack slot acquired earlier for a temporary value. % The persistence argument should match the acquire operation. % :- pred release_temp_slot(lval::in, temp_slot_persistence::in, code_info::in, code_info::out) is det. % acquire_several_temp_slots(Items, Persistence, StackVars, % StackId, N, M, !Info): % % Perform an acquire_temp_slot operation for each element of the % input list, all with the same persistence. % % The slots will be the ones from stack_slot_num_to_lval(StackId, N) % consecutively to stack_slot_num_to_lval(StackId, M), with N < M. % These will also be returned as StackVars. % :- pred acquire_several_temp_slots(list(slot_contents)::in, temp_slot_persistence::in, list(lval)::out, main_stack::out, int::out, int::out, code_info::in, code_info::out) is det. % Release the stack slots acquired by an earlier acquire_several_temp_slots % operation. The persistence argument should match the acquire operation. % :- pred release_several_temp_slots(list(lval)::in, temp_slot_persistence::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 get_variable_slot(code_info::in, prog_var::in, lval::out) is det. %---------------------------------------------------------------------------% %---------------------------------------------------------------------------% :- implementation. acquire_temp_slot(Item, Persistence, StackVar, !CI) :- get_temp_content_map(!.CI, TempContentMap0), map.to_assoc_list(TempContentMap0, TempContentList), get_temps_in_use(!.CI, TempsInUse0), ( find_unused_slot_for_item(TempContentList, Item, TempsInUse0, ChosenStackVar, _) -> StackVar = ChosenStackVar ; new_temp_slot(Item, StackVar, !CI) ), set.insert(StackVar, TempsInUse0, TempsInUse), set_temps_in_use(TempsInUse, !CI), ( Persistence = persistent_temp_slot, get_persistent_temps(!.CI, PersistentTemps0), set.insert(StackVar, PersistentTemps0, PersistentTemps), set_persistent_temps(PersistentTemps, !CI) ; Persistence = non_persistent_temp_slot ). acquire_several_temp_slots([], _, _, _, _, _, !CI) :- % We could return an empty list of stack vars for StackVars, but there is % nothing meaningful we can return for the other outputs. unexpected($module, $pred, "[]"). acquire_several_temp_slots([HeadItem | TailItems], Persistence, StackVars, StackId, FirstSlotNum, LastSlotNum, !CI) :- get_temp_content_map(!.CI, TempContentMap0), map.to_assoc_list(TempContentMap0, TempContentList), get_temps_in_use(!.CI, TempsInUse0), ( find_unused_slots_for_items(TempContentList, HeadItem, TailItems, TempsInUse0, StackVarsPrime, StackIdPrime, FirstSlotNumPrime, LastSlotNumPrime) -> StackVars = StackVarsPrime, StackId = StackIdPrime, FirstSlotNum = FirstSlotNumPrime, LastSlotNum = LastSlotNumPrime ; new_temp_slots([HeadItem | TailItems], StackVars, StackId, FirstSlotNum, LastSlotNum, !CI) ), set.insert_list(StackVars, TempsInUse0, TempsInUse), set_temps_in_use(TempsInUse, !CI), ( Persistence = persistent_temp_slot, get_persistent_temps(!.CI, PersistentTemps0), set.insert_list(StackVars, PersistentTemps0, PersistentTemps), set_persistent_temps(PersistentTemps, !CI) ; Persistence = non_persistent_temp_slot ). :- pred new_temp_slot(slot_contents::in, lval::out, code_info::in, code_info::out) is det. new_temp_slot(Item, StackVar, !CI) :- get_var_slot_count(!.CI, VarSlotCount), get_max_temp_slot_count(!.CI, TempSlotCount0), TempSlotCount = TempSlotCount0 + 1, SlotNum = VarSlotCount + TempSlotCount, CodeModel = get_proc_model(!.CI), StackId = code_model_to_main_stack(CodeModel), StackVar = stack_slot_num_to_lval(StackId, SlotNum), set_max_temp_slot_count(TempSlotCount, !CI), get_temp_content_map(!.CI, TempContentMap0), map.det_insert(StackVar, Item, TempContentMap0, TempContentMap), set_temp_content_map(TempContentMap, !CI). :- pred new_temp_slots(list(slot_contents)::in, list(lval)::out, main_stack::out, int::out, int::out, code_info::in, code_info::out) is det. new_temp_slots(Items, StackVars, StackId, FirstSlotNum, LastSlotNum, !CI) :- get_var_slot_count(!.CI, VarSlotCount), get_max_temp_slot_count(!.CI, TempSlotCount0), FirstSlotNum = VarSlotCount + TempSlotCount0 + 1, CodeModel = get_proc_model(!.CI), StackId = code_model_to_main_stack(CodeModel), get_temp_content_map(!.CI, TempContentMap0), record_new_temp_slots(Items, StackId, FirstSlotNum, FirstUnusedSlotNum, TempSlotCount0, TempSlotCount, TempContentMap0, TempContentMap, StackVars), LastSlotNum = FirstUnusedSlotNum - 1, set_max_temp_slot_count(TempSlotCount, !CI), set_temp_content_map(TempContentMap, !CI). :- pred record_new_temp_slots(list(slot_contents)::in, main_stack::in, int::in, int::out, int::in, int::out, map(lval, slot_contents)::in, map(lval, slot_contents)::out, list(lval)::out) is det. record_new_temp_slots([], _, !CurSlotNum, !TempSlotCount, !TempContentMap, []). record_new_temp_slots([Item | Items], StackId, !CurSlotNum, !TempSlotCount, !TempContentMap, [StackVar | StackVars]) :- StackVar = stack_slot_num_to_lval(StackId, !.CurSlotNum), map.det_insert(StackVar, Item, !TempContentMap), !:CurSlotNum = !.CurSlotNum + 1, !:TempSlotCount = !.TempSlotCount + 1, record_new_temp_slots(Items, StackId, !CurSlotNum, !TempSlotCount, !TempContentMap, StackVars). :- pred find_unused_slot_for_item(assoc_list(lval, slot_contents)::in, slot_contents::in, set(lval)::in, lval::out, assoc_list(lval, slot_contents)::out) is semidet. find_unused_slot_for_item([Head | Tail], Item, TempsInUse, ChosenStackVar, Remainder) :- Head = HeadStackVar - HeadSlotType, ( HeadSlotType = Item, \+ set.member(HeadStackVar, TempsInUse) -> ChosenStackVar = HeadStackVar, Remainder = Tail ; find_unused_slot_for_item(Tail, Item, TempsInUse, ChosenStackVar, Remainder) ). :- pred find_unused_slots_for_items(assoc_list(lval, slot_contents)::in, slot_contents::in, list(slot_contents)::in, set(lval)::in, list(lval)::out, main_stack::out, int::out, int::out) is semidet. find_unused_slots_for_items([Head | Tail], HeadItem, TailItems, TempsInUse, ChosenStackVars, StackId, FirstSlotNum, LastSlotNum) :- ( find_unused_slot_for_item([Head | Tail], HeadItem, TempsInUse, ChosenHeadStackVar, Remainder), ( ChosenHeadStackVar = stackvar(N) -> StackId0 = det_stack, FirstSlotNum0 = N ; ChosenHeadStackVar = framevar(N) -> StackId0 = nondet_stack, FirstSlotNum0 = N ; unexpected($module, $pred, "not stackvar or framevar") ), StackId1 = StackId0, FirstSlotNum1 = FirstSlotNum0, find_next_slots_for_items(Remainder, TailItems, TempsInUse, ChosenTailStackVars, StackId1, FirstSlotNum1, LastSlotNum1) -> ChosenStackVars = [ChosenHeadStackVar | ChosenTailStackVars], StackId = StackId1, FirstSlotNum = FirstSlotNum1, LastSlotNum = LastSlotNum1 ; find_unused_slots_for_items(Tail, HeadItem, TailItems, TempsInUse, ChosenStackVars, StackId, FirstSlotNum, LastSlotNum) ). :- pred find_next_slots_for_items(assoc_list(lval, slot_contents)::in, list(slot_contents)::in, set(lval)::in, list(lval)::out, main_stack::in, int::in, int::out) is semidet. find_next_slots_for_items([], [], _, [], _, !SlotNum). find_next_slots_for_items([Head | Tail], [HeadItem | TailItems], TempsInUse, [HeadStackVar | TailStackVars], StackId, !SlotNum) :- Head = HeadStackVar - HeadSlotType, !:SlotNum = !.SlotNum + 1, HeadStackVar = stack_slot_num_to_lval(StackId, !.SlotNum), HeadSlotType = HeadItem, \+ set.member(HeadStackVar, TempsInUse), find_next_slots_for_items(Tail, TailItems, TempsInUse, TailStackVars, StackId, !SlotNum). release_temp_slot(StackVar, Persistence, !CI) :- get_temps_in_use(!.CI, TempsInUse0), set.delete(StackVar, TempsInUse0, TempsInUse), set_temps_in_use(TempsInUse, !CI), get_persistent_temps(!.CI, PersistentTemps0), set.is_member(StackVar, PersistentTemps0, IsInPersistentTemps0), ( Persistence = persistent_temp_slot, expect(unify(IsInPersistentTemps0, yes), $module, $pred, "released stack slot should be persistent"), set.delete(StackVar, PersistentTemps0, PersistentTemps), set_persistent_temps(PersistentTemps, !CI) ; Persistence = non_persistent_temp_slot, expect(unify(IsInPersistentTemps0, no), $module, $pred, "released stack slot should not be persistent") ). release_several_temp_slots([], _Persistence, !CI). release_several_temp_slots([StackVar | StackVars], Persistence, !CI) :- release_temp_slot(StackVar, Persistence, !CI), release_several_temp_slots(StackVars, Persistence, !CI). %---------------------------------------------------------------------------% get_variable_slot(CI, Var, Slot) :- get_stack_slots(CI, StackSlots), ( map.search(StackSlots, Var, SlotLocn) -> Slot = stack_slot_to_lval(SlotLocn) ; Name = variable_name(CI, Var), term.var_to_int(Var, Num), string.int_to_string(Num, NumStr), Str = "variable `" ++ Name ++ "' " ++ "(" ++ NumStr ++ ") not found", unexpected($module, $pred, Str) ). get_total_stackslot_count(CI, NumSlots) :- get_var_slot_count(CI, SlotsForVars), get_max_temp_slot_count(CI, SlotsForTemps), NumSlots = SlotsForVars + SlotsForTemps. :- pred max_var_slot(stack_slots::in, int::out) is det. max_var_slot(StackSlots, SlotCount) :- map.values(StackSlots, StackSlotList), max_var_slot_2(StackSlotList, 0, SlotCount). :- pred max_var_slot_2(list(stack_slot)::in, int::in, int::out) is det. max_var_slot_2([], !Max). max_var_slot_2([L | Ls], !Max) :- ( L = det_slot(N, Width) ; L = parent_det_slot(N, Width) ; L = nondet_slot(N, Width) ), ( Width = single_width, int.max(N, !Max) ; Width = double_width, int.max(N + 1, !Max) ), max_var_slot_2(Ls, !Max). %---------------------------------------------------------------------------% % Submodule for debugging the code generator itself. :- interface. % Should we trace the operation of the code generator. % :- pred should_trace_code_gen(code_info::in) is semidet. :- type code_info_component ---> cic_forward_live_vars ; cic_zombies ; cic_temps_in_use ; cic_par_conj_depth. % Print the selected parts of the code_info. % % If you need to print a part that is not currently selectable, make it % selectable. % :- pred output_code_info(list(code_info_component)::in, code_info::in, io::di, io::uo) is det. :- implementation. should_trace_code_gen(CI) :- code_info.get_pred_id(CI, PredId), pred_id_to_int(PredId, PredIdInt), code_info.get_module_info(CI, ModuleInfo), module_info_get_globals(ModuleInfo, Globals), globals.lookup_int_option(Globals, debug_code_gen_pred_id, DebugPredIdInt), PredIdInt = DebugPredIdInt. output_code_info(Components, CI, !IO) :- CI = code_info(Static, LocDep, _Persistent), VarSet = Static ^ cis_varset, LocDep = code_info_loc_dep(ForwardLiveVars, _InstMap, Zombies, _VarLocnInfo, TempsInUse, _FailInfo, ParConjDepth), ( list.member(cic_forward_live_vars, Components) -> io.write_string("forward live vars: ", !IO), mercury_output_vars(VarSet, yes, set_of_var.to_sorted_list(ForwardLiveVars), !IO), io.nl(!IO) ; true ), ( list.member(cic_zombies, Components) -> io.write_string("zombies: ", !IO), mercury_output_vars(VarSet, yes, set_of_var.to_sorted_list(Zombies), !IO), io.nl(!IO) ; true ), ( list.member(cic_temps_in_use, Components) -> io.write_string("temps_in_use: ", !IO), io.write_string(dump_lvals(no, set.to_sorted_list(TempsInUse)), !IO), io.nl(!IO) ; true ), ( list.member(cic_par_conj_depth, Components) -> io.format("par_conj_depth: %d\n", [i(ParConjDepth)], !IO) ; true ). :- pred output_resume_map(prog_varset::in, map(prog_var, set(lval))::in, io::di, io::uo) is det. output_resume_map(VarSet, ResumeMap, !IO) :- map.to_assoc_list(ResumeMap, ResumeAssocList), list.foldl(output_resume_map_element(VarSet), ResumeAssocList, !IO). :- pred output_resume_map_element(prog_varset::in, pair(prog_var, set(lval))::in, io::di, io::uo) is det. output_resume_map_element(VarSet, Var - LvalSet, !IO) :- io.write_string(describe_var(VarSet, Var), !IO), io.write_string(": ", !IO), Lvals = set.to_sorted_list(LvalSet), LvalDescs = list.map(dump_lval(no), Lvals), SpaceLvalDescs = list.map(string.append(" "), LvalDescs), io.write_string(string.append_list(SpaceLvalDescs), !IO), io.nl(!IO). %---------------------------------------------------------------------------% :- end_module ll_backend.code_info. %---------------------------------------------------------------------------%