diff --git a/compiler/call_gen.m b/compiler/call_gen.m index 0f9f87fca..c3994d125 100644 --- a/compiler/call_gen.m +++ b/compiler/call_gen.m @@ -160,10 +160,11 @@ call_gen__generate_semidet_call_2(PredId, ModeId, Arguments, Code) --> call_gen__generate_return_livevals(OutArgs, OutputArguments, OutLiveVals), code_info__make_entry_label(ModuleInfo, PredId, ModeId, yes, Address), - { CodeC1 = node([ - call(Address, label(ReturnLabel), OutLiveVals, semidet) + { CodeC1 = node([ + call(Address, label(ReturnLabel), OutLiveVals, semidet) - "branch to semidet procedure", - label(ReturnLabel) - "Continuation label" + label(ReturnLabel) + - "Continuation label" ]) }, call_gen__rebuild_registers(Args), code_info__generate_failure(FailCode), @@ -181,32 +182,33 @@ call_gen__generate_nondet_call(PredId, ModeId, Arguments, Code) --> code_info__get_pred_proc_arginfo(PredId, ModeId, ArgInfo), { assoc_list__from_corresponding_lists(Arguments, ArgInfo, Args) }, { call_gen__select_out_args(Args, OutArgs) }, - call_gen__save_variables(OutArgs, CodeA), - code_info__setup_call(Args, caller, CodeB), + call_gen__save_variables(OutArgs, SaveCode), + code_info__unset_failure_cont(FlushCode), + code_info__setup_call(Args, caller, SetupCode), code_info__get_next_label(ReturnLabel), code_info__get_module_info(ModuleInfo), { call_gen__input_args(ArgInfo, InputArguments) }, - call_gen__generate_call_livevals(OutArgs, InputArguments, CodeC0), + call_gen__generate_call_livevals(OutArgs, InputArguments, LiveCode), { call_gen__output_arg_locs(Args, OutputArguments) }, call_gen__generate_return_livevals(OutArgs, OutputArguments, OutLiveVals), code_info__make_entry_label(ModuleInfo, PredId, ModeId, yes, Address), - code_info__failure_cont(failure_cont(IsKnown, _, FailureMap)), - ( - { IsKnown = known(_) }, - { FailureMap = [_ - do_fail | _] } - -> - { TailCallable = nondet(yes) } - ; - { TailCallable = nondet(no) } - ), - { CodeC1 = node([ - call(Address, label(ReturnLabel), OutLiveVals, TailCallable) + code_info__may_use_nondet_tailcall(TailCall), + { CallModel = nondet(TailCall) }, + { CallCode = node([ + call(Address, label(ReturnLabel), OutLiveVals, CallModel) - "branch to nondet procedure", - label(ReturnLabel) - "Continuation label" + label(ReturnLabel) + - "Continuation label" ]) }, - { Code = tree(CodeA, tree(CodeB, tree(CodeC0, CodeC1))) }, - call_gen__rebuild_registers(Args). + call_gen__rebuild_registers(Args), + { Code = + tree(SaveCode, + tree(FlushCode, + tree(SetupCode, + tree(LiveCode, + CallCode)))) + }. %---------------------------------------------------------------------------% @@ -235,8 +237,8 @@ call_gen__save_variables(Args, Code) --> call_gen__save_variables_2([], empty) --> []. call_gen__save_variables_2([Var | Vars], Code) --> code_info__save_variable_on_stack(Var, CodeA), - call_gen__save_variables_2(Vars, CodeB), - { Code = tree(CodeA, CodeB) }. + call_gen__save_variables_2(Vars, CodeB), + { Code = tree(CodeA, CodeB) }. %---------------------------------------------------------------------------% @@ -610,6 +612,11 @@ call_gen__generate_higher_call(CodeModel, PredVar, InVars, OutVars, Code) --> code_info__set_succip_used(yes), { set__list_to_set(OutVars, OutArgs) }, call_gen__save_variables(OutArgs, SaveCode), + ( { CodeModel = model_non } -> + code_info__unset_failure_cont(FlushCode) + ; + { FlushCode = empty } + ), % place the immediate input arguments in registers % starting at r4. call_gen__generate_immediate_args(InVars, 4, InLocs, ImmediateCode), @@ -656,16 +663,8 @@ call_gen__generate_higher_call(CodeModel, PredVar, InVars, OutVars, Code) --> { RuntimeAddr = do_semidet_closure } ; { CodeModel = model_non }, - code_info__failure_cont(failure_cont(IsKnown, _, FailureMap)), - ( - { IsKnown = known(_) }, - { FailureMap = [_ - do_fail | _] } - -> - { TailCallable = yes } - ; - { TailCallable = no } - ), - { CallModel = nondet(TailCallable) }, + code_info__may_use_nondet_tailcall(TailCall), + { CallModel = nondet(TailCall) }, { RuntimeAddr = do_nondet_closure } ), { TryCallCode = node([ @@ -680,16 +679,27 @@ call_gen__generate_higher_call(CodeModel, PredVar, InVars, OutVars, Code) --> -> code_info__generate_failure(FailCode), code_info__get_next_label(ContLab), - { CheckReturnCode = tree(node([ + { TestSuccessCode = node([ if_val(lval(reg(r(1))), label(ContLab)) - "Test for success" - ]), tree(FailCode, node([ label(ContLab) - "" ]))) }, - { CallCode = tree(TryCallCode, CheckReturnCode) } + ]) }, + { ContLabelCode = node([label(ContLab) - ""]) }, + { CallCode = + tree(TryCallCode, + tree(TestSuccessCode, + tree(FailCode, + ContLabelCode))) } ; { CallCode = TryCallCode } ), - { Code = tree(tree(SaveCode, tree(ImmediateCode, PredVarCode)), - tree(SetupCode, CallCode)) }. + { Code = + tree(SaveCode, + tree(FlushCode, + tree(ImmediateCode, + tree(PredVarCode, + tree(SetupCode, + CallCode))))) + }. %---------------------------------------------------------------------------% diff --git a/compiler/code_aux.m b/compiler/code_aux.m index bd1443cb9..a05ed784d 100644 --- a/compiler/code_aux.m +++ b/compiler/code_aux.m @@ -64,9 +64,8 @@ % If any of the variables that have died wrt forward execution are % nevertheless needed at a resume point, we need to flush them to % their stack slots. The returned code does this. -:- pred code_aux__pre_goal_update(hlds__goal_info, bool, code_tree, - code_info, code_info). -:- mode code_aux__pre_goal_update(in, in, out, in, out) is det. +:- pred code_aux__pre_goal_update(hlds__goal_info, bool, code_info, code_info). +:- mode code_aux__pre_goal_update(in, in, in, out) is det. % code_aux__post_goal_update(GoalInfo, OldCodeInfo, NewCodeInfo) % updates OldCodeInfo to produce NewCodeInfo with the changes described @@ -84,9 +83,8 @@ % If any of the variables that have died wrt forward execution are % nevertheless needed at a resume point, we need to flush them to % their stack slots. The returned code does this. -:- pred code_aux__post_goal_update(hlds__goal_info, code_tree, - code_info, code_info). -:- mode code_aux__post_goal_update(in, out, in, out) is det. +:- pred code_aux__post_goal_update(hlds__goal_info, code_info, code_info). +:- mode code_aux__post_goal_update(in, in, out) is det. :- pred code_aux__explain_stack_slots(stack_slots, varset, string). :- mode code_aux__explain_stack_slots(in, in, out) is det. @@ -223,58 +221,39 @@ code_aux__is_recursive_call(Goal, CodeInfo) :- % Update the code info structure to be consistent % immediately prior to generating a goal -code_aux__pre_goal_update(GoalInfo, Atomic, Code) --> +code_aux__pre_goal_update(GoalInfo, Atomic) --> { goal_info_nondet_lives(GoalInfo, NondetLives) }, code_info__set_nondet_lives(NondetLives), + { goal_info_get_resume_point(GoalInfo, ResumePoint) }, + ( + { ResumePoint = no_resume_point } + ; + { ResumePoint = resume_point(_, _) }, + { error("pre_goal_update with resume point") } + ), { goal_info_pre_births(GoalInfo, PreBirths) }, { goal_info_pre_deaths(GoalInfo, PreDeaths) }, code_info__update_liveness_info(PreBirths), code_info__update_deadness_info(PreDeaths), - code_info__make_vars_dead(PreDeaths, Code), + code_info__make_vars_dead(PreDeaths), ( { Atomic = yes } -> { goal_info_post_deaths(GoalInfo, PostDeaths) }, code_info__update_deadness_info(PostDeaths) ; [] - ), - { goal_info_get_resume_point(GoalInfo, ResumePoint) }, - ( - { ResumePoint = none} - ; - { ResumePoint = orig_only(ResumeVars)}, - code_info__push_resume_point_vars(ResumeVars) - ; - { ResumePoint = stack_only(ResumeVars)}, - code_info__push_resume_point_vars(ResumeVars) - ; - { ResumePoint = orig_and_stack(ResumeVars)}, - code_info__push_resume_point_vars(ResumeVars) ). % Update the code info structure to be consistent % immediately after generating a goal -code_aux__post_goal_update(GoalInfo, Code) --> +code_aux__post_goal_update(GoalInfo) --> { goal_info_post_births(GoalInfo, PostBirths) }, { goal_info_post_deaths(GoalInfo, PostDeaths) }, code_info__update_liveness_info(PostBirths), code_info__update_deadness_info(PostDeaths), - code_info__make_vars_dead(PostDeaths, Code), + code_info__make_vars_dead(PostDeaths), code_info__make_vars_live(PostBirths), { goal_info_get_instmap_delta(GoalInfo, InstMapDelta) }, - code_info__apply_instmap_delta(InstMapDelta), - { goal_info_get_resume_point(GoalInfo, ResumePoint) }, - ( - { ResumePoint = none} - ; - { ResumePoint = orig_only(_)}, - code_info__pop_resume_point_vars - ; - { ResumePoint = stack_only(_)}, - code_info__pop_resume_point_vars - ; - { ResumePoint = orig_and_stack(_)}, - code_info__pop_resume_point_vars - ). + code_info__apply_instmap_delta(InstMapDelta). %-----------------------------------------------------------------------------% diff --git a/compiler/code_exprn.m b/compiler/code_exprn.m index 884813d4b..c987c671c 100644 --- a/compiler/code_exprn.m +++ b/compiler/code_exprn.m @@ -84,6 +84,15 @@ :- pred code_exprn__place_var(var, lval, code_tree, exprn_info, exprn_info). :- mode code_exprn__place_var(in, in, out, in, out) is det. +% code_exprn__place_vars(StoreMap, Code, ExprnInfo0, ExprnInfo) +% Produces Code and a modified version of ExprnInfo0, +% ExprnInfo which places the value of each variable +% mentioned in the store map into the corresponding location. + +:- pred code_exprn__place_vars(assoc_list(var, lval), code_tree, + exprn_info, exprn_info). +:- mode code_exprn__place_vars(in, out, in, out) is det. + % code_exprn__produce_var(Var, Rval, Code, ExprnInfo0, ExprnInfo) % Produces a code fragment Code to evaluate Var and % provide it as Rval (which may be a const, etc, or an lval). @@ -888,6 +897,12 @@ code_exprn__cache_exprn(Var, Rval) --> %------------------------------------------------------------------------------% +code_exprn__place_vars([], empty) --> []. +code_exprn__place_vars([Var - Lval | StoreMap], Code) --> + code_exprn__place_var(Var, Lval, FirstCode), + code_exprn__place_vars(StoreMap, RestCode), + { Code = tree(FirstCode, RestCode) }. + code_exprn__place_var(Var, Lval, Code) --> code_exprn__get_var_status(Var, Stat), ( diff --git a/compiler/code_gen.m b/compiler/code_gen.m index ba8c30d13..0a4b1bb94 100644 --- a/compiler/code_gen.m +++ b/compiler/code_gen.m @@ -47,21 +47,9 @@ code_info, code_info). :- mode code_gen__generate_goal(in, in, out, in, out) is det. - % This predicate generates code for a goal - % and leaves all live values in locations - % determined by the stack_slots structure. - -:- pred code_gen__generate_forced_goal(code_model, hlds__goal, store_map, - code_tree, code_info, code_info). -:- mode code_gen__generate_forced_goal(in, in, in, out, in, out) is det. - :- pred code_gen__output_args(assoc_list(var, arg_info), set(lval)). :- mode code_gen__output_args(in, out) is det. -:- pred code_gen__ensure_vars_are_saved(list(var), code_tree, - code_info, code_info). -:- mode code_gen__ensure_vars_are_saved(in, out, in, out) is det. - %---------------------------------------------------------------------------% %---------------------------------------------------------------------------% @@ -584,14 +572,6 @@ code_gen__generate_non_epilog(Instr) --> %---------------------------------------------------------------------------% -code_gen__generate_forced_goal(Det, Goal, StoreMap, Code) --> - code_gen__generate_goal(Det, Goal, CodeA), - code_info__generate_forced_saves(StoreMap, CodeB), - { Code = tree(CodeA, CodeB) }, - code_info__remake_with_store_map(StoreMap). - -%---------------------------------------------------------------------------% - % Generate a goal. This predicate arranges for the necessary updates of % the generic data structures before and after the actual code generation, % which is delegated to context-specific predicates. @@ -603,7 +583,7 @@ code_gen__generate_goal(ContextModel, Goal - GoalInfo, Code) --> ; IsAtomic = no }, - code_aux__pre_goal_update(GoalInfo, IsAtomic, PreFlushCode), + code_aux__pre_goal_update(GoalInfo, IsAtomic), code_info__get_instmap(Instmap), ( { instmap__is_reachable(Instmap) } @@ -624,12 +604,7 @@ code_gen__generate_goal(ContextModel, Goal - GoalInfo, Code) --> { CodeModel = model_non }, ( { ContextModel = model_non } -> code_gen__generate_non_goal_2(Goal, GoalInfo, - Code0), - % the nondet goal may have created choice - % points, so we must set the current failure - % continuation to `unknown', which means - % "on failure, just do a redo()". - code_info__unset_failure_cont + Code0) ; { error("nondet model in det/semidet context") } ) @@ -637,7 +612,7 @@ code_gen__generate_goal(ContextModel, Goal - GoalInfo, Code) --> % Make live any variables which subsequent goals % will expect to be live, but were not generated code_info__set_instmap(Instmap), - code_aux__post_goal_update(GoalInfo, PostFlushCode), + code_aux__post_goal_update(GoalInfo), code_info__get_globals(Options), ( { globals__lookup_bool_option(Options, lazy_code, yes) } @@ -647,10 +622,9 @@ code_gen__generate_goal(ContextModel, Goal - GoalInfo, Code) --> { error("Eager code unavailable") } %%% code_info__generate_eager_flush(Code1) ), - { Code = tree(PreFlushCode, - tree(Code0, tree(PostFlushCode, Code1))) } + { Code = tree(Code0, Code1) } ; - { Code = PreFlushCode } + { Code = empty } ), !. @@ -685,7 +659,7 @@ code_gen__generate_goals([Goal | Goals], CodeModel, Instr) --> code_gen__generate_det_goal_2(conj(Goals), _GoalInfo, Instr) --> code_gen__generate_goals(Goals, model_det, Instr). -code_gen__generate_det_goal_2(some(Vars, Goal), _GoalInfo, Instr) --> +code_gen__generate_det_goal_2(some(_Vars, Goal), _GoalInfo, Instr) --> { Goal = _ - InnerGoalInfo }, { goal_info_get_code_model(InnerGoalInfo, CodeModel) }, ( @@ -700,18 +674,11 @@ code_gen__generate_det_goal_2(some(Vars, Goal), _GoalInfo, Instr) --> code_gen__generate_goal(model_non, Goal, GoalCode), code_info__generate_det_commit(Commit), { Instr = tree(PreCommit, tree(GoalCode, Commit)) } - ), - % Any variables that became nondet live - % during the quantified goal that are - % quantified to the scope of that goal - % are no longer nondet live. - code_info__get_nondet_lives(NondetLives0), - { set__delete_list(NondetLives0, Vars, NondetLives) }, - code_info__set_nondet_lives(NondetLives). + ). code_gen__generate_det_goal_2(disj(Goals, StoreMap), _GoalInfo, Instr) --> disj_gen__generate_det_disj(Goals, StoreMap, Instr). code_gen__generate_det_goal_2(not(Goal), _GoalInfo, Instr) --> - code_gen__generate_negation_general(model_det, Goal, Instr). + code_gen__generate_negation(model_det, Goal, Instr). code_gen__generate_det_goal_2(higher_order_call(PredVar, Args, Types, Modes, Det), _CodeInfo, Instr) --> @@ -1081,7 +1048,7 @@ code_gen__generate_semi_goal_2(some(_Vars, Goal), _GoalInfo, Code) --> code_gen__generate_semi_goal_2(disj(Goals, StoreMap), _GoalInfo, Code) --> disj_gen__generate_semi_disj(Goals, StoreMap, Code). code_gen__generate_semi_goal_2(not(Goal), _GoalInfo, Code) --> - code_gen__generate_negation(Goal, Code). + code_gen__generate_negation(model_semi, Goal, Code). code_gen__generate_semi_goal_2(higher_order_call(PredVar, Args, Types, Modes, Det), _CodeInfo, Code) --> call_gen__generate_higher_order_call(model_semi, PredVar, Args, @@ -1134,16 +1101,33 @@ code_gen__generate_semi_goal_2(pragma_c_code(C_Code, IsRecursive, %---------------------------------------------------------------------------% %---------------------------------------------------------------------------% -:- pred code_gen__generate_negation(hlds__goal, code_tree, - code_info, code_info). -:- mode code_gen__generate_negation(in, out, in, out) is det. +:- pred code_gen__generate_negation(code_model, hlds__goal, code_tree, + code_info, code_info). +:- mode code_gen__generate_negation(in, in, out, in, out) is det. -code_gen__generate_negation(Goal, Code) --> - % for a negated simple test, we see if - % we can do a more efficient mechanism that - % doesn't require a cache flush. +code_gen__generate_negation(CodeModel, Goal0, Code) --> + { Goal0 = GoalExpr - GoalInfo0 }, + { goal_info_get_resume_point(GoalInfo0, Resume) }, ( - { Goal = unify(_, _, _, simple_test(L, R), _) - GoalInfo }, + { Resume = resume_point(ResumeVarsPrime, ResumeLocsPrime) } + -> + { ResumeVars = ResumeVarsPrime}, + { ResumeLocs = ResumeLocsPrime} + ; + { error("negated goal has no resume point") } + ), + code_info__push_resume_point_vars(ResumeVars), + % The next line is to enable Goal to pass the + % pre_goal_update sanity check + { goal_info_set_resume_point(GoalInfo0, no_resume_point, GoalInfo) }, + { Goal = GoalExpr - GoalInfo }, + + % for a negated simple test, we can generate better code + % than the general mechanism, because we don't have to + % flush the cache. + ( + { CodeModel = model_semi }, + { GoalExpr = unify(_, _, _, simple_test(L, R), _) }, code_info__can_generate_direct_branch(CodeAddr), code_info__get_globals(Globals), { globals__lookup_bool_option(Globals, simple_neg, yes) } @@ -1152,7 +1136,7 @@ code_gen__generate_negation(Goal, Code) --> % (special-cased, though it may be) % we need to apply the pre- and post- % updates. - code_aux__pre_goal_update(GoalInfo, yes, PreFlushCode), + code_aux__pre_goal_update(GoalInfo, yes), code_info__produce_variable(L, CodeL, ValL), code_info__produce_variable(R, CodeR, ValR), code_info__variable_type(L, Type), @@ -1167,61 +1151,64 @@ code_gen__generate_negation(Goal, Code) --> if_val(binop(Op, ValL, ValR), CodeAddr) - "test inequality" ]) }, - code_aux__post_goal_update(GoalInfo, PostFlushCode), - { Code = tree(PreFlushCode, tree(tree(CodeL, CodeR), - tree(TestCode, PostFlushCode))) } + code_aux__post_goal_update(GoalInfo), + { Code = tree(tree(CodeL, CodeR), TestCode) } ; - code_gen__generate_negation_general(model_semi, Goal, Code) - ). + code_gen__generate_negation_general(CodeModel, Goal, + ResumeVars, ResumeLocs, Code) + ), + code_info__pop_resume_point_vars. -:- pred code_gen__generate_negation_general(code_model, hlds__goal, code_tree, - code_info, code_info). -:- mode code_gen__generate_negation_general(in, in, out, in, out) is det. +:- pred code_gen__generate_negation_general(code_model, hlds__goal, + set(var), resume_locs, code_tree, code_info, code_info). +:- mode code_gen__generate_negation_general(in, in, in, in, out, in, out) + is det. -code_gen__generate_negation_general(CodeModel, Goal, Code) --> - % make sure that any variables that became - % nondet live during the negated goal are - % no longer considered nondet live by reinstating - % the initial set of nondet live variables. - code_info__get_nondet_lives(NondetLives), +code_gen__generate_negation_general(CodeModel, Goal, ResumeVars, ResumeLocs, + Code) --> + % This code is a cut-down version of the code for semidet + % if-then-elses. + % XXX It does not save or restore tickets + + code_info__make_known_failure_cont(ResumeVars, ResumeLocs, no, + no, _, ModContCode), + + % Maybe save the heap state current before the condition; + % this ought to be after we make the failure continuation + % because that causes the cache to get flushed code_info__get_globals(Globals), - { Goal = _NotGoal - GoalInfo }, - { goal_info_cont_lives(GoalInfo, Lives) }, - { - Lives = yes(Vars0) - -> - Vars = Vars0 - ; - error("code_gen__generate_negation: no cont_lives!") - }, - % make the failure cont before saving the heap - % pointer because the cache gets flushed. - code_info__make_known_failure_cont(Vars, no, ModContCode), { globals__lookup_bool_option(Globals, reclaim_heap_on_semidet_failure, yes), code_util__goal_may_allocate_heap(Goal) -> - Reclaim = yes + ReclaimHeap = yes ; - Reclaim = no + ReclaimHeap = no }, - code_info__maybe_save_hp(Reclaim, SaveHeapCode), - % The contained goal cannot be nondet, because if it's - % mode-correct, it won't have any output vars, and so - % it will be semi-det. + code_info__maybe_save_hp(ReclaimHeap, SaveHeapCode), + + % Generate the condition as a semi-deterministic goal; + % it cannot be nondet, since mode correctness requires it + % to have no output vars code_gen__generate_goal(model_semi, Goal, GoalCode), + ( { CodeModel = model_det } -> { FailCode = empty } ; - code_info__generate_under_failure(FailCode) + code_info__grab_code_info(CodeInfo), + code_info__pop_failure_cont, + code_info__generate_failure(FailCode), + code_info__slap_code_info(CodeInfo) ), code_info__restore_failure_cont(RestoreContCode), - code_info__maybe_restore_hp(Reclaim, RestoreHeapCode), - code_info__set_nondet_lives(NondetLives), - { Code = tree(tree(tree(ModContCode, SaveHeapCode), GoalCode), - tree(FailCode, tree(RestoreContCode, - RestoreHeapCode))) }. + code_info__maybe_restore_hp(ReclaimHeap, RestoreHeapCode), + { Code = tree(ModContCode, + tree(SaveHeapCode, + tree(GoalCode, + tree(FailCode, + tree(RestoreContCode, + RestoreHeapCode))))) }. %---------------------------------------------------------------------------% %---------------------------------------------------------------------------% @@ -1318,15 +1305,5 @@ code_gen__add_saved_succip([Instrn0 - Comment | Instrns0 ], StackLoc, ), code_gen__add_saved_succip(Instrns0, StackLoc, Instrns). -%---------------------------------------------------------------------------% - -code_gen__ensure_vars_are_saved([], empty) --> []. -code_gen__ensure_vars_are_saved([V | Vs], Code) --> - code_info__get_stack_slots(StackSlots), - { map__lookup(StackSlots, V, Slot) }, - code_info__place_var(V, Slot, Code0), - code_gen__ensure_vars_are_saved(Vs, Code1), - { Code = tree(Code0, Code1) }. - %---------------------------------------------------------------------------% %---------------------------------------------------------------------------% diff --git a/compiler/code_info.m b/compiler/code_info.m index 675da60f2..d25d7949f 100644 --- a/compiler/code_info.m +++ b/compiler/code_info.m @@ -4,9 +4,9 @@ % Public License - see the file COPYING in the Mercury distribution. %---------------------------------------------------------------------------% % -% file: code_info.m +% File: code_info.m % -% main author: conway. +% Main authors: conway, zs. % % This file defines the code_info type and various operations on it. % @@ -66,6 +66,7 @@ % --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. + % XXX :- pred code_info__make_entry_label(module_info, pred_id, proc_id, bool, code_addr, code_info, code_info). :- mode code_info__make_entry_label(in, in, in, in, out, in, out) is det. @@ -114,9 +115,13 @@ :- mode code_info__setup_call(in, in, out, in, out) is det. :- pred code_info__save_variable_on_stack(var, code_tree, - code_info, code_info). + code_info, code_info). :- mode code_info__save_variable_on_stack(in, out, in, out) is det. +:- pred code_info__save_variables_on_stack(list(var), code_tree, + code_info, code_info). +:- mode code_info__save_variables_on_stack(in, out, in, out) is det. + % Succeed if the given variable is live at the % end of this goal. :- pred code_info__variable_is_live(var, code_info, code_info). @@ -149,9 +154,9 @@ :- pred code_info__get_live_variables(list(var), code_info, code_info). :- mode code_info__get_live_variables(out, in, out) is det. -:- pred code_info__generate_forced_saves(store_map, code_tree, +:- pred code_info__generate_branch_end(code_model, store_map, code_tree, code_info, code_info). -:- mode code_info__generate_forced_saves(in, out, in, out) is det. +:- mode code_info__generate_branch_end(in, in, out, in, out) is det. :- pred code_info__grab_code_info(code_info, code_info, code_info). :- mode code_info__grab_code_info(out, in, out) is det. @@ -171,9 +176,6 @@ :- pred code_info__remake_with_store_map(store_map, code_info, code_info). :- mode code_info__remake_with_store_map(in, in, out) is det. -:- pred code_info__remake_with_stack_slots(code_info, code_info). -:- mode code_info__remake_with_stack_slots(in, out) is det. - :- pred code_info__update_liveness_info(set(var), code_info, code_info). :- mode code_info__update_liveness_info(in, in, out) is det. @@ -197,8 +199,11 @@ code_info). :- mode code_info__materialize_vars_in_rval(in, out, out, in, out) is det. -:- pred code_info__make_vars_dead(set(var), code_tree, code_info, code_info). -:- mode code_info__make_vars_dead(in, out, in, out) is det. +:- pred code_info__pickup_zombies(set(var), code_info, code_info). +:- mode code_info__pickup_zombies(out, in, out) is det. + +:- pred code_info__make_vars_dead(set(var), code_info, code_info). +:- mode code_info__make_vars_dead(in, in, out) is det. :- pred code_info__make_vars_live(set(var), code_info, code_info). :- mode code_info__make_vars_live(in, in, out) is det. @@ -233,11 +238,20 @@ :- pred code_info__set_shapes(shape_table, code_info, code_info). :- mode code_info__set_shapes(in, in, out) is det. +:- implementation. + +% This implementation section is an ugly hack. +% I will fix it when I break code_info into mini-modules. - zs + :- type failure_cont - ---> failure_cont(failure_continuation, maybe(label), failure_map). + ---> failure_cont( + failure_continuation, + maybe(label), % the maybe(label) is yes if we created a % temporary frame and we need to restore % curfr after a redo() + resume_maps + ). :- type failure_continuation ---> known(bool) % on failure we jump to a label @@ -249,19 +263,22 @@ % better save variables that are live (on % redo) onto the stack. -:- type failure_map == assoc_list(map(var, set(rval)), code_addr). - % we assume that there are exactly two - % alternatives in the failure_map list; - % the first one is the entry point with variables - % potentially in registers and the second one in - % the list is the entry point with variables on the - % stack. +:- type resume_map == map(var, set(rval)). + +:- type resume_maps + ---> 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). + +:- interface. % push a new failure continuation onto the stack -:- pred code_info__make_known_failure_cont(set(var), bool, code_tree, - code_info, code_info). -:- mode code_info__make_known_failure_cont(in, in, out, in, out) is det. +:- pred code_info__make_known_failure_cont(set(var), resume_locs, bool, + bool, bool, code_tree, code_info, code_info). +:- mode code_info__make_known_failure_cont(in, in, in, in, out, out, in, out) + is det. % We manufacture a failure cont when we start generating % code for a proc because on the failure of a procedure @@ -278,13 +295,14 @@ % set the topmost failure cont to `unknown' (e.g. after % a nondet call or after a disjunction). -:- pred code_info__unset_failure_cont(code_info, code_info). -:- mode code_info__unset_failure_cont(in, out) is det. +:- pred code_info__unset_failure_cont(code_tree, code_info, code_info). +:- mode code_info__unset_failure_cont(out, in, out) is det. - % lookup the value on the top of the failure continuation stack + % flush the variables needed for any current resumption point + % to their stack slots. -:- pred code_info__failure_cont(failure_cont, code_info, code_info). -:- mode code_info__failure_cont(out, in, out) is det. +:- pred code_info__flush_resume_vars_to_stack(code_tree, code_info, code_info). +:- mode code_info__flush_resume_vars_to_stack(out, in, out) is det. % generate some code to restore the current redoip, by looking % at the top of the failure continuation stack. @@ -292,9 +310,6 @@ :- pred code_info__restore_failure_cont(code_tree, code_info, code_info). :- mode code_info__restore_failure_cont(out, in, out) is det. -:- pred code_info__modify_failure_cont(code_tree, code_info, code_info). -:- mode code_info__modify_failure_cont(out, in, out) is det. - % do_soft_cut takes the lval where the address of the % failure frame is stored, and it returns code to % set the redoip of that frame to do_fail. @@ -305,15 +320,9 @@ :- pred code_info__stack_variable(int, lval, code_info, code_info). :- mode code_info__stack_variable(in, out, in, out) is det. -:- pred code_info__generate_nondet_saves(code_tree, code_info, code_info). -:- mode code_info__generate_nondet_saves(out, in, out) is det. - :- pred code_info__generate_failure(code_tree, code_info, code_info). :- mode code_info__generate_failure(out, in, out) is det. -:- pred code_info__generate_under_failure(code_tree, code_info, code_info). -:- mode code_info__generate_under_failure(out, in, out) is det. - :- pred code_info__generate_test_and_fail(rval, code_tree, code_info, code_info). :- mode code_info__generate_test_and_fail(in, out, in, out) is det. @@ -331,6 +340,9 @@ :- pred code_info__generate_semi_commit(label, code_tree, code_info, code_info). :- mode code_info__generate_semi_commit(in, out, in, out) is det. +:- pred code_info__may_use_nondet_tailcall(bool, code_info, code_info). +:- mode code_info__may_use_nondet_tailcall(out, in, out) is det. + :- pred code_info__save_maxfr(lval, code_tree, code_info, code_info). :- mode code_info__save_maxfr(out, out, in, out) is det. @@ -382,7 +394,7 @@ :- pred code_info__maybe_restore_ticket(bool, code_tree, code_info, code_info). :- mode code_info__maybe_restore_ticket(in, out, in, out) is det. -:- pred code_info__maybe_restore_ticket_and_pop(bool, code_tree, +:- pred code_info__maybe_restore_ticket_and_pop(bool, code_tree, code_info, code_info). :- mode code_info__maybe_restore_ticket_and_pop(in, out, in, out) is det. @@ -427,7 +439,7 @@ :- pred code_info__can_generate_direct_branch(code_addr, code_info, code_info). :- mode code_info__can_generate_direct_branch(out, in, out) is semidet. - % Given a list of type variables, find the lvals where the + % Given a list of type variables, find the lvals where the % corresponding type_infos are being stored. :- pred code_info__find_type_infos(list(var), list(lval), code_info, code_info). @@ -504,6 +516,9 @@ set(var), % Variables that are not quite live % but are only nondet-live (so that % we make sure we save them). + set(var), % Zombie variables; variables that have + % been killed but are protected by a + % resume point. stack(set(var)), % Each resumption point has an % associated set of variables @@ -543,6 +558,7 @@ code_info__init(Varset, Liveness, StackSlots, SaveSuccip, Globals, stack__init(PushedVals0), stack__init(ResumeSetStack0), set__init(NondetLives), + set__init(Zombies), code_info__max_slot(StackSlots, SlotCount0), proc_info_interface_code_model(ProcInfo, CodeModel), ( @@ -556,7 +572,7 @@ code_info__init(Varset, Liveness, StackSlots, SaveSuccip, Globals, SlotCount, 0, Varset, - StackSlots, + StackSlots, PredId, ProcId, 0, @@ -573,10 +589,13 @@ code_info__init(Varset, Liveness, StackSlots, SaveSuccip, Globals, PushedVals0, Shapes, NondetLives, + Zombies, ResumeSetStack0, [] ). + % XXX This should call arg_info + :- pred code_info__build_input_arg_list(assoc_list(var, arg_info), assoc_list(var, rval)). :- mode code_info__build_input_arg_list(in, out) is det. @@ -701,17 +720,22 @@ code_info__cons_id_to_tag(Var, ConsId, ConsTag) --> code_info__get_live_variables(VarList) --> code_info__get_liveness_info(NormalLiveVars), + code_info__current_resume_point_vars(ResumeVars), code_info__get_nondet_lives(NondetLiveVars), - { set__union(NormalLiveVars, NondetLiveVars, Vars) }, + { set__union(NormalLiveVars, NondetLiveVars, Vars1) }, + { set__union(Vars1, ResumeVars, Vars) }, { set__to_sorted_list(Vars, VarList) }. %---------------------------------------------------------------------------% code_info__variable_is_live(Var) --> code_info__get_liveness_info(Liveness), + code_info__current_resume_point_vars(ResumeVars), code_info__get_nondet_lives(Nondets), ( { set__member(Var, Liveness) } + ; + { set__member(Var, ResumeVars) } ; { set__member(Var, Nondets) } ). @@ -883,8 +907,9 @@ code_info__setup_call([V - arg_info(Loc,Mode) | Rest], Direction, Code) --> { Code = tree(Code0, Code1) } ; { code_exprn__lock_reg(Reg, Exprn1, Exprn2) }, - { code_exprn__var_becomes_dead(V, Exprn2, Exprn3) }, - code_info__set_exprn_info(Exprn3), + code_info__set_exprn_info(Exprn2), + { set__singleton_set(Vset, V) }, + code_info__make_vars_dead(Vset), code_info__setup_call(Rest, Direction, Code1), code_info__get_exprn_info(Exprn4), { code_exprn__unlock_reg(Reg, Exprn4, Exprn) }, @@ -903,6 +928,12 @@ code_info__save_variable_on_stack(Var, Code) --> { code_exprn__place_var(Var, Slot, Code, Exprn0, Exprn) }, code_info__set_exprn_info(Exprn). +code_info__save_variables_on_stack([], empty) --> []. +code_info__save_variables_on_stack([Var | Vars], Code) --> + code_info__save_variable_on_stack(Var, FirstCode), + code_info__save_variables_on_stack(Vars, RestCode), + { Code = tree(FirstCode, RestCode) }. + %---------------------------------------------------------------------------% code_info__get_arginfo(ArgInfo) --> @@ -913,66 +944,17 @@ code_info__get_arginfo(ArgInfo) --> %---------------------------------------------------------------------------% %---------------------------------------------------------------------------% -code_info__generate_forced_saves(StoreMap, Code) --> - code_info__get_stack_slots(StackSlots), - code_info__get_live_variables(Vars), - code_info__generate_forced_saves_2(Vars, StoreMap, StackSlots, Code). - -:- pred code_info__generate_forced_saves_2(list(var), map(var, lval), - map(var, lval), code_tree, code_info, code_info). -:- mode code_info__generate_forced_saves_2(in, in, in, out, in, out) is det. - -code_info__generate_forced_saves_2([], _Store, _StackSlots, empty) --> []. -code_info__generate_forced_saves_2([V | Vs], Store, StackSlots, Code) --> - ( - { map__search(Store, V, Lval) } - -> - code_info__get_exprn_info(Exprn0), - { code_exprn__place_var(V, Lval, Code0, Exprn0, Exprn) }, - code_info__set_exprn_info(Exprn) +code_info__generate_branch_end(CodeModel, StoreMap, Code) --> + code_info__get_exprn_info(Exprn0), + { map__to_assoc_list(StoreMap, VarLocs) }, + { code_exprn__place_vars(VarLocs, PlaceCode, Exprn0, Exprn) }, + code_info__set_exprn_info(Exprn), + ( { CodeModel = model_non } -> + code_info__unset_failure_cont(FlushCode), + { Code = tree(PlaceCode, FlushCode) } ; - { map__search(StackSlots, V, Lval) } - -> - code_info__get_exprn_info(Exprn0), - { code_exprn__place_var(V, Lval, Code0, Exprn0, Exprn) }, - code_info__set_exprn_info(Exprn) - ; - code_info__variable_to_string(V, Name), - { term__var_to_int(V, Num) }, - { string__int_to_string(Num, NumStr) }, - { string__append_list([ - "code_info__generate_forced_saves: variable `", - Name, "' (", NumStr, ") not found"], Str) }, - { error(Str) } - ), - code_info__generate_forced_saves_2(Vs, Store, StackSlots, Code1), - { Code = tree(Code0, Code1) }. - -%---------------------------------------------------------------------------% - -code_info__generate_nondet_saves(Code) --> - code_info__get_stack_slots(StackSlots), - { map__to_assoc_list(StackSlots, StackSlotList) }, - code_info__generate_nondet_saves_2(StackSlotList, Code). - -:- pred code_info__generate_nondet_saves_2(assoc_list(var, lval), code_tree, - code_info, code_info). -:- mode code_info__generate_nondet_saves_2(in, out, in, out) is det. - -code_info__generate_nondet_saves_2([], empty) --> []. -code_info__generate_nondet_saves_2([Var - StackThing | VarSlots], Code) --> - ( - code_info__variable_is_live(Var) - -> - code_info__get_exprn_info(Exprn0), - { code_exprn__place_var(Var, StackThing, Code0, - Exprn0, Exprn) }, - code_info__set_exprn_info(Exprn) - ; - { Code0 = empty } - ), - { Code = tree(Code0, Code1) }, - code_info__generate_nondet_saves_2(VarSlots, Code1). + { Code = PlaceCode } + ). %---------------------------------------------------------------------------% %---------------------------------------------------------------------------% @@ -989,56 +971,24 @@ code_info__get_headvars(HeadVars) --> %---------------------------------------------------------------------------% -% code_info__remake_with_store_map rebuilds the register info and -% variable info data structures. It operates under the assumption -% that if a variable is live, it is stored somewhere. What is more, -% it assumes that if a variable is live, then there is a copy of it -% stored on the stack in the location given by the store map. This -% means that it only makes sense to call this predicate in situations -% such as the start of disjuncts where it only makes sense to have -% variables on the stack. (Note that disj_gen contains a piece of -% magic to use the register versions of variables if possible in the -% first disjunct). +% code_info__remake_with_store_map throws away the exprn_info data structure, +% forgetting the current locations of all variables, and rebuilds it from +% scratch based on the given store map. The new exprn_info will know about +% only the variables present in the store map, and will believe they are +% where the store map says they are. code_info__remake_with_store_map(StoreMap) --> + { map__to_assoc_list(StoreMap, VarLvals) }, + { code_info__fixup_lvallist(VarLvals, VarRvals) }, code_info__get_varset(Varset), - code_info__get_live_variables(VarList), - { set__list_to_set(VarList, Vars) }, - code_info__get_stack_slots(StackSlots), - { map__overlay(StackSlots, StoreMap, LvalMap0) }, - { map__select(LvalMap0, Vars, LvalMap) }, - { map__to_assoc_list(LvalMap, VarLvals0) }, - { code_info__fixup_lvallist(VarLvals0, VarLvals) }, code_info__get_globals(Globals), { globals__get_options(Globals, Options) }, - { code_exprn__init_state(VarLvals, Varset, Options, Exprn) }, + { code_exprn__init_state(VarRvals, Varset, Options, Exprn) }, code_info__set_exprn_info(Exprn). %---------------------------------------------------------------------------% %---------------------------------------------------------------------------% -% code_info__remake_with_stack_slots rebuilds the register info and the -% variable info structures. It operates under the assumption that if -% a variable is live, then it should be located in the storage indicated -% by the call info information. This is for use at the end of the branches -% in branched structures, where we need to ensure that variables are -% stored in consistient places. - -code_info__remake_with_stack_slots --> - code_info__get_varset(Varset), - code_info__get_live_variables(VarList), - { set__list_to_set(VarList, Vars) }, - code_info__get_stack_slots(StackSlots), - { map__select(StackSlots, Vars, LvalMap) }, - { map__to_assoc_list(LvalMap, VarLvals0) }, - { code_info__fixup_lvallist(VarLvals0, VarLvals) }, - code_info__get_globals(Globals), - { globals__get_options(Globals, Options) }, - { code_exprn__init_state(VarLvals, Varset, Options, Exprn) }, - code_info__set_exprn_info(Exprn). - -%---------------------------------------------------------------------------% - :- pred code_info__fixup_lvallist(assoc_list(var, lval), assoc_list(var, rval)). :- mode code_info__fixup_lvallist(in, out) is det. @@ -1060,27 +1010,26 @@ code_info__update_deadness_info(Deaths) --> %---------------------------------------------------------------------------% -code_info__make_vars_dead(Vars0, Code) --> +code_info__pickup_zombies(Zombies) --> + code_info__get_zombies(Zombies), + { set__init(Empty) }, + code_info__set_zombies(Empty). + +%---------------------------------------------------------------------------% + +code_info__make_vars_dead(Vars0) --> code_info__current_resume_point_vars(ResumeVars), { set__intersect(Vars0, ResumeVars, FlushVars) }, - { set__to_sorted_list(FlushVars, FlushVarList) }, - code_info__flush_resume_variables(FlushVarList, Code), + code_info__get_zombies(Zombies0), + { set__union(Zombies0, FlushVars, Zombies) }, + code_info__set_zombies(Zombies), code_info__get_nondet_lives(NondetLives), - % Don't kill off nondet-live variables - { set__difference(Vars0, NondetLives, Vars) }, + % Don't kill off nondet-live or zombie variables + { set__difference(Vars0, NondetLives, Vars1) }, + { set__difference(Vars1, Zombies, Vars) }, { set__to_sorted_list(Vars, VarList) }, code_info__make_vars_dead_2(VarList). -:- pred code_info__flush_resume_variables(list(var), code_tree, - code_info, code_info). -:- mode code_info__flush_resume_variables(in, out, in, out) is det. - -code_info__flush_resume_variables([], empty) --> []. -code_info__flush_resume_variables([Var | Vars], Code) --> - code_info__save_variable_on_stack(Var, FirstCode), - code_info__flush_resume_variables(Vars, RestCode), - { Code = tree(FirstCode, RestCode) }. - :- pred code_info__make_vars_dead_2(list(var), code_info, code_info). :- mode code_info__make_vars_dead_2(in, in, out) is det. @@ -1097,28 +1046,29 @@ code_info__make_vars_dead_2([V | Vs]) --> % We don't care where they are put. code_info__make_vars_live(Vars) --> - { set__to_sorted_list(Vars, VarList) }, - code_info__make_vars_live_2(VarList, 1). - -:- pred code_info__make_vars_live_2(list(var), int, code_info, code_info). -:- mode code_info__make_vars_live_2(in, in, in, out) is det. - -code_info__make_vars_live_2([], _) --> []. -code_info__make_vars_live_2([V | Vs], N0) --> - code_info__get_exprn_info(Exprn0), code_info__get_stack_slots(StackSlots), + code_info__get_exprn_info(Exprn0), + { set__to_sorted_list(Vars, VarList) }, + { code_info__make_vars_live_2(VarList, StackSlots, 1, Exprn0, Exprn) }, + code_info__set_exprn_info(Exprn). + +:- pred code_info__make_vars_live_2(list(var), stack_slots, int, + exprn_info, exprn_info). +:- mode code_info__make_vars_live_2(in, in, in, in, out) is det. + +code_info__make_vars_live_2([], _, _, Exprn, Exprn). +code_info__make_vars_live_2([V | Vs], StackSlots, N0, Exprn0, Exprn) :- ( - { map__search(StackSlots, V, Lval0) } + map__search(StackSlots, V, Lval0) -> - { Lval = Lval0 }, - { N1 = N0 } + Lval = Lval0, + N1 = N0 ; - { code_info__find_unused_reg(N0, Exprn0, N1) }, - { Lval = reg(r(N1)) } + code_info__find_unused_reg(N0, Exprn0, N1), + Lval = reg(r(N1)) ), - { code_exprn__maybe_set_var_location(V, Lval, Exprn0, Exprn) }, - code_info__set_exprn_info(Exprn), - code_info__make_vars_live_2(Vs, N1). + code_exprn__maybe_set_var_location(V, Lval, Exprn0, Exprn1), + code_info__make_vars_live_2(Vs, StackSlots, N1, Exprn1, Exprn). :- pred code_info__find_unused_reg(int, exprn_info, int). :- mode code_info__find_unused_reg(in, in, out) is det. @@ -1131,32 +1081,6 @@ code_info__find_unused_reg(N0, Exprn0, N) :- N = N0 ). -% :- pred code_info__make_vars_live_2(list(var), code_info, code_info). -% :- mode code_info__make_vars_live_2(in, in, out) is det. -% -% code_info__make_vars_live_2([], _) --> []. -% code_info__make_vars_live_2([V | Vs]) --> -% ( -% { map__search(StoreMap, V, Lval0) } -% -> -% { Lval = Lval0 } -% ; -% code_info__get_stack_slots(StackSlots), -% { map__search(StackSlots, V, Lval0) } -% -> -% { Lval = Lval0 } -% ; -% code_info__get_varset(Varset), -% { varset__lookup_name(Varset, V, Name) }, -% { string__append("I don't know where to put variable ", -% Name, Msg) }, -% { error(Msg) } -% ), -% code_info__get_exprn_info(Exprn0), -% { code_exprn__maybe_set_var_location(V, Lval, Exprn0, Exprn) }, -% code_info__set_exprn_info(Exprn), -% code_info__make_vars_live_2(Vs, StoreMap). - %---------------------------------------------------------------------------% code_info__maybe_save_hp(Maybe, Code) --> @@ -1293,7 +1217,7 @@ code_info__stack_variable(Num, Lval) --> % The stack pointer points to the first free location at the % top of the stack. % - % `code_info__num_stackslots' counts the number of slots reserved + % `code_info__num_stackslots' counts the number of slots reserved % for saving local variables. % % `code_info__max_push_count' counts the number of slots reserved @@ -1351,7 +1275,7 @@ code_info__add_commit_val(Item, StackVar) --> code_info__rem_commit_val --> code_info__get_commit_vals(Stack0), - ( + ( { Stack0 = [_ | Stack] }, code_info__set_commit_vals(Stack) ; @@ -1399,64 +1323,62 @@ code_info__get_stack_top(StackVar) --> %---------------------------------------------------------------------------% code_info__generate_failure(Code) --> - code_info__grab_code_info(CodeInfo), - code_info__failure_cont(failure_cont(Cont, _MaybeRedoLab, FailureMap)), + code_info__top_failure_cont(FailureCont), + { FailureCont = failure_cont(Cont, _MaybeRedoLabel, FailureMap) }, ( { Cont = known(_) }, ( - code_info__pick_failure(FailureMap, FailureAddress0) + code_info__pick_matching_resume_addr(FailureMap, + FailureAddress0) -> { FailureAddress = FailureAddress0 }, { PlaceCode = empty } ; - { FailureMap = [Map - Addr | _] } - -> - { FailureAddress = Addr }, + { code_info__pick_first_resume_point(FailureMap, + Map, FailureAddress) }, { map__to_assoc_list(Map, AssocList) }, - code_info__place_vars(AssocList, PlaceCode) - ; - {error("code_info__generate_failure: no valid failmap")} + code_info__grab_code_info(CodeInfo), + code_info__place_vars(AssocList, PlaceCode), + code_info__slap_code_info(CodeInfo) ), { BranchCode = node([goto(FailureAddress) - "fail"]) }, { Code = tree(PlaceCode, BranchCode) } ; { Cont = unknown }, { Code = node([goto(do_redo) - "fail"]) } - ), - code_info__slap_code_info(CodeInfo). - -%---------------------------------------------------------------------------% - -code_info__generate_under_failure(Code) --> - code_info__grab_code_info(CodeInfo), - code_info__pop_failure_cont, - code_info__generate_failure(Code), - code_info__slap_code_info(CodeInfo). + ). %---------------------------------------------------------------------------% code_info__generate_test_and_fail(Rval0, Code) --> - code_info__failure_cont(failure_cont(Cont, _MaybeRedoLab, FailureMap)), + code_info__top_failure_cont(FailureCont), + { FailureCont = failure_cont(Cont, _MaybeRedoLabel, FailureMap) }, ( { Cont = known(_) }, ( - code_info__pick_failure(FailureMap, FailureAddress0) + code_info__pick_matching_resume_addr(FailureMap, + FailureAddress0) -> % We branch away if the test *fails* { code_util__neg_rval(Rval0, Rval) }, - { Code = node([ if_val(Rval, FailureAddress0) - - "Test for failure" ]) } + { Code = node([ + if_val(Rval, FailureAddress0) - + "Test for failure" + ]) } ; - { FailureMap = [Map - Addr | _] } - -> - { FailureAddress = Addr }, + { code_info__pick_first_resume_point(FailureMap, + Map, FailureAddress) }, { map__to_assoc_list(Map, AssocList) }, code_info__get_next_label(SuccessLabel), code_info__grab_code_info(CodeInfo), code_info__place_vars(AssocList, PlaceCode), code_info__slap_code_info(CodeInfo), { SuccessAddress = label(SuccessLabel) }, - % We branch away if the test Succeeds + % We branch away if the test *fails*, + % therefore we branch around the code + % that moves variables to their failure + % locations and branches away + % if the test succeeds { TestCode = node([ if_val(Rval0, SuccessAddress) - "Test for failure" ]) }, { FailCode = tree(PlaceCode, node([ @@ -1465,55 +1387,113 @@ code_info__generate_test_and_fail(Rval0, Code) --> { Code = tree(tree(TestCode, FailCode), node([ label(SuccessLabel) - "success continuation" ])) } - ; - { error("code_info__generate_test_and_fail: no valid failmap") } ) ; { Cont = unknown }, { FailureAddress = do_redo }, % We branch away if the test *fails* { code_util__neg_rval(Rval0, Rval) }, - { Code = node([ if_val(Rval, FailureAddress) - - "Test for failure" ]) } + { Code = node([ + if_val(Rval, FailureAddress) - + "Test for failure" + ]) } ). %---------------------------------------------------------------------------% code_info__can_generate_direct_branch(CodeAddr) --> - code_info__failure_cont(failure_cont(known(no), no, FailureMap)), - code_info__pick_failure(FailureMap, CodeAddr). - % { FailureMap = [Map - CodeAddr | _] }, - % { map__is_empty(Map) }. + code_info__top_failure_cont(FailureCont), + { FailureCont = failure_cont(known(no), no, FailureMap) }, + code_info__pick_matching_resume_addr(FailureMap, CodeAddr). %---------------------------------------------------------------------------% -:- pred code_info__pick_failure(assoc_list(map(var, set(rval)), code_addr), - code_addr, code_info, code_info). -:- mode code_info__pick_failure(in, out, in, out) is semidet. + % 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. -code_info__pick_failure([], _CodeAddr) --> - { fail }. -code_info__pick_failure([Map - Addr | Rest], CodeAddr) --> - { map__keys(Map, KeyList) }, - { set__list_to_set(KeyList, Keys) }, - code_info__variable_locations(Locations0), - { map__select(Locations0, Keys, Locations) }, - { map__to_assoc_list(Locations, List) }, - ( - \+ ( - { list__member(Thingy, List) }, - \+ ( - { Thingy = Var - Actual }, - { map__search(Map, Var, Rvals) }, - { set__subset(Rvals, Actual) } - ) +:- pred code_info__pick_matching_resume_addr(resume_maps, code_addr, + code_info, code_info). +:- mode code_info__pick_matching_resume_addr(in, out, in, out) is semidet. + +code_info__pick_matching_resume_addr(ResumeMaps, Addr) --> + code_info__variable_locations(Locations), + { + ResumeMaps = orig_only(Map1, Addr1), + ( code_info__match_resume_loc(Map1, Locations) -> + Addr = Addr1 + ; + fail ) - -> - { CodeAddr = Addr } ; - code_info__pick_failure(Rest, CodeAddr) + ResumeMaps = stack_only(Map1, Addr1), + ( code_info__match_resume_loc(Map1, Locations) -> + Addr = Addr1 + ; + fail + ) + ; + ResumeMaps = orig_and_stack(Map1, Addr1, Map2, Addr2), + ( code_info__match_resume_loc(Map1, Locations) -> + Addr = Addr1 + ; code_info__match_resume_loc(Map2, Locations) -> + Addr = Addr2 + ; + fail + ) + ; + ResumeMaps = stack_and_orig(Map1, Addr1, Map2, Addr2), + ( code_info__match_resume_loc(Map1, Locations) -> + Addr = Addr1 + ; code_info__match_resume_loc(Map2, Locations) -> + Addr = Addr2 + ; + fail + ) + }. + +:- pred code_info__match_resume_loc(resume_map, resume_map). +:- mode code_info__match_resume_loc(in, in) is semidet. + +code_info__match_resume_loc(Map, Locations0) :- + map__keys(Map, KeyList), + set__list_to_set(KeyList, Keys), + map__select(Locations0, Keys, Locations), + map__to_assoc_list(Locations, List), + \+ ( + list__member(Thingy, List), + \+ ( + Thingy = Var - Actual, + map__search(Map, Var, Rvals), + set__subset(Rvals, Actual) + ) ). +:- pred code_info__pick_first_resume_point(resume_maps, resume_map, code_addr). +:- mode code_info__pick_first_resume_point(in, out, out) is det. + +code_info__pick_first_resume_point(orig_only(Map, Addr), Map, Addr). +code_info__pick_first_resume_point(stack_only(Map, Addr), Map, Addr). +code_info__pick_first_resume_point(orig_and_stack(Map, Addr, _, _), Map, Addr). +code_info__pick_first_resume_point(stack_and_orig(Map, Addr, _, _), Map, Addr). + +:- pred code_info__pick_last_resume_point(resume_maps, resume_map, code_addr). +:- mode code_info__pick_last_resume_point(in, out, out) is det. + +code_info__pick_last_resume_point(orig_only(Map, Addr), Map, Addr). +code_info__pick_last_resume_point(stack_only(Map, Addr), Map, Addr). +code_info__pick_last_resume_point(orig_and_stack( _, _, Map, Addr), Map, Addr). +code_info__pick_last_resume_point(stack_and_orig( _, _, Map, Addr), Map, Addr). + +:- pred code_info__pick_stack_resume_point(resume_maps, resume_map, code_addr). +:- mode code_info__pick_stack_resume_point(in, out, out) is det. + +code_info__pick_stack_resume_point(orig_only(_, _), _, _) :- + error("no stack resume point"). +code_info__pick_stack_resume_point(stack_only(Map, Addr), Map, Addr). +code_info__pick_stack_resume_point(orig_and_stack(_, _, Map, Addr), Map, Addr). +code_info__pick_stack_resume_point(stack_and_orig(Map, Addr, _, _), Map, Addr). + %---------------------------------------------------------------------------% :- pred code_info__variable_locations(map(var, set(rval)), @@ -1528,59 +1508,27 @@ code_info__variable_locations(Locations) --> % `semi_pre_commit' and `semi_commit' should be generated as a pair % surrounding a nondet goal. + % % `generate_semi_pre_commit' returns a label (a failure cont label) % which should be passed to `generate_semi_commit'. % If the goal succeeds, the `commit' will cut any choice points % generated in the goal. -code_info__generate_semi_pre_commit(RedoLab, PreCommit) --> - code_info__get_proc_model(CodeModel), - ( { CodeModel = model_non } -> - % the pushes and pops on the det stack below will cause - % problems for accurate garbage collection. Hence we - % make sure the commit vals are made live, so gc - % can figure out what is going on later. - code_info__get_module_info(ModuleInfo), - code_info__get_pred_id(PredId), - { predicate_name(ModuleInfo, PredId, PredName) }, - { string__append("commit in ", PredName, Message) }, - { PushCode = node([ - incr_sp(3, Message) - - "push space for curfr, maxfr, and redoip" - ]) }, - { CurfrSlot = stackvar(1) }, - { MaxfrSlot = stackvar(2) }, - { RedoipSlot = stackvar(3) }, - code_info__add_commit_val(curfr, CurfrSlot), - code_info__add_commit_val(maxfr, MaxfrSlot), - code_info__add_commit_val(redoip(lval(maxfr)), RedoipSlot) - ; - { PushCode = empty }, - code_info__push_temp(lval(curfr), CurfrSlot), - code_info__push_temp(lval(maxfr), MaxfrSlot), - code_info__push_temp(lval(redoip(lval(maxfr))), RedoipSlot) - ), - { SaveCode = node([ - assign(CurfrSlot, lval(curfr)) - - "Save current nondet frame pointer", - assign(MaxfrSlot, lval(maxfr)) - - "Save top of nondet stack", - assign(RedoipSlot, lval(redoip(lval(maxfr)))) - - "Save the top redoip" - ]) }, - code_info__failure_cont(failure_cont(_OrigCont, _MaybeRedo, - FailureMap0)), - code_info__relabel_failure_cont(FailureMap0, FailureMap), +code_info__generate_semi_pre_commit(RedoLabel, PreCommit) --> + code_info__generate_pre_commit_saves(SaveCode), + code_info__top_failure_cont(FailureCont), + { FailureCont = failure_cont(_OrigCont, _MaybeRedo, FailureMap0) }, + code_info__clone_failure_cont(FailureMap0, FailureMap), code_info__push_failure_cont(failure_cont(known(yes), no, FailureMap)), - code_info__get_next_label(RedoLab), + code_info__get_next_label(RedoLabel), { HijackCode = node([ assign(redoip(lval(maxfr)), - const(code_addr_const(label(RedoLab)))) - + const(code_addr_const(label(RedoLabel)))) - "Hijack the failure cont" ]) }, - { PreCommit = tree(tree(PushCode, SaveCode), HijackCode) }. + { PreCommit = tree(SaveCode, HijackCode) }. -code_info__generate_semi_commit(RedoLab, Commit) --> +code_info__generate_semi_commit(RedoLabel, Commit) --> code_info__get_next_label(SuccLabel), { GotoSuccLabel = node([ goto(label(SuccLabel)) - "Jump to success continuation" @@ -1588,54 +1536,44 @@ code_info__generate_semi_commit(RedoLab, Commit) --> { SuccLabelCode = node([ label(SuccLabel) - "Success continuation" ]) }, - { RedoLabCode = node([ - label(RedoLab) - "Failure (redo) continuation" + { RedoLabelCode = node([ + label(RedoLabel) - "Failure (redo) continuation" ]) }, + code_info__grab_code_info(CodeInfo0), - code_info__failure_cont(failure_cont(_OldCont, _MaybeRedo, - HFailureMap)), - code_info__generate_failure_continuation(HFailureMap, FailureContCode), - code_info__generate_under_failure(Fail), + code_info__top_failure_cont(FailureCont), + { FailureCont = failure_cont(_OldCont, _MaybeRedo, HFailureMap) }, + code_info__generate_resume_setup(HFailureMap, FailureContCode), + code_info__pop_failure_cont, + code_info__generate_failure(Fail), code_info__slap_code_info(CodeInfo0), code_info__pop_failure_cont, - code_info__get_proc_model(CodeModel), - ( { CodeModel = model_non } -> - { PopCode = node([decr_sp(3) - - "pop redoip, maxfr & curfr"]) }, - { RedoipSlot = stackvar(3) }, - { MaxfrSlot = stackvar(2) }, - { CurfrSlot = stackvar(1) }, - code_info__rem_commit_val, - code_info__rem_commit_val, - code_info__rem_commit_val - ; - { PopCode = empty }, - code_info__pop_temp(RedoipSlot), - code_info__pop_temp(MaxfrSlot), - code_info__pop_temp(CurfrSlot) - ), - { RestoreMaxfr = node([ - assign(maxfr, lval(MaxfrSlot)) - - "Prune away unwanted choice-points" - ]) }, - { RestoreRedoip = node([ - assign(redoip(lval(maxfr)), lval(RedoipSlot)) - - "Restore the top redoip" - ]) }, - { RestoreCurfr = node([ - assign(curfr, lval(CurfrSlot)) - - "Restore nondet frame pointer" - ]) }, - { SuccessCode = tree( - RestoreMaxfr, - tree(tree(RestoreRedoip, RestoreCurfr), PopCode) - ) }, - { FailCode = tree( - tree(tree(RedoLabCode, RestoreCurfr), FailureContCode), - tree(tree(RestoreRedoip, PopCode), Fail) - ) }, - { Commit = tree(tree(SuccessCode, tree(GotoSuccLabel, FailCode)), - SuccLabelCode) }. + + code_info__undo_pre_commit_saves(RestoreMaxfr, RestoreRedoip, + RestoreCurfr, PopCode), + + { SuccessCode = + tree(RestoreMaxfr, + tree(RestoreRedoip, + tree(RestoreCurfr, + PopCode))) + }, + { FailCode = + tree(RedoLabelCode, + tree(RestoreCurfr, + tree(FailureContCode, + tree(RestoreRedoip, + tree(PopCode, + Fail))))) + }, + { Commit = + tree(SuccessCode, + tree(GotoSuccLabel, + tree(FailCode, + SuccLabelCode))) + }. + +%---------------------------------------------------------------------------% % `det_pre_commit' and `det_commit' should be generated as a pair % surrounding a multidet goal. @@ -1643,10 +1581,27 @@ code_info__generate_semi_commit(RedoLab, Commit) --> % generated in the goal. code_info__generate_det_pre_commit(PreCommit) --> + code_info__generate_pre_commit_saves(PreCommit). + +code_info__generate_det_commit(Commit) --> + code_info__undo_pre_commit_saves(RestoreMaxfr, RestoreRedoip, + RestoreCurfr, PopCode), + { Commit = tree(RestoreMaxfr, + tree(RestoreRedoip, + tree(RestoreCurfr, + PopCode))) + }. + +%---------------------------------------------------------------------------% + +:- pred code_info__generate_pre_commit_saves(code_tree, code_info, code_info). +:- mode code_info__generate_pre_commit_saves(out, in, out) is det. + +code_info__generate_pre_commit_saves(Code) --> code_info__get_proc_model(CodeModel), ( { CodeModel = model_non } -> % the pushes and pops on the det stack below will cause - % problems for accurate garbage collection. Hence we + % problems for accurate garbage collection. Hence we % make sure the commit vals are made live, so gc % can figure out what is going on later. code_info__get_module_info(ModuleInfo), @@ -1654,8 +1609,8 @@ code_info__generate_det_pre_commit(PreCommit) --> { predicate_name(ModuleInfo, PredId, PredName) }, { string__append("commit in ", PredName, Message) }, { PushCode = node([ - incr_sp(3, Message) - - "push space for curfr, maxfr, and redoip" + incr_sp(3, Message) - + "push space for curfr, maxfr, and redoip" ]) }, { CurfrSlot = stackvar(1) }, { MaxfrSlot = stackvar(2) }, @@ -1677,12 +1632,17 @@ code_info__generate_det_pre_commit(PreCommit) --> assign(RedoipSlot, lval(redoip(lval(maxfr)))) - "Save the top redoip" ]) }, - { PreCommit = tree(PushCode, SaveCode) }. + { Code = tree(PushCode, SaveCode) }. -code_info__generate_det_commit(Commit) --> +:- pred code_info__undo_pre_commit_saves(code_tree, code_tree, + code_tree, code_tree, code_info, code_info). +:- mode code_info__undo_pre_commit_saves(out, out, out, out, in, out) is det. + +code_info__undo_pre_commit_saves(RestoreMaxfr, RestoreRedoip, + RestoreCurfr, PopCode) --> code_info__get_proc_model(CodeModel), ( { CodeModel = model_non } -> - { PopCode = node([decr_sp(3) - + { PopCode = node([decr_sp(3) - "pop redoip, maxfr & curfr"]) }, { RedoipSlot = stackvar(3) }, { MaxfrSlot = stackvar(2) }, @@ -1707,10 +1667,7 @@ code_info__generate_det_commit(Commit) --> { RestoreCurfr = node([ assign(curfr, lval(CurfrSlot)) - "Restore nondet frame pointer" - ]) }, - { Commit = tree( RestoreMaxfr, - tree(tree(RestoreRedoip, RestoreCurfr), PopCode) - ) }. + ]) }. %---------------------------------------------------------------------------% @@ -1723,7 +1680,7 @@ code_info__push_failure_cont(Cont) --> code_info__set_fall_through(Fall). code_info__pop_failure_cont --> - ( + ( code_info__get_fall_through(Fall0), { stack__pop(Fall0, _, Fall) }, code_info__set_fall_through(Fall) @@ -1733,7 +1690,12 @@ code_info__pop_failure_cont --> { error("code_info__pop_failure_cont: empty stack") } ). -code_info__failure_cont(Cont) --> + % lookup the value on the top of the failure continuation stack + +:- pred code_info__top_failure_cont(failure_cont, code_info, code_info). +:- mode code_info__top_failure_cont(out, in, out) is det. + +code_info__top_failure_cont(Cont) --> code_info__get_fall_through(Fall), ( { stack__top(Fall, Cont0) } @@ -1748,63 +1710,116 @@ code_info__failure_cont(Cont) --> code_info__manufacture_failure_cont(IsNondet) --> { map__init(Empty) }, ( - { IsNondet = no } - -> + { IsNondet = no }, code_info__get_next_label(ContLab1), - code_info__get_next_label(ContLab2), { Address1 = label(ContLab1) }, - { Address2 = label(ContLab2) } + { ResumeMap = stack_only(Empty, Address1) } ; + { IsNondet = yes }, { Address1 = do_fail }, - { Address2 = do_fail } + { ResumeMap = stack_only(Empty, Address1) } ), - code_info__push_failure_cont(failure_cont(known(IsNondet), - no, [Empty - Address1, Empty - Address2])). + { FailureCont = failure_cont(known(IsNondet), no, ResumeMap) }, + code_info__push_failure_cont(FailureCont). -code_info__make_known_failure_cont(Vars, IsNondet, ModContCode) --> - code_info__get_next_label(ContLab), - code_info__get_next_label(StackLab), +%---------------------------------------------------------------------------% + +code_info__make_known_failure_cont(ResumeVars, ResumeLocs, IsNondet, + HaveTempFrame0, HaveTempFrame, ModContCode) --> + code_info__get_next_label(OrigLabel), + code_info__get_next_label(StackLabel), + { OrigAddr = label(OrigLabel) }, + { StackAddr = label(StackLabel) }, ( - { IsNondet = no } - -> % In semidet continuations we don't use the redoip - { HijackCode = empty }, - { MaybeRedoLab = no } + % of the top stack frame. + + { IsNondet = no }, + { TempFrameCode = empty }, + { MaybeRedoLabel = no }, + { HaveTempFrame = HaveTempFrame0 } ; - code_info__failure_cont(failure_cont(OrigCont, _, _)), - { OrigCont = unknown } - -> - % efficiency of this code could be improved - % ("mkframe()" is a bit of a sledge hammer) - code_info__get_next_label(RedoLab), - { MaybeRedoLab = yes(RedoLab) }, - { HijackCode = - node([ - mkframe("hijack", 1, label(RedoLab)) - - "create a temporary frame", - assign(curfr, lval(succfr(lval(maxfr)))) - - "restore curfr (which was clobbered by mkframe)" - ]) - } - ; - { MaybeRedoLab = no }, - { HijackCode = node([ - assign(redoip(lval(maxfr)), - const(code_addr_const(label(StackLab)))) - - "Set failure continuation" - ]) } + % In nondet continuations we may use the redoip + % of the top stack frame. Therefore we must ensure + % that this redoip is free for use, creating our own + % frame if necessary. + + { IsNondet = yes }, + code_info__top_failure_cont(FailureCont), + { FailureCont = failure_cont(OrigCont, _, _) }, + ( + { OrigCont = unknown } + -> + code_info__get_next_label(RedoLabel), + { MaybeRedoLabel = yes(RedoLabel) }, + { RedoAddr = label(RedoLabel) }, + ( + { HaveTempFrame0 = no } + -> + % this code could be better + % (mkframe is a bit of a sledge hammer) + { TempFrameCode = node([ + mkframe("temp frame", 1, RedoAddr) + - "create a temporary frame", + assign(curfr, lval(succfr(lval(maxfr)))) + - "restore curfr after mkframe" + ]) } + ; + { TempFrameCode = node([ + assign(redoip(lval(maxfr)), + const(code_addr_const(RedoAddr))) + - "Set failure continuation" + ]) } + ), + { HaveTempFrame = yes } + ; + { MaybeRedoLabel = no }, + { TempFrameCode = node([ + assign(redoip(lval(maxfr)), + const(code_addr_const(StackAddr))) - + "Set failure continuation" + ]) }, + { HaveTempFrame = HaveTempFrame0 } + ) + ), + { set__to_sorted_list(ResumeVars, VarList) }, + ( + { ResumeLocs = orig_only }, + code_info__produce_vars(VarList, OrigMap, OrigCode), + { ResumeMaps = orig_only(OrigMap, OrigAddr) } + ; + { ResumeLocs = stack_only }, + code_info__produce_vars(VarList, _OrigMap, OrigCode), + code_info__get_stack_slots(StackSlots), + { map__select(StackSlots, ResumeVars, StackMap0) }, + { map__to_assoc_list(StackMap0, StackList0) }, + { code_info__tweak_stacklist(StackList0, StackList) }, + { map__from_assoc_list(StackList, StackMap) }, + { ResumeMaps = stack_only(StackMap, StackAddr) } + ; + { ResumeLocs = orig_and_stack }, + code_info__produce_vars(VarList, OrigMap, OrigCode), + code_info__get_stack_slots(StackSlots), + { map__select(StackSlots, ResumeVars, StackMap0) }, + { map__to_assoc_list(StackMap0, StackList0) }, + { code_info__tweak_stacklist(StackList0, StackList) }, + { map__from_assoc_list(StackList, StackMap) }, + { ResumeMaps = orig_and_stack(OrigMap, OrigAddr, + StackMap, StackAddr) } + ; + { ResumeLocs = stack_and_orig }, + code_info__produce_vars(VarList, OrigMap, OrigCode), + code_info__get_stack_slots(StackSlots), + { map__select(StackSlots, ResumeVars, StackMap0) }, + { map__to_assoc_list(StackMap0, StackList0) }, + { code_info__tweak_stacklist(StackList0, StackList) }, + { map__from_assoc_list(StackList, StackMap) }, + { ResumeMaps = stack_and_orig(StackMap, StackAddr, + OrigMap, OrigAddr) } ), - { set__to_sorted_list(Vars, VarList) }, - code_info__produce_vars(VarList, RegMap, RegCode), - code_info__get_stack_slots(StackSlots), - { map__select(StackSlots, Vars, StackMap0) }, - { map__to_assoc_list(StackMap0, StackList0) }, - { code_info__tweak_stacklist(StackList0, StackList) }, - { map__from_assoc_list(StackList, StackMap) }, - { ContMap = [RegMap-label(ContLab), StackMap-label(StackLab)] }, code_info__push_failure_cont( - failure_cont(known(IsNondet), MaybeRedoLab, ContMap)), - { ModContCode = tree(RegCode, HijackCode) }. + failure_cont(known(IsNondet), MaybeRedoLabel, ResumeMaps)), + { ModContCode = tree(OrigCode, TempFrameCode) }. %---------------------------------------------------------------------------% @@ -1816,6 +1831,7 @@ code_info__produce_vars([], Map, empty) --> { map__init(Map) }. code_info__produce_vars([V | Vs], Map, Code) --> code_info__produce_vars(Vs, Map0, Code0), + % XXX should use follow_vars to decide whether to put into reg code_info__produce_variable_in_reg_or_stack(V, Code1, Rval), { set__singleton_set(Rvals, Rval) }, { map__set(Map0, V, Rvals, Map) }, @@ -1834,134 +1850,148 @@ code_info__tweak_stacklist([V-L | Rest0], [V-Rs | Rest]) :- %---------------------------------------------------------------------------% -code_info__unset_failure_cont --> - code_info__failure_cont( - failure_cont(_OrigCont, MaybeRedoLabel, FailureMap)), +code_info__unset_failure_cont(Code) --> + code_info__flush_resume_vars_to_stack(Code), + code_info__top_failure_cont(FailureCont0), + { FailureCont0 = failure_cont(_, MaybeRedoLabel, FailureMap) }, code_info__pop_failure_cont, - code_info__push_failure_cont( - failure_cont(unknown, MaybeRedoLabel, FailureMap)). + { FailureCont = failure_cont(unknown, MaybeRedoLabel, FailureMap) }, + code_info__push_failure_cont(FailureCont). + +code_info__flush_resume_vars_to_stack(Code) --> + code_info__top_failure_cont(FailureCont0), + { FailureCont0 = failure_cont(_, _, FailureMap) }, + { code_info__pick_stack_resume_point(FailureMap, StackMap, _) }, + { map__to_assoc_list(StackMap, StackLocs) }, + code_info__place_resume_vars(StackLocs, Code). %---------------------------------------------------------------------------% -:- pred code_info__relabel_failure_cont(failure_map, failure_map, +:- pred code_info__clone_failure_cont(resume_maps, resume_maps, code_info, code_info). -:- mode code_info__relabel_failure_cont(in, out, in, out) is det. +:- mode code_info__clone_failure_cont(in, out, in, out) is det. -code_info__relabel_failure_cont([], []) --> []. -code_info__relabel_failure_cont([Map - _ | Rest0], [Map - L | Rest]) --> - code_info__get_next_label(L0), - { L = label(L0) }, - code_info__relabel_failure_cont(Rest0, Rest). +code_info__clone_failure_cont(ResumeMaps0, ResumeMaps) --> + ( + { ResumeMaps0 = orig_only(Map1, _) }, + code_info__get_next_label(Label1), + { Addr1 = label(Label1) }, + { ResumeMaps = orig_only(Map1, Addr1) } + ; + { ResumeMaps0 = stack_only(Map1, _) }, + code_info__get_next_label(Label1), + { Addr1 = label(Label1) }, + { ResumeMaps = stack_only(Map1, Addr1) } + ; + { ResumeMaps0 = stack_and_orig(Map1, _, Map2, _) }, + code_info__get_next_label(Label1), + { Addr1 = label(Label1) }, + code_info__get_next_label(Label2), + { Addr2 = label(Label2) }, + { ResumeMaps = stack_and_orig(Map1, Addr1, Map2, Addr2) } + ; + { ResumeMaps0 = orig_and_stack(Map1, _, Map2, _) }, + code_info__get_next_label(Label1), + { Addr1 = label(Label1) }, + code_info__get_next_label(Label2), + { Addr2 = label(Label2) }, + { ResumeMaps = orig_and_stack(Map1, Addr1, Map2, Addr2) } + ). %---------------------------------------------------------------------------% -code_info__modify_failure_cont(ModifyCode) --> - code_info__failure_cont(failure_cont(OldCont, MaybeRedo0, FailureMap)), - code_info__generate_failure_cont(FailureMap, FailureCode), - code_info__pop_failure_cont, - code_info__get_next_label(NewRegCont), - code_info__get_next_label(NewStackCont), - ( - { OldCont = unknown ; OldCont = known(yes) } - -> - { NewCont = known(yes) }, - ( { MaybeRedo0 = yes(_OldRedo) } -> - code_info__get_next_label(NewRedoCont), - { MaybeRedo = yes(NewRedoCont) } - ; - { NewRedoCont = NewStackCont }, - { MaybeRedo = no } - ), - { ResetCode = node([ - assign(redoip(lval(maxfr)), - const(code_addr_const(label(NewRedoCont)))) - - "modify failure cont" - ]) } - ; - { error("code_info__modify_failure_cont: semidet context") } - % { NewCont = known(no) }, - % { ResetCode = empty }, - % { MaybeRedo = no } - ), - ( - { FailureMap = [RegMap - _RegCont, StackMap - _StackCont] } - -> - code_info__push_failure_cont(failure_cont(NewCont, MaybeRedo, - [RegMap - label(NewRegCont), - StackMap - label(NewStackCont)])) - ; - { error("code_info__modify_failure_cont: bad failure map.") } - ), - { ModifyCode = tree(FailureCode, ResetCode) }. +% :- pred code_info__modify_failure_cont(code_tree, code_info, code_info). +% :- mode code_info__modify_failure_cont(out, in, out) is det. +% +% code_info__modify_failure_cont(ModifyCode) --> +% code_info__top_failure_cont(FailureCont), +% { FailureCont = failure_cont(OldCont, MaybeRedo0, FailureMap) }, +% code_info__generate_failure_cont(FailureCont, FailureCode), +% code_info__pop_failure_cont, +% code_info__get_next_label(NewRegCont), +% code_info__get_next_label(NewStackCont), +% ( +% { OldCont = unknown ; OldCont = known(yes) } +% -> +% { NewCont = known(yes) }, +% ( { MaybeRedo0 = yes(_OldRedo) } -> +% code_info__get_next_label(NewRedoCont), +% { MaybeRedo = yes(NewRedoCont) } +% ; +% { NewRedoCont = NewStackCont }, +% { MaybeRedo = no } +% ), +% { ResetCode = node([ +% assign(redoip(lval(maxfr)), +% const(code_addr_const(label(NewRedoCont)))) - +% "modify failure cont" +% ]) } +% ; +% { error("code_info__modify_failure_cont: semidet context") } +% % { NewCont = known(no) }, +% % { ResetCode = empty }, +% % { MaybeRedo = no } +% ), +% ( +% { FailureMap = [RegMap - _RegCont, StackMap - _StackCont] } +% -> +% code_info__push_failure_cont(failure_cont(NewCont, MaybeRedo, +% [RegMap - label(NewRegCont), +% StackMap - label(NewStackCont)])) +% ; +% { error("code_info__modify_failure_cont: bad failure map.") } +% ), +% { ModifyCode = tree(FailureCode, ResetCode) }. % XXX rewrite this to fit on one screeen code_info__restore_failure_cont(Code) --> - code_info__failure_cont(failure_cont(CurrentCont, _Redo1, FailureMap)), - code_info__generate_failure_cont(FailureMap, FailureCode), + code_info__top_failure_cont(CurFailureCont), + { CurFailureCont = failure_cont(CurrentCont, _Redo1, _FailureMap) }, + code_info__generate_failure_cont(CurFailureCont, FailureCode), code_info__pop_failure_cont, % Fixup the redoip of the top frame if necessary ( { CurrentCont = known(no) } -> - { RestoreCode = empty }, { ResetCode = empty } ; % { CurrentCont = known(yes) ; CurrentCont = unknown } - { RestoreCode = empty }, - code_info__failure_cont(failure_cont(NewCont, MaybeRedoLab, - NewFailureMap)), - ( - { NewCont = unknown }, - { ResetCode = node([ + code_info__top_failure_cont(NewFailureCont), + { NewFailureCont = failure_cont(NewCont, _, _) }, + { + NewCont = unknown, + ResetCode = node([ assign(redoip(lval(maxfr)), const(code_addr_const(do_fail))) - "restore failure cont" - ]) } + ]) ; - { NewCont = known(NondetCont) }, + NewCont = known(NondetCont), ( - { NondetCont = no }, - { ResetCode = empty } + NondetCont = no, + ResetCode = empty ; - { NondetCont = yes }, - ( - { MaybeRedoLab = yes(RedoLab) } - -> - { NewRedoAddress = label(RedoLab) } - ; - { NewFailureMap = [_, _Map - Cont] } - -> - { NewRedoAddress = Cont } - ; - { error("code_info__restore_failure_cont: no valid failure-map") } - ), - { ResetCode = node([ + NondetCont = yes, + code_info__find_first_resume_label( + NewFailureCont, NewRedoAddress), + ResetCode = node([ assign(redoip(lval(maxfr)), const(code_addr_const(NewRedoAddress))) - "restore failure cont" - ]) } + ]) ) - ) + } ), - { Code = tree(FailureCode, tree(RestoreCode, ResetCode)) }. + { Code = tree(FailureCode, ResetCode) }. %---------------------------------------------------------------------------% code_info__do_soft_cut(TheFrame, Code) --> - code_info__failure_cont(failure_cont(ContType, MaybeRedo, FailMap)), + code_info__top_failure_cont(FailureCont), + { FailureCont = failure_cont(ContType, _, _) }, ( { ContType = known(_) }, - ( - { MaybeRedo = yes(RedoLab) } - -> - { Address = label(RedoLab) } - ; - { FailMap = [_, _Places - StackLab] } - -> - { Address = StackLab } - ; - { error("code_info__do_soft_cut: invalid failmap") } - ) + { code_info__find_first_resume_label(FailureCont, Address) } ; % If the newly uncovered cont is unknown then % we must have created a new frame before the @@ -1977,63 +2007,154 @@ code_info__do_soft_cut(TheFrame, Code) --> %---------------------------------------------------------------------------% -:- pred code_info__generate_failure_cont(failure_map, - code_tree, code_info, code_info). + % Find the first label that will be generated for the + % given failure continuation based on the scheme used by + % code_info__generate_failure_cont. + +:- pred code_info__find_first_resume_label(failure_cont, code_addr). +:- mode code_info__find_first_resume_label(in, out) is det. + +code_info__find_first_resume_label(FailureCont, Address) :- + FailureCont = failure_cont(_, MaybeRedoLabel, FailMap), + ( + MaybeRedoLabel = yes(RedoLabel) + -> + Address = label(RedoLabel) + ; + code_info__pick_first_resume_point(FailMap, _, Address) + ). + +%---------------------------------------------------------------------------% + + % In establishing this resumption point, we may have pushed + % a temporary frame onto the nondet stack. If we have done so, + % we will have recorded this fact by setting the second argument + % of the top failure continuation to yes, with the argument of + % the yes giving the name of the label whose address was put + % into the redoip slot of that frame. + % + % When control arrives at that label, curfr will point to the + % temporary frame. However, the variables needed by the code + % at the resumption point are in the main nondet frame of the + % procedure. Since this was the current frame when we created + % the temporary frame, we can find it by following the succfr + % link in the temporary frame. + % + % The code we generate in general is + % + % label(RedoLabel) + % + % label(StackLabel) + % + % + % label(OrigLabel) + % + % + % If, in establishing this resumption point, we did not create + % a temporary frame, then curfr will be OK when code to the right + % does a fail(), and hence the first label and the resetting of + % the curfr register can be omitted. + % + % Failures at different points may cause control to arrive at + % the resumption point via any one of these each labels. + % The last line above is necessary since it may arrive at OrigLabel + % without going through StackLabel first. + % + % The first two lines above are generated in this predicate; + % the others are generated in code_info__generate_resume_setup. + % It may not generate some of these other lines if it knows that + % they won't be needed. + +:- pred code_info__generate_failure_cont(failure_cont, code_tree, + code_info, code_info). :- mode code_info__generate_failure_cont(in, out, in, out) is det. -code_info__generate_failure_cont(FailMap, Code) --> - code_info__failure_cont(FailureCont), - { FailureCont = failure_cont(_CurrentCont, MaybeRedo, _FailureMap) }, +code_info__generate_failure_cont(FailureCont, Code) --> + { FailureCont = failure_cont(_CurrentCont, MaybeRedo, ResumeMap) }, - % did we create a temp nondet frame? - % if so, we need to create a redo() continuation, - % which restores curfr before continuing - ( - { MaybeRedo = yes(RedoLab) } + % Did we create a temp nondet frame for this continuation? + % If not, then curfr will be right when we arrive here. + % If yes, it will be wrong, pointing to the temp frame, + % so we must restore curfr before continuing. + { + MaybeRedo = yes(RedoLabel) -> - { FixCurFrCode = - node([ - label(RedoLab) - "redo entry point", - assign(curfr, lval(succfr(lval(maxfr)))) - - "restore curfr" - ]) - } + FixCurFrCode = node([ + label(RedoLabel) - + "redo entry point", + assign(curfr, lval(succfr(lval(maxfr)))) - + "restore curfr" + ]), + ( + ( ResumeMap = orig_only(_, _) + ; ResumeMap = orig_and_stack(_, _, _, _) + ) + -> + error("redo entry before an orig resume point") + ; + true + ) ; - { FixCurFrCode = empty } - ), - code_info__generate_failure_continuation(FailMap, Code0), - { Code = tree(FixCurFrCode, Code0) }. + FixCurFrCode = empty + }, + code_info__generate_resume_setup(ResumeMap, ResumeCode), + { Code = tree(FixCurFrCode, ResumeCode) }. -:- pred code_info__generate_failure_continuation(failure_map, code_tree, +:- pred code_info__generate_resume_setup(resume_maps, code_tree, code_info, code_info). -:- mode code_info__generate_failure_continuation(in, out, in, out) is det. +:- mode code_info__generate_resume_setup(in, out, in, out) is det. -code_info__generate_failure_continuation(Cs, Code) --> +code_info__generate_resume_setup(ResumeMaps, Code) --> ( - { Cs = [] }, - { error("code_info__generate_failure_continuation: |map| = 0") } - ; - { Cs = [C1] }, - { C1 = Map1 - CodeAddr1 }, - { extract_label_from_code_addr(CodeAddr1, Label1) }, - { Code = node([label(Label1) - "Failure Continuation" ]) }, + { ResumeMaps = orig_only(Map1, Addr1) }, + { extract_label_from_code_addr(Addr1, Label1) }, + { Code = node([ + label(Label1) - + "orig only failure continuation" + ]) }, code_info__set_var_locations(Map1) ; - { Cs = [C1, C2] }, - { C1 = Map1 - CodeAddr1 }, - { C2 = Map2 - CodeAddr2 }, - { extract_label_from_code_addr(CodeAddr1, Label1) }, - { extract_label_from_code_addr(CodeAddr2, Label2) }, - { Entry = node([label(Label2) - "fail continuation entry" ]) }, - code_info__set_var_locations(Map2), - { map__to_assoc_list(Map1, AssocList1) }, - code_info__place_resume_vars(AssocList1, PlaceCode), - { Exit = node([label(Label1) - "fail continuation exit" ]) }, - code_info__set_var_locations(Map1), - { Code = tree(Entry, tree(PlaceCode, Exit)) } + { ResumeMaps = stack_only(Map1, Addr1) }, + { extract_label_from_code_addr(Addr1, Label1) }, + { Code = node([ + label(Label1) - + "stack only failure continuation" + ]) }, + code_info__set_var_locations(Map1) ; - { Cs = [_, _, _ | _] }, - { error("code_info__generate_failure_continuation: |map| > 2") } + { ResumeMaps = stack_and_orig(Map1, Addr1, Map2, Addr2) }, + { extract_label_from_code_addr(Addr1, Label1) }, + { extract_label_from_code_addr(Addr2, Label2) }, + { Label1Code = node([ + label(Label1) - + "stack failure continuation before orig" + ]) }, + code_info__set_var_locations(Map1), + { map__to_assoc_list(Map2, AssocList2) }, + code_info__place_resume_vars(AssocList2, PlaceCode), + { Label2Code = node([ + label(Label2) - + "orig failure continuation after stack" + ]) }, + code_info__set_var_locations(Map2), + { Code = tree(Label1Code, tree(PlaceCode, Label2Code)) } + ; + { ResumeMaps = orig_and_stack(Map1, Addr1, Map2, Addr2) }, + { extract_label_from_code_addr(Addr1, Label1) }, + { extract_label_from_code_addr(Addr2, Label2) }, + { Label1Code = node([ + label(Label1) - + "orig failure continuation before stack" + ]) }, + code_info__set_var_locations(Map1), + { map__to_assoc_list(Map2, AssocList2) }, + code_info__place_resume_vars(AssocList2, PlaceCode), + { Label2Code = node([ + label(Label2) - + "stack failure continuation after orig" + ]) }, + code_info__set_var_locations(Map2), + { Code = tree(Label1Code, tree(PlaceCode, Label2Code)) } ). :- pred extract_label_from_code_addr(code_addr, label). @@ -2043,7 +2164,7 @@ extract_label_from_code_addr(CodeAddr, Label) :- ( CodeAddr = label(Label0) -> Label = Label0 ; - error("code_info__generate_failure_continuation: non-label!") + error("code_info__generate_resume_setup: non-label!") ). :- pred code_info__place_resume_vars(assoc_list(var, set(rval)), code_tree, @@ -2073,6 +2194,20 @@ code_info__place_resume_var(Var, [Target | Targets], Code) --> %---------------------------------------------------------------------------% +code_info__may_use_nondet_tailcall(MayTailCall) --> + code_info__top_failure_cont(FailureCont), + { FailureCont = failure_cont(IsKnown, _, FailureMap) }, + ( + { IsKnown = known(_) }, + { FailureMap = stack_only(_, do_fail) } + -> + { MayTailCall = yes } + ; + { MayTailCall = no } + ). + +%---------------------------------------------------------------------------% + :- pred code_info__lval_member(lval, list(rval)). :- mode code_info__lval_member(out, in) is semidet. @@ -2087,6 +2222,10 @@ code_info__lval_member(Lval, [X | Xs]) :- %---------------------------------------------------------------------------% + % Reset the the code generator's database of what is where. + % Remember that the variables in the map are available in their + % associated rvals; forget about all other variables. + :- pred code_info__set_var_locations(map(var, set(rval)), code_info, code_info). :- mode code_info__set_var_locations(in, in, out) is det. @@ -2142,7 +2281,7 @@ code_info__generate_stack_livevals_2([V | Vs], Vals0, Vals) --> { set__insert(Vals0, Slot, Vals1) }, code_info__generate_stack_livevals_2(Vs, Vals1, Vals). -:- pred code_info__generate_stack_livevals_3(stack(pair(lval, lval_or_ticket)), +:- pred code_info__generate_stack_livevals_3(stack(pair(lval, lval_or_ticket)), set(lval), set(lval)). :- mode code_info__generate_stack_livevals_3(in, in, out) is det. @@ -2171,18 +2310,18 @@ code_info__generate_stack_livelvals(Args, LiveVals) --> { set__list_to_set(LiveVars, Vars0) }, { set__difference(Vars0, Args, Vars) }, { set__to_sorted_list(Vars, VarList) }, - { set__init(LiveVals0) }, - code_info__generate_stack_livelvals_2(VarList, LiveVals0, LiveVals1), + { set__init(LiveVals0) }, + code_info__generate_stack_livelvals_2(VarList, LiveVals0, LiveVals1), { set__to_sorted_list(LiveVals1, LiveVals2) }, code_info__get_globals(Globals), { globals__get_gc_method(Globals, GC_Method) }, code_info__livevals_to_livelvals(LiveVals2, LiveVals3, GC_Method), - code_info__get_pushed_values(Pushed0), + code_info__get_pushed_values(Pushed0), code_info__get_commit_vals(CommitVals), { stack__push_list(Pushed0, CommitVals, Pushed) }, - { code_info__generate_stack_livelvals_3(Pushed, LiveVals3, LiveVals) }. + { code_info__generate_stack_livelvals_3(Pushed, LiveVals3, LiveVals) }. -:- pred code_info__generate_stack_livelvals_2(list(var), +:- pred code_info__generate_stack_livelvals_2(list(var), set(pair(lval, var)), set(pair(lval, var)), code_info, code_info). @@ -2215,11 +2354,11 @@ code_info__generate_stack_livelvals_3(Stack0, LiveInfo0, LiveInfo) :- code_info__get_shape_num(lval(succip), succip). code_info__get_shape_num(lval(hp), hp). code_info__get_shape_num(lval(maxfr), maxfr). -code_info__get_shape_num(lval(curfr), curfr). -code_info__get_shape_num(lval(succfr(_)), succfr). -code_info__get_shape_num(lval(prevfr(_)), prevfr). -code_info__get_shape_num(lval(redoip(_)), redoip). -code_info__get_shape_num(lval(succip(_)), succip). +code_info__get_shape_num(lval(curfr), curfr). +code_info__get_shape_num(lval(succfr(_)), succfr). +code_info__get_shape_num(lval(prevfr(_)), prevfr). +code_info__get_shape_num(lval(redoip(_)), redoip). +code_info__get_shape_num(lval(succip(_)), succip). code_info__get_shape_num(lval(sp), sp). code_info__get_shape_num(lval(lvar(_)), unwanted). code_info__get_shape_num(lval(field(_, _, _)), unwanted). @@ -2234,9 +2373,9 @@ code_info__get_shape_num(ticket, ticket). :- mode code_info__livevals_to_livelvals(in, out, in, in, out) is det. code_info__livevals_to_livelvals([], [], _GC_Method, C, C). -code_info__livevals_to_livelvals([L - V | Ls], +code_info__livevals_to_livelvals([L - V | Ls], [live_lvalue(L, num(S_Num), TypeParams) | Lives], GC_Method) --> - ( + ( { GC_Method = accurate } -> code_info__get_module_info(ModuleInfo), @@ -2246,7 +2385,7 @@ code_info__livevals_to_livelvals([L - V | Ls], % XXX We don't yet support partial insts when allocating % XXX shapes, so pass ground(shared, no) as a placeholder. - { shapes__request_shape_number(Type - ground(shared, no), + { shapes__request_shape_number(Type - ground(shared, no), Type_Table, S_Tab0, S_Tab1, S_Num) }, { type_util__vars(Type, TypeVars) }, ( @@ -2312,14 +2451,20 @@ code_info__variable_type(Var, Type) --> :- pred code_info__set_max_push_count(int, code_info, code_info). :- mode code_info__set_max_push_count(in, in, out) is det. -:- pred code_info__get_pushed_values(stack(pair(lval, lval_or_ticket)), +:- pred code_info__get_pushed_values(stack(pair(lval, lval_or_ticket)), code_info, code_info). :- mode code_info__get_pushed_values(out, in, out) is det. -:- pred code_info__set_pushed_values(stack(pair(lval, lval_or_ticket)), +:- pred code_info__set_pushed_values(stack(pair(lval, lval_or_ticket)), code_info, code_info). :- mode code_info__set_pushed_values(in, in, out) is det. +:- pred code_info__get_zombies(set(var), code_info, code_info). +:- mode code_info__get_zombies(out, in, out) is det. + +:- pred code_info__set_zombies(set(var), code_info, code_info). +:- mode code_info__set_zombies(in, in, out) is det. + :- pred code_info__get_resume_point_stack(stack(set(var)), code_info, code_info). :- mode code_info__get_resume_point_stack(out, in, out) is det. @@ -2328,101 +2473,105 @@ code_info__variable_type(Var, Type) --> code_info, code_info). :- mode code_info__set_resume_point_stack(in, in, out) is det. -:- pred code_info__get_commit_vals(list(pair(lval, lval_or_ticket)), +:- pred code_info__get_commit_vals(list(pair(lval, lval_or_ticket)), code_info, code_info). :- mode code_info__get_commit_vals(out, in, out) is det. -:- pred code_info__set_commit_vals(list(pair(lval, lval_or_ticket)), +:- pred code_info__set_commit_vals(list(pair(lval, lval_or_ticket)), code_info, code_info). :- mode code_info__set_commit_vals(in, in, out) is det. code_info__get_stackslot_count(A, CI, CI) :- CI = code_info(A, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, - _, _, _). + _, _, _, _). code_info__get_label_count(B, CI, CI) :- CI = code_info(_, B, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, - _, _, _). + _, _, _, _). code_info__get_varset(C, CI, CI) :- CI = code_info(_, _, C, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, - _, _, _). + _, _, _, _). code_info__get_stack_slots(D, CI, CI) :- CI = code_info(_, _, _, D, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, - _, _, _). + _, _, _, _). code_info__get_pred_id(E, CI, CI) :- CI = code_info(_, _, _, _, E, _, _, _, _, _, _, _, _, _, _, _, _, _, _, - _, _, _). + _, _, _, _). code_info__get_proc_id(F, CI, CI) :- CI = code_info(_, _, _, _, _, F, _, _, _, _, _, _, _, _, _, _, _, _, _, - _, _, _). + _, _, _, _). code_info__get_cell_count(G, CI, CI) :- CI = code_info(_, _, _, _, _, _, G, _, _, _, _, _, _, _, _, _, _, _, _, - _, _, _). + _, _, _, _). code_info__get_exprn_info(H, CI, CI) :- CI = code_info(_, _, _, _, _, _, _, H, _, _, _, _, _, _, _, _, _, _, _, - _, _, _). + _, _, _, _). code_info__get_proc_info(I, CI, CI) :- CI = code_info(_, _, _, _, _, _, _, _, I, _, _, _, _, _, _, _, _, _, _, - _, _, _). + _, _, _, _). code_info__get_succip_used(J, CI, CI) :- CI = code_info(_, _, _, _, _, _, _, _, _, J, _, _, _, _, _, _, _, _, _, - _, _, _). + _, _, _, _). code_info__get_fall_through(K, CI, CI) :- CI = code_info(_, _, _, _, _, _, _, _, _, _, K, _, _, _, _, _, _, _, _, - _, _, _). + _, _, _, _). code_info__get_module_info(L, CI, CI) :- CI = code_info(_, _, _, _, _, _, _, _, _, _, _, L, _, _, _, _, _, _, _, - _, _, _). + _, _, _, _). code_info__get_liveness_info(M, CI, CI) :- CI = code_info(_, _, _, _, _, _, _, _, _, _, _, _, M, _, _, _, _, _, _, - _, _, _). + _, _, _, _). code_info__get_instmap(N, CI, CI) :- CI = code_info(_, _, _, _, _, _, _, _, _, _, _, _, _, N, _, _, _, _, _, - _, _, _). + _, _, _, _). code_info__get_push_count(O, CI, CI) :- CI = code_info(_, _, _, _, _, _, _, _, _, _, _, _, _, _, O, _, _, _, _, - _, _, _). + _, _, _, _). code_info__get_max_push_count(P, CI, CI) :- CI = code_info(_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, P, _, _, _, - _, _, _). + _, _, _, _). code_info__get_globals(Q, CI, CI) :- CI = code_info(_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, Q, _, _, - _, _, _). + _, _, _, _). code_info__get_pushed_values(R, CI, CI) :- CI = code_info(_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, R, _, - _, _, _). + _, _, _, _). code_info__get_shapes(S, CI, CI) :- CI = code_info(_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, S, - _, _, _). + _, _, _, _). code_info__get_nondet_lives(T, CI, CI) :- CI = code_info(_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, - T, _, _). + T, _, _, _). -code_info__get_resume_point_stack(U, CI, CI) :- +code_info__get_zombies(U, CI, CI) :- CI = code_info(_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, - _, U, _). + _, U, _, _). -code_info__get_commit_vals(V, CI, CI) :- +code_info__get_resume_point_stack(V, CI, CI) :- CI = code_info(_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, - _, _, V). + _, _, V, _). + +code_info__get_commit_vals(W, CI, CI) :- + CI = code_info(_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, + _, _, _, W). % :- type code_info ---> % code_info( @@ -2464,7 +2613,10 @@ code_info__get_commit_vals(V, CI, CI) :- % T set(var), % Variables that are not quite live % % but are only nondet-live (so that % % we make sure we save them). -% U stack(set(var)), +% U set(var), % Zombie variables; variables that have +% % been killed but are protected by a +% % resume point. +% V stack(set(var)), % % Each resumption point has an % % associated set of variables % % whose values may be needed on @@ -2476,7 +2628,7 @@ code_info__get_commit_vals(V, CI, CI) :- % % When a variable included in the top % % set becomes no longer forward live, % % we must save its value to the stack. -% V list(pair(lval, lval_or_ticket)) +% W list(pair(lval, lval_or_ticket)) % % A list of lvalues (ie curfr, maxfr % % and redoip) that get saved onto the % % det stack even though the current @@ -2489,57 +2641,57 @@ code_info__get_commit_vals(V, CI, CI) :- code_info__set_label_count(B, CI0, CI) :- CI0 = code_info(A, _, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, - R, S, T, U, V), + R, S, T, U, V, W), CI = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, - R, S, T, U, V). + R, S, T, U, V, W). code_info__set_varset(C, CI0, CI) :- CI0 = code_info(A, B, _, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, - R, S, T, U, V), + R, S, T, U, V, W), CI = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, - R, S, T, U, V). + R, S, T, U, V, W). code_info__set_stack_slots(D, CI0, CI) :- CI0 = code_info(A, B, C, _, E, F, G, H, I, J, K, L, M, N, O, P, Q, - R, S, T, U, V), + R, S, T, U, V, W), CI = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, - R, S, T, U, V). + R, S, T, U, V, W). code_info__set_pred_id(E, CI0, CI) :- CI0 = code_info(A, B, C, D, _, F, G, H, I, J, K, L, M, N, O, P, Q, - R, S, T, U, V), + R, S, T, U, V, W), CI = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, - R, S, T, U, V). + R, S, T, U, V, W). code_info__set_proc_id(F, CI0, CI) :- CI0 = code_info(A, B, C, D, E, _, G, H, I, J, K, L, M, N, O, P, Q, - R, S, T, U, V), + R, S, T, U, V, W), CI = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, - R, S, T, U, V). + R, S, T, U, V, W). code_info__set_cell_count(G, CI0, CI) :- CI0 = code_info(A, B, C, D, E, F, _, H, I, J, K, L, M, N, O, P, Q, - R, S, T, U, V), + R, S, T, U, V, W), CI = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, - R, S, T, U, V). + R, S, T, U, V, W). code_info__set_exprn_info(H, CI0, CI) :- CI0 = code_info(A, B, C, D, E, F, G, _, I, J, K, L, M, N, O, P, Q, - R, S, T, U, V), + R, S, T, U, V, W), CI = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, - R, S, T, U, V). + R, S, T, U, V, W). code_info__set_succip_used(J, CI0, CI) :- CI0 = code_info(A, B, C, D, E, F, G, H, I, _, K, L, M, N, O, P, Q, - R, S, T, U, V), + R, S, T, U, V, W), CI = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, - R, S, T, U, V). + R, S, T, U, V, W). code_info__set_fall_through(K, CI0, CI) :- CI0 = code_info(A, B, C, D, E, F, G, H, I, J, _, L, M, N, O, P, Q, - R, S, T, U, V), + R, S, T, U, V, W), CI = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, - R, S, T, U, V). + R, S, T, U, V, W). % It is a bad idea to allow the code generator to modify module_info % in arbitrary ways. @@ -2547,64 +2699,70 @@ code_info__set_fall_through(K, CI0, CI) :- code_info__set_liveness_info(M, CI0, CI) :- CI0 = code_info(A, B, C, D, E, F, G, H, I, J, K, L, _, N, O, P, Q, - R, S, T, U, V), + R, S, T, U, V, W), CI = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, - R, S, T, U, V). + R, S, T, U, V, W). code_info__set_instmap(N, CI0, CI) :- CI0 = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, _, O, P, Q, - R, S, T, U, V), + R, S, T, U, V, W), CI = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, - R, S, T, U, V). + R, S, T, U, V, W). code_info__set_push_count(O, CI0, CI) :- CI0 = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, N, _, P0, Q, - R, S, T, U, V), + R, S, T, U, V, W), int__max(P0, O, P), CI = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, - R, S, T, U, V). + R, S, T, U, V, W). code_info__set_max_push_count(P, CI0, CI) :- CI0 = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, _, Q, - R, S, T, U, V), + R, S, T, U, V, W), CI = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, - R, S, T, U, V). + R, S, T, U, V, W). code_info__set_globals(Q, CI0, CI) :- CI0 = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, _, - R, S, T, U, V), + R, S, T, U, V, W), CI = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, - R, S, T, U, V). + R, S, T, U, V, W). code_info__set_pushed_values(R, CI0, CI) :- CI0 = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, - _, S, T, U, V), + _, S, T, U, V, W), CI = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, - R, S, T, U, V). + R, S, T, U, V, W). code_info__set_shapes(S, CI0, CI) :- CI0 = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, - R, _, T, U, V), + R, _, T, U, V, W), CI = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, - R, S, T, U, V). + R, S, T, U, V, W). code_info__set_nondet_lives(T, CI0, CI) :- CI0 = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, - R, S, _, U, V), + R, S, _, U, V, W), CI = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, - R, S, T, U, V). + R, S, T, U, V, W). -code_info__set_resume_point_stack(U, CI0, CI) :- +code_info__set_zombies(U, CI0, CI) :- CI0 = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, - R, S, T, _, V), + R, S, T, _, V, W), CI = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, - R, S, T, U, V). + R, S, T, U, V, W). -code_info__set_commit_vals(V, CI0, CI) :- +code_info__set_resume_point_stack(V, CI0, CI) :- CI0 = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, - R, S, T, U, _), + R, S, T, U, _, W), CI = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, - R, S, T, U, V). + R, S, T, U, V, W). + +code_info__set_commit_vals(W, CI0, CI) :- + CI0 = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, + R, S, T, U, V, _), + CI = code_info(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, + R, S, T, U, V, W). %---------------------------------------------------------------------------% @@ -2666,7 +2824,7 @@ code_info__variable_to_string(Var, Name) --> %---------------------------------------------------------------------------% code_info__find_type_infos([], []) --> []. -code_info__find_type_infos([TVar | TVars], [Lval | Lvals]) --> +code_info__find_type_infos([TVar | TVars], [Lval | Lvals]) --> code_info__get_proc_info(ProcInfo), { proc_info_typeinfo_varmap(ProcInfo, TypeInfoMap) }, ( @@ -2677,7 +2835,7 @@ code_info__find_type_infos([TVar | TVars], [Lval | Lvals]) --> { error("cannot find var for type variable") } ), { proc_info_stack_slots(ProcInfo, StackSlots) }, - ( + ( { map__search(StackSlots, Var, Lval0) } -> { Lval = Lval0 } diff --git a/compiler/code_util.m b/compiler/code_util.m index 8d6400bb4..98f50d203 100644 --- a/compiler/code_util.m +++ b/compiler/code_util.m @@ -117,6 +117,24 @@ :- pred code_util__cons_id_to_tag(cons_id, type, module_info, cons_tag). :- mode code_util__cons_id_to_tag(in, in, in, out) is det. + % Succeed if the given goal cannot encounter a context + % that causes any variable to be flushed to its stack slot. + % If such a goal needs a resume point, and that resume point cannot + % be backtracked to once control leaves the goal, then the only entry + % point we need for the resume point is the one with the resume + % variables in their original locations. + +:- pred code_util__cannot_stack_flush(hlds__goal). +:- mode code_util__cannot_stack_flush(in) is semidet. + + % Succeed if the given goal cannot fail before encountering a context + % that forces all variables to be flushed to their stack slots. + % If such a goal needs a resume point, the only entry point we need + % is the stack entry point. + +:- pred code_util__cannot_fail_before_stack_flush(hlds__goal). +:- mode code_util__cannot_fail_before_stack_flush(in) is semidet. + %---------------------------------------------------------------------------% :- implementation. @@ -654,3 +672,75 @@ code_util__cons_id_to_tag(cons(unqualified(Name), Arity), ). %-----------------------------------------------------------------------------% + +code_util__cannot_stack_flush(GoalExpr - _) :- + code_util__cannot_stack_flush_2(GoalExpr). + +:- pred code_util__cannot_stack_flush_2(hlds__goal_expr). +:- mode code_util__cannot_stack_flush_2(in) is semidet. + +code_util__cannot_stack_flush_2(unify(_, _, _, Unify, _)) :- + Unify \= complicated_unify(_, _). +code_util__cannot_stack_flush_2(call(_, _, _, IsBuiltin, _, _)) :- + hlds__is_builtin_is_internal(IsBuiltin), + hlds__is_builtin_is_inline(IsBuiltin). +code_util__cannot_stack_flush_2(conj(Goals)) :- + code_util__cannot_stack_flush_goals(Goals). +code_util__cannot_stack_flush_2(switch(_, _, Cases, _)) :- + code_util__cannot_stack_flush_cases(Cases). + +:- pred code_util__cannot_stack_flush_goals(list(hlds__goal)). +:- mode code_util__cannot_stack_flush_goals(in) is semidet. + +code_util__cannot_stack_flush_goals([]). +code_util__cannot_stack_flush_goals([Goal | Goals]) :- + code_util__cannot_stack_flush(Goal), + code_util__cannot_stack_flush_goals(Goals). + +:- pred code_util__cannot_stack_flush_cases(list(case)). +:- mode code_util__cannot_stack_flush_cases(in) is semidet. + +code_util__cannot_stack_flush_cases([]). +code_util__cannot_stack_flush_cases([case(_, Goal) | Cases]) :- + code_util__cannot_stack_flush(Goal), + code_util__cannot_stack_flush_cases(Cases). + +%-----------------------------------------------------------------------------% + +code_util__cannot_fail_before_stack_flush(GoalExpr - GoalInfo) :- + goal_info_get_determinism(GoalInfo, Detism), + determinism_components(Detism, CanFail, _), + ( CanFail = cannot_fail -> + true + ; + code_util__cannot_fail_before_stack_flush_2(GoalExpr) + ). + +:- pred code_util__cannot_fail_before_stack_flush_2(hlds__goal_expr). +:- mode code_util__cannot_fail_before_stack_flush_2(in) is semidet. + +code_util__cannot_fail_before_stack_flush_2(conj(Goals)) :- + code_util__cannot_fail_before_stack_flush_conj(Goals). + +:- pred code_util__cannot_fail_before_stack_flush_conj(list(hlds__goal)). +:- mode code_util__cannot_fail_before_stack_flush_conj(in) is semidet. + +code_util__cannot_fail_before_stack_flush_conj([]). +code_util__cannot_fail_before_stack_flush_conj([Goal | Goals]) :- + Goal = GoalExpr - GoalInfo, + ( + ( GoalExpr = call(_, _, _, _, _, _) + ; GoalExpr = higher_order_call(_, _, _, _, _) + ) + -> + true + ; + goal_info_get_determinism(GoalInfo, Detism), + determinism_components(Detism, cannot_fail, _) + -> + code_util__cannot_fail_before_stack_flush_conj(Goals) + ; + fail + ). + +%-----------------------------------------------------------------------------% diff --git a/compiler/dense_switch.m b/compiler/dense_switch.m index 31186f19f..d4a87cb19 100644 --- a/compiler/dense_switch.m +++ b/compiler/dense_switch.m @@ -172,9 +172,8 @@ dense_switch__generate(Cases, StartVal, EndVal, Var, CodeModel, CanFail, computed_goto(Index, Labels) - "switch (using dense jump table)" ]) }, - % Assemble to code together - { Code = tree(tree(VarCode, RangeCheck), tree(DoJump, CasesCode)) }, - code_info__remake_with_store_map(StoreMap). + % Assemble the code together + { Code = tree(VarCode, tree(RangeCheck, tree(DoJump, CasesCode))) }. :- pred dense_switch__generate_cases(cases_list, int, int, code_model, store_map, label, list(label), code_tree, @@ -230,12 +229,13 @@ dense_switch__generate_case(Cases0, NextVal, CodeModel, StoreMap, Cases, % and restore them when we've finished { Comment = "case of dense switch" }, code_info__grab_code_info(CodeInfo), - code_gen__generate_forced_goal(CodeModel, Goal, StoreMap, - Code), + code_gen__generate_goal(CodeModel, Goal, GoalCode), + code_info__generate_branch_end(CodeModel, StoreMap, SaveCode), + { Code = tree(GoalCode, SaveCode) }, code_info__get_liveness_info(L), code_info__slap_code_info(CodeInfo), - { ML = yes(L) }, - { Cases = Cases1 } + { Cases = Cases1 }, + { ML = yes(L) } ; % This case didn't occur in the original case list - just % generate a `fail' for it. diff --git a/compiler/disj_gen.m b/compiler/disj_gen.m index a47e0f8b4..063eb852f 100644 --- a/compiler/disj_gen.m +++ b/compiler/disj_gen.m @@ -6,7 +6,12 @@ % % File: disj_gen.m: % -% Generate code for disjunctions. +% Main authors: conway, zs. +% +% The predicates of this module generate code for disjunctions. +% +% The handling of model_det and model_semi disjunctions is almost identical. +% The handling of model_non disjunctions is also quite similar. % %---------------------------------------------------------------------------% %---------------------------------------------------------------------------% @@ -39,158 +44,29 @@ %---------------------------------------------------------------------------% disj_gen__generate_det_disj(Goals, StoreMap, Code) --> - % If we are using constraints, save the current solver state - % before the first disjunct. - code_info__get_globals(Globals), - { globals__lookup_bool_option(Globals, - constraints, SaveTicket) }, - code_info__maybe_save_ticket(SaveTicket, SaveTicketCode), - - % Rather than saving the heap pointer here, - % we delay saving it until we get to the first - % disjunct that might allocate some heap space. - { SavedHP = no }, % we haven't yet saved the heap pointer - { MustRestoreHP = no }, % we won't need to restore yet - - % Generate all the cases - code_info__get_next_label(EndLabel), - disj_gen__generate_det_disj_2(Goals, StoreMap, EndLabel, - SavedHP, MustRestoreHP, GoalsCode), - { Code = tree(SaveTicketCode, GoalsCode) }. - -:- pred disj_gen__generate_det_disj_2(list(hlds__goal), store_map, - label, bool, bool, code_tree, code_info, code_info). -:- mode disj_gen__generate_det_disj_2(in, in, in, in, in, out, in, out) is det. - - % To generate code for a det disjunction, we generate a - % chain (if-then-else style) of goals until we come to - % one that cannot fail. When we get to a goal that can't - % fail, we just generate that goal. -disj_gen__generate_det_disj_2([], _, _, _, _, _) --> - { error("Empty det disj!") }. -disj_gen__generate_det_disj_2([Goal | Goals], StoreMap, EndLabel, - SavedHP, MustRestoreHP, Code) --> - { Goal = _ - GoalInfo }, - { goal_info_get_determinism(GoalInfo, GoalDet) }, - { goal_info_get_code_model(GoalInfo, GoalModel) }, - { determinism_components(GoalDet, CanFail, _) }, - code_info__get_globals(Globals), - { globals__lookup_bool_option(Globals, - constraints, RestoreTicket) }, - ( - { CanFail = cannot_fail } - -> - % If this disjunct can't fail, we ignore - % the remaining disjuncts, and treat this - % disjunct as the last disjunct. - - % Restore the heap pointer if necessary, - % and pop the temp stack that we saved it on - % if we saved it - code_info__maybe_get_old_hp(MustRestoreHP, RestoreHPCode), - code_info__maybe_pop_stack(SavedHP, UnSaveHPCode), - - % Restore the solver state if necessary - code_info__maybe_restore_ticket_and_pop(RestoreTicket, - RestoreTicketCode), - - % Generate the goal - code_gen__generate_forced_goal(GoalModel, Goal, StoreMap, - GoalCode), - - { EndCode = node([label(EndLabel) - "end of det disj"]) }, - { Code = tree(RestoreHPCode, - tree(UnSaveHPCode, - tree(RestoreTicketCode, - tree(GoalCode, - EndCode)))) }, - code_info__remake_with_store_map(StoreMap) - ; - code_info__get_live_variables(VarList), - { set__list_to_set(VarList, Vars) }, - code_info__make_known_failure_cont(Vars, no, ModContCode), - - % Reset the heap pointer to recover memory allocated - % by the previous disjunct, if necessary - code_info__maybe_get_old_hp(MustRestoreHP, RestoreHPCode), - - % If this disjunct might allocate heap space, then - % we must restore the HP on entry to the next one. - { globals__lookup_bool_option(Globals, - reclaim_heap_on_semidet_failure, ReclaimHeap) }, - { ReclaimHeap = yes, code_util__goal_may_allocate_heap(Goal) -> - MustRestoreHP_Next = yes - ; - MustRestoreHP_Next = no - }, - - % If we are going to need to restore the HP, - % and we haven't saved it already, then we must - % save it now. - ( { MustRestoreHP_Next = yes, SavedHP = no } -> - code_info__save_hp(SaveHPCode), - { SavedHP_Next = yes } - ; - { SaveHPCode = empty }, - { SavedHP_Next = SavedHP } - ), - - code_info__maybe_restore_ticket(RestoreTicket, - RestoreTicketCode), - - code_info__grab_code_info(CodeInfo), - - % generate the case as a semi-deterministic goal - code_gen__generate_forced_goal(GoalModel, Goal, StoreMap, - GoalCode), - { BranchCode = node([goto(label(EndLabel)) - - "skip to end of det disj"]) }, - { ThisCode = tree(GoalCode, BranchCode) }, - - % If there are more cases, then we need to restore - % the machine state, and clear registers, since - % we need to use the saved input vars. - code_info__slap_code_info(CodeInfo), - code_info__restore_failure_cont(RestoreContCode), - ( - { Goals \= [] } - -> - disj_gen__generate_det_disj_2(Goals, StoreMap, - EndLabel, SavedHP_Next, MustRestoreHP_Next, - RestCode) - ; - % a det disj should have at least one det disjunct - { error("disj_gen__generate_det_disj: huh?") } - ), - { Code = tree(ModContCode, - tree(RestoreHPCode, - tree(SaveHPCode, - tree(RestoreTicketCode, - tree(ThisCode, - tree(RestoreContCode, - RestCode)))))) } - ). - -%---------------------------------------------------------------------------% + disj_gen__generate_pruned_disj(Goals, StoreMap, Code). disj_gen__generate_semi_disj(Goals, StoreMap, Code) --> ( { Goals = [] } -> code_info__generate_failure(Code) ; - disj_gen__generate_semi_disj_2(Goals, StoreMap, Code) + disj_gen__generate_pruned_disj(Goals, StoreMap, Code) ). -:- pred disj_gen__generate_semi_disj_2(list(hlds__goal), store_map, - code_tree, code_info, code_info). -:- mode disj_gen__generate_semi_disj_2(in, in, out, in, out) is det. +%---------------------------------------------------------------------------% -disj_gen__generate_semi_disj_2(Goals, StoreMap, Code) --> +:- pred disj_gen__generate_pruned_disj(list(hlds__goal), store_map, + code_tree, code_info, code_info). +:- mode disj_gen__generate_pruned_disj(in, in, out, in, out) is det. + +disj_gen__generate_pruned_disj(Goals, StoreMap, Code) --> % If we are using constraints, save the current solver state % before the first disjunct. code_info__get_globals(Globals), - { globals__lookup_bool_option(Globals, - constraints, SaveTicket) }, - code_info__maybe_save_ticket(SaveTicket, SaveTicketCode), + { globals__lookup_bool_option(Globals, reclaim_heap_on_semidet_failure, + ReclaimHeap) }, + { globals__lookup_bool_option(Globals, constraints, Constraints) }, + code_info__maybe_save_ticket(Constraints, SaveTicketCode), % Rather than saving the heap pointer here, % we delay saving it until we get to the first @@ -198,10 +74,10 @@ disj_gen__generate_semi_disj_2(Goals, StoreMap, Code) --> { SavedHP = no }, % we haven't yet saved the heap pointer { MustRestoreHP = no }, % we won't need to restore yet - % Generate all the cases + % Generate all the disjuncts code_info__get_next_label(EndLabel), - disj_gen__generate_semi_cases(Goals, StoreMap, EndLabel, - SavedHP, MustRestoreHP, GoalsCode), + disj_gen__generate_pruned_disjuncts(Goals, StoreMap, EndLabel, no, + SavedHP, MustRestoreHP, ReclaimHeap, Constraints, GoalsCode), % Remake the code_info using the store map for the % variable locations at the end of the disjunction. @@ -209,47 +85,48 @@ disj_gen__generate_semi_disj_2(Goals, StoreMap, Code) --> { Code = tree(SaveTicketCode, GoalsCode) }. -:- pred disj_gen__generate_semi_cases(list(hlds__goal), store_map, label, - bool, bool, code_tree, code_info, code_info). -:- mode disj_gen__generate_semi_cases(in, in, in, in, in, out, in, out) is det. +%---------------------------------------------------------------------------% -disj_gen__generate_semi_cases([], _, _, _, _, _) --> - { error("disj_gen__generate_semi_cases") }. -disj_gen__generate_semi_cases([Goal | Goals], StoreMap, EndLabel, - SavedHP, MustRestoreHP, GoalsCode) --> - code_info__get_globals(Globals), - { globals__lookup_bool_option(Globals, - constraints, RestoreTicket) }, +:- pred disj_gen__generate_pruned_disjuncts(list(hlds__goal), store_map, + label, bool, bool, bool, bool, bool, code_tree, code_info, code_info). +:- mode disj_gen__generate_pruned_disjuncts(in, in, in, in, in, in, in, in, out, + in, out) is det. + + % To generate code for a det or semidet disjunction, + % we generate a chain of goals if-then-else style + % until we come to a goal without a resume point. + % That goal is the last in the chain that we need to + % generate code for. (This is figured out by the liveness pass.) + % + % For a semidet disj, this goal will be semidet, + % and will be followed by no other goal. + % For a det disj, this goal will be det, + % and may be followed by other goals. + % + % XXX We ought not to restore anything in the first disjunct. + +disj_gen__generate_pruned_disjuncts([], _, _, _, _, _, _, _, _) --> + { error("Empty pruned disjunction!") }. +disj_gen__generate_pruned_disjuncts([Goal0 | Goals], StoreMap, EndLabel, + HaveTempFrame0, SavedHP, MustRestoreHP, ReclaimHeap, + Constraints, Code) --> + { Goal0 = GoalExpr0 - GoalInfo0 }, + { goal_info_get_code_model(GoalInfo0, CodeModel) }, + { goal_info_get_resume_point(GoalInfo0, Resume) }, ( - { Goals = [] } + { Resume = resume_point(ResumeVars, ResumeLocs) } -> - % Restore the heap pointer if necessary, - % and pop the temp stack that we saved it on - % if we saved it - code_info__maybe_get_old_hp(MustRestoreHP, RestoreHPCode), - code_info__maybe_pop_stack(SavedHP, UnSaveHPCode), + % Emit code for a non-last disjunct, including setting things + % up for the execution of the next disjunct. - % Restore the solver state if necessary - code_info__maybe_restore_ticket_and_pop(RestoreTicket, - RestoreTicketCode), - - % Generate the case as a semi-deterministic goal - code_gen__generate_forced_goal(model_semi, Goal, StoreMap, - ThisCode), - - { EndCode = node([ - label(EndLabel) - "End of model_semi disj" - ]) }, - - { GoalsCode = tree(RestoreHPCode, - tree(UnSaveHPCode, - tree(RestoreTicketCode, - tree(ThisCode, - EndCode)))) } - ; - code_info__get_live_variables(VarList), - { set__list_to_set(VarList, Vars) }, - code_info__make_known_failure_cont(Vars, no, ModContCode), + code_info__push_resume_point_vars(ResumeVars), + code_info__make_known_failure_cont(ResumeVars, ResumeLocs, no, + HaveTempFrame0, HaveTempFrame, ModContCode), + % The next line is to enable Goal to pass the + % pre_goal_update sanity check + { goal_info_set_resume_point(GoalInfo0, no_resume_point, + GoalInfo) }, + { Goal = GoalExpr0 - GoalInfo }, % Reset the heap pointer to recover memory allocated % by the previous disjunct, if necessary @@ -257,8 +134,6 @@ disj_gen__generate_semi_cases([Goal | Goals], StoreMap, EndLabel, % If this disjunct might allocate heap space, then % we must restore the HP on entry to the next one. - { globals__lookup_bool_option(Globals, - reclaim_heap_on_semidet_failure, ReclaimHeap) }, { ReclaimHeap = yes, code_util__goal_may_allocate_heap(Goal) -> MustRestoreHP_Next = yes ; @@ -277,155 +152,244 @@ disj_gen__generate_semi_cases([Goal | Goals], StoreMap, EndLabel, ), % Reset the solver state if necessary - code_info__maybe_restore_ticket(RestoreTicket, + code_info__maybe_restore_ticket(Constraints, RestoreTicketCode), code_info__grab_code_info(CodeInfo), - % generate the case as a semi-deterministic goal - code_gen__generate_forced_goal(model_semi, Goal, StoreMap, - ThisCode), + % generate the disjunct as a semi-deterministic goal + { CodeModel = model_semi -> + true + ; + error("pruned disj non-last goal is not semidet") + }, + code_gen__generate_goal(CodeModel, Goal, GoalCode), + code_info__generate_branch_end(CodeModel, StoreMap, SaveCode), + % Kill any variables made zombies by the goal + % XXX should not be necessary, since the state + % we set up will be discarded anyway + code_info__pickup_zombies(Zombies), + code_info__make_vars_dead(Zombies), + + { BranchCode = node([ + goto(label(EndLabel)) - + "skip to end of pruned disj" + ]) }, - % If there are more cases, then we need to restore - % the machine state, and clear registers, since - % we need to use the saved input vars. code_info__slap_code_info(CodeInfo), + code_info__pop_resume_point_vars, code_info__restore_failure_cont(RestoreContCode), - % generate the rest of the cases. - disj_gen__generate_semi_cases(Goals, StoreMap, EndLabel, - SavedHP_Next, MustRestoreHP_Next, GoalsCode0), - { SuccCode = node([ - goto(label(EndLabel)) - "Jump to end of model_semi disj" - ]) }, - { GoalsCode = tree(ModContCode, - tree(RestoreHPCode, - tree(SaveHPCode, - tree(RestoreTicketCode, - tree(ThisCode, - tree(SuccCode, - tree(RestoreContCode, - GoalsCode0))))))) } + disj_gen__generate_pruned_disjuncts(Goals, StoreMap, EndLabel, + HaveTempFrame, SavedHP_Next, MustRestoreHP_Next, + ReclaimHeap, Constraints, RestCode), + + { Code = tree(ModContCode, + tree(RestoreHPCode, + tree(SaveHPCode, + tree(RestoreTicketCode, + tree(GoalCode, + tree(SaveCode, + tree(BranchCode, + tree(RestoreContCode, + RestCode)))))))) } + ; + % Emit code for the last disjunct + + % Restore the heap pointer if necessary, + % and pop the temp stack that we saved it on + % if we saved it + code_info__maybe_get_old_hp(MustRestoreHP, RestoreHPCode), + code_info__maybe_pop_stack(SavedHP, UnSaveHPCode), + + % Restore the solver state if necessary + code_info__maybe_restore_ticket_and_pop(Constraints, + RestorePopTicketCode), + + % Generate the goal + code_gen__generate_goal(CodeModel, Goal0, GoalCode), + code_info__generate_branch_end(CodeModel, StoreMap, SaveCode), + % Kill any variables made zombies by the goal + % XXX should not be necessary, since we are not + % coming out from under a restore anyway + code_info__pickup_zombies(Zombies), + code_info__make_vars_dead(Zombies), + + { EndCode = node([ + label(EndLabel) - "End of pruned disj" + ]) }, + { Code = tree(RestoreHPCode, + tree(UnSaveHPCode, + tree(RestorePopTicketCode, + tree(GoalCode, + tree(SaveCode, + EndCode))))) } ). %---------------------------------------------------------------------------% -disj_gen__generate_non_disj(Goals1, StoreMap, Code) --> +disj_gen__generate_non_disj(Goals, StoreMap, Code) --> % Sanity check - { Goals1 = [] -> + { + Goals = [], error("empty disjunction shouldn't be non-det") - ; Goals1 = [_] -> + ; + Goals = [_], error("singleton disjunction") ; - true + Goals = [_, _ | _] }, - code_info__get_live_variables(VarList), - { set__list_to_set(VarList, Vars) }, - code_info__make_known_failure_cont(Vars, yes, HijackCode), - + % If we are using constraints, save the current solver state + % before the first disjunct. code_info__get_globals(Globals), - { globals__lookup_bool_option(Globals, - reclaim_heap_on_nondet_failure, ReclaimHeap) }, + { globals__lookup_bool_option(Globals, constraints, Constraints) }, + code_info__maybe_save_ticket(Constraints, SaveTicketCode), + + % With non-det disjunctions, we must recover memory across + % all disjuncts, since we can backtract to disjunct N + % even after control leaves disjunct N-1. + { globals__lookup_bool_option(Globals, reclaim_heap_on_nondet_failure, + ReclaimHeap) }, code_info__maybe_save_hp(ReclaimHeap, SaveHeapCode), - { globals__lookup_bool_option(Globals, - constraints, SaveTicket) }, - code_info__maybe_save_ticket(SaveTicket, SaveTicketCode), - code_info__get_next_label(EndLab), - disj_gen__generate_non_disj_2(Goals1, StoreMap, EndLab, GoalsCode), - { Code = tree(HijackCode, - tree(SaveHeapCode, - tree(SaveTicketCode, GoalsCode))) }, + + code_info__get_next_label(EndLabel), + disj_gen__generate_non_disjuncts(Goals, StoreMap, EndLabel, no, + ReclaimHeap, Constraints, GoalsCode), % since we don't know which disjunct we have come from % we must set the current failure continuation to unkown. - code_info__unset_failure_cont. -:- pred disj_gen__generate_non_disj_2(list(hlds__goal), store_map, label, - code_tree, code_info, code_info). -:- mode disj_gen__generate_non_disj_2(in, in, in, out, in, out) is det. + code_info__unset_failure_cont(FlushResumeVarsCode), + code_info__remake_with_store_map(StoreMap), + { Code = tree(SaveTicketCode, + tree(SaveHeapCode, + tree(GoalsCode, + FlushResumeVarsCode))) }. -disj_gen__generate_non_disj_2([], _StoreMap, _EndLab, _Code) --> - { error("disj_gen__generate_non_disj_2") }. -disj_gen__generate_non_disj_2([Goal | Goals], StoreMap, EndLab, DisjCode) --> - code_info__get_globals(Globals), - { globals__lookup_bool_option(Globals, - reclaim_heap_on_nondet_failure, ReclaimHeap) }, - { globals__lookup_bool_option(Globals, - constraints, RestoreTicket) }, - code_info__get_live_variables(Vars), - code_gen__ensure_vars_are_saved(Vars, GoalCode0), - code_info__grab_code_info(CodeInfo), - code_gen__generate_forced_goal(model_non, Goal, StoreMap, GoalCode1), - { GoalCode = tree(GoalCode0, GoalCode1) }, - code_info__slap_code_info(CodeInfo), - { SuccCode = - node([ - goto(label(EndLab)) - "Jump to end of disj" - ]) - }, - ( { Goals = [] } -> - { error("disj_gen__generate_non_disj_2 #2") } - ; { Goals = [Goal2] } -> - % Process the last disjunct - code_info__remake_with_stack_slots, - code_info__restore_failure_cont(RestoreAfterFailureCode), - code_info__maybe_get_old_hp(ReclaimHeap, RestoreHeapCode), - code_info__maybe_pop_stack(ReclaimHeap, PopCode), - % restore and pop the solver ticket before - % the final arm of the disjunction - code_info__maybe_restore_ticket_and_pop(RestoreTicket, - RestorePopCode), - code_gen__generate_forced_goal(model_non, Goal2, StoreMap, - Goal2Code), - { EndCode = node([ - label(EndLab) - "End of disj" - ]) }, - { DisjCode = tree(tree(GoalCode, SuccCode), - tree(RestoreAfterFailureCode, - tree(RestoreHeapCode, - tree(PopCode, - tree(RestorePopCode, - tree(Goal2Code, EndCode)))))) } - ; - code_info__remake_with_stack_slots, - code_info__modify_failure_cont(ModifyFailureContCode), - code_info__maybe_get_old_hp(ReclaimHeap, RestoreHeapCode), - code_info__maybe_restore_ticket(RestoreTicket, +%---------------------------------------------------------------------------% + + % XXX We ought not to restore anything in the first disjunct. + % + % XXX We ought to be able to pass information between the calls to + % make_known_failure_cont and restore_failure_cont, in order to + % prevent the repeated construction and discarding of a temporary + % nondet frame. + +:- pred disj_gen__generate_non_disjuncts(list(hlds__goal), store_map, label, + bool, bool, bool, code_tree, code_info, code_info). +:- mode disj_gen__generate_non_disjuncts(in, in, in, in, in, in, out, in, out) + is det. + +disj_gen__generate_non_disjuncts([], _, _, _, _, _, _) --> + { error("empty nondet disjunction!") }. +disj_gen__generate_non_disjuncts([Goal0 | Goals], StoreMap, EndLabel, + HaveTempFrame0, ReclaimHeap, Constraints, Code) --> + + { Goal0 = GoalExpr0 - GoalInfo0 }, + { goal_info_get_resume_point(GoalInfo0, Resume) }, + ( + { Resume = resume_point(ResumeVars, ResumeLocs) } + -> + % Emit code for a non-last disjunct, including setting things + % up for the execution of the next disjunct. + + code_info__push_resume_point_vars(ResumeVars), + code_info__make_known_failure_cont(ResumeVars, ResumeLocs, yes, + HaveTempFrame0, HaveTempFrame, ModContCode), + % The next line is to enable Goal to pass the + % pre_goal_update sanity check + { goal_info_set_resume_point(GoalInfo0, no_resume_point, + GoalInfo) }, + { Goal = GoalExpr0 - GoalInfo }, + + % Reset the heap pointer to recover memory allocated + % by the previous disjunct, if necessary + code_info__maybe_get_old_hp(ReclaimHeap, RestoreHPCode), + + % Reset the solver state if necessary + code_info__maybe_restore_ticket(Constraints, RestoreTicketCode), - disj_gen__generate_non_disj_2(Goals, StoreMap, EndLab, - RestCode), - { DisjCode = tree(tree(GoalCode, SuccCode), - tree(ModifyFailureContCode, - tree(RestoreHeapCode, - tree(RestoreTicketCode, RestCode)))) } - ). -%---------------------------------------------------------------------------% + code_info__grab_code_info(CodeInfo), -:- pred disj_gen__sort_cases(list(hlds__goal), list(hlds__goal)). -:- mode disj_gen__sort_cases(in, out) is det. + code_gen__generate_goal(model_non, Goal, GoalCode), + code_info__generate_branch_end(model_non, StoreMap, SaveCode), -disj_gen__sort_cases(Goals0, Goals) :- - disj_gen__sort_cases_2(Goals0, CanFail, CannotFail), - list__append(CannotFail, CanFail, Goals). + % make sure every variable in the resume set is in its + % stack slot + code_info__flush_resume_vars_to_stack(FlushResumeVarsCode), -:- pred disj_gen__sort_cases_2(list(hlds__goal), list(hlds__goal), - list(hlds__goal)). -:- mode disj_gen__sort_cases_2(in, out, out) is det. + % make sure the redoip of the top frame points to the + % right label + % XXX code missing -disj_gen__sort_cases_2([], [], []). -disj_gen__sort_cases_2([Goal0 - GoalInfo0 | Goals0], CanFail, CannotFail) :- - disj_gen__sort_cases_2(Goals0, CanFail0, CannotFail0), - goal_info_get_code_model(GoalInfo0, CodeModel), - ( CodeModel = model_det -> - CannotFail = [Goal0 - GoalInfo0 | CannotFail0], - CanFail = CanFail0 + + % Kill any variables made zombies by the goal + % XXX should not be necessary, since the state + % we set up will be discarded anyway + code_info__pickup_zombies(Zombies), + code_info__make_vars_dead(Zombies), + + { BranchCode = node([ + goto(label(EndLabel)) - + "skip to end of nondet disj" + ]) }, + + code_info__slap_code_info(CodeInfo), + code_info__pop_resume_point_vars, + code_info__restore_failure_cont(RestoreContCode), + + disj_gen__generate_non_disjuncts(Goals, StoreMap, EndLabel, + HaveTempFrame, ReclaimHeap, Constraints, RestCode), + + { Code = tree(ModContCode, + tree(RestoreHPCode, + tree(RestoreTicketCode, + tree(GoalCode, + tree(SaveCode, + tree(FlushResumeVarsCode, + tree(BranchCode, + tree(RestoreContCode, + RestCode)))))))) } ; - CannotFail = CannotFail0, - CanFail = [Goal0 - GoalInfo0 | CanFail0] + % Emit code for the last disjunct + + { Goals = [] -> + true + ; + error("disj_gen__generate_non_disjuncts: last disjunct followed by others") + }, + + % Restore the heap pointer if necessary, + % and pop the temp stack that we saved it on + % if we saved it + code_info__maybe_get_old_hp(ReclaimHeap, RestoreHPCode), + code_info__maybe_pop_stack(ReclaimHeap, UnSaveHPCode), + + % Restore the solver state if necessary + code_info__maybe_restore_ticket_and_pop(Constraints, + RestorePopTicketCode), + + code_gen__generate_goal(model_non, Goal0, GoalCode), + code_info__generate_branch_end(model_non, StoreMap, SaveCode), + + % Kill any variables made zombies by the goal + % XXX should not be necessary, since we are not + % coming out from under a restore anyway + code_info__pickup_zombies(Zombies), + code_info__make_vars_dead(Zombies), + + { EndCode = node([ + label(EndLabel) - "End of pruned disj" + ]) }, + { Code = tree(RestoreHPCode, + tree(UnSaveHPCode, + tree(RestorePopTicketCode, + tree(GoalCode, + tree(SaveCode, + EndCode))))) } ). %---------------------------------------------------------------------------% -%---------------------------------------------------------------------------% diff --git a/compiler/hlds_goal.m b/compiler/hlds_goal.m index c787e65d7..6e6e34ca3 100644 --- a/compiler/hlds_goal.m +++ b/compiler/hlds_goal.m @@ -337,10 +337,13 @@ % for the definition of this. % see notes/ALLOCATION for what these alternatives mean -:- type resume_point ---> none - ; orig_only(set(var)) - ; stack_only(set(var)) - ; orig_and_stack(set(var)). +:- type resume_point ---> resume_point(set(var), resume_locs) + ; no_resume_point. + +:- type resume_locs ---> orig_only + ; stack_only + ; orig_and_stack + ; stack_and_orig. :- implementation. @@ -552,6 +555,9 @@ get_pragma_c_var_names_2([MaybeName | MaybeNames], Names0, Names) :- :- pred goal_set_resume_point(hlds__goal, resume_point, hlds__goal). :- mode goal_set_resume_point(in, in, out) is det. +:- pred goal_info_resume_vars_and_loc(resume_point, set(var), resume_locs). +:- mode goal_info_resume_vars_and_loc(in, out, out) is det. + % Convert a goal to a list of conjuncts. % If the goal is a conjunction, then return its conjuncts, % otherwise return the goal as a singleton list. @@ -594,6 +600,8 @@ get_pragma_c_var_names_2([MaybeName | MaybeNames], Names0, Names) :- :- implementation. +:- import_module require. + goal_info_init(GoalInfo) :- ExternalDetism = erroneous, set__init(PreBirths), @@ -607,7 +615,7 @@ goal_info_init(GoalInfo) :- set__init(Features), GoalInfo = goal_info(PreBirths, PostBirths, PreDeaths, PostDeaths, ExternalDetism, InstMapDelta, Context, NonLocals, no, - no, Features, NondetLives, none). + no, Features, NondetLives, no_resume_point). goal_info_pre_births(GoalInfo, PreBirths) :- GoalInfo = goal_info(PreBirths, _, _, _, _, _, _, _, _, _, _, _, _). @@ -741,6 +749,17 @@ goal_set_follow_vars(Goal - GoalInfo0, FollowVars, Goal - GoalInfo) :- goal_set_resume_point(Goal - GoalInfo0, ResumePoint, Goal - GoalInfo) :- goal_info_set_resume_point(GoalInfo0, ResumePoint, GoalInfo). +%-----------------------------------------------------------------------------% + +goal_info_resume_vars_and_loc(Resume, Vars, Locs) :- + + ( + Resume = resume_point(Vars, Locs) + ; + Resume = no_resume_point, + error("goal_info__get_resume_vars_and_loc: no resume point") + ). + %-----------------------------------------------------------------------------% % Convert a goal to a list of conjuncts. diff --git a/compiler/hlds_out.m b/compiler/hlds_out.m index 81248380e..3d21fbf7e 100644 --- a/compiler/hlds_out.m +++ b/compiler/hlds_out.m @@ -606,19 +606,19 @@ hlds_out__write_goal_a(Goal - GoalInfo, ModuleInfo, VarSet, Indent, Follow, TypeQual) --> globals__io_lookup_bool_option(verbose_dump_hlds, Verbose), ( { Verbose = yes } -> - { goal_info_context(GoalInfo, Context) }, - { term__context_file(Context, FileName) }, - { term__context_line(Context, LineNumber) }, - ( { FileName \= "" } -> - hlds_out__write_indent(Indent), - io__write_string("% context: file `"), - io__write_string(FileName), - io__write_string("', line "), - io__write_int(LineNumber), - io__write_string("\n") - ; - [] - ), +% { goal_info_context(GoalInfo, Context) }, +% { term__context_file(Context, FileName) }, +% { term__context_line(Context, LineNumber) }, +% ( { FileName \= "" } -> +% hlds_out__write_indent(Indent), +% io__write_string("% context: file `"), +% io__write_string(FileName), +% io__write_string("', line "), +% io__write_int(LineNumber), +% io__write_string("\n") +% ; +% [] +% ), { goal_info_get_nonlocals(GoalInfo, NonLocalsSet) }, { set__to_sorted_list(NonLocalsSet, NonLocalsList) }, ( { NonLocalsList \= [] } -> @@ -649,29 +649,6 @@ hlds_out__write_goal_a(Goal - GoalInfo, ModuleInfo, ; [] ), - { goal_info_nondet_lives(GoalInfo, NondetLives) }, - { set__to_sorted_list(NondetLives, NondetList) }, - ( { NondetList \= [] } -> - hlds_out__write_indent(Indent), - io__write_string("% nondet-lives: "), - mercury_output_vars(NondetList, VarSet), - io__write_string("\n") - ; - [] - ), - { goal_info_cont_lives(GoalInfo, MaybeContLives) }, - ( - { MaybeContLives = yes(ContLives) }, - { set__to_sorted_list(ContLives, ContList) }, - { ContList \= [] } - -> - hlds_out__write_indent(Indent), - io__write_string("% cont-lives: "), - mercury_output_vars(ContList, VarSet), - io__write_string("\n") - ; - [] - ), { goal_info_follow_vars(GoalInfo, MaybeFollowVars) }, ( { MaybeFollowVars = yes(FollowVars) } @@ -728,26 +705,25 @@ hlds_out__write_goal_a(Goal - GoalInfo, ModuleInfo, ), { goal_info_get_resume_point(GoalInfo, Resume) }, ( - { Resume = none } + { Resume = no_resume_point } ; - { Resume = orig_only(ResumeVars) }, + { Resume = resume_point(ResumeVars, Locs) }, { set__to_sorted_list(ResumeVars, ResumeVarList) }, hlds_out__write_indent(Indent), - io__write_string("% resume point orig only: "), - mercury_output_vars(ResumeVarList, VarSet), - io__write_string("\n") - ; - { Resume = stack_only(ResumeVars) }, - { set__to_sorted_list(ResumeVars, ResumeVarList) }, - hlds_out__write_indent(Indent), - io__write_string("% resume point stack only: "), - mercury_output_vars(ResumeVarList, VarSet), - io__write_string("\n") - ; - { Resume = orig_and_stack(ResumeVars) }, - { set__to_sorted_list(ResumeVars, ResumeVarList) }, - hlds_out__write_indent(Indent), - io__write_string("% resume point orig and stack: "), + io__write_string("% resume point "), + ( + { Locs = orig_only }, + io__write_string("orig only ") + ; + { Locs = stack_only }, + io__write_string("stack only ") + ; + { Locs = orig_and_stack }, + io__write_string("orig and stack ") + ; + { Locs = stack_and_orig }, + io__write_string("stack and orig ") + ), mercury_output_vars(ResumeVarList, VarSet), io__write_string("\n") ), diff --git a/compiler/ite_gen.m b/compiler/ite_gen.m index acf9ca3b6..1b8516a94 100644 --- a/compiler/ite_gen.m +++ b/compiler/ite_gen.m @@ -5,9 +5,14 @@ %---------------------------------------------------------------------------% %---------------------------------------------------------------------------% % -% file: ite_gen.m +% File: ite_gen.m % -% main authors: conway, fjh. +% Main authors: conway, fjh, zs. +% +% The predicates of this module generate code for if-then-elses. +% +% The handling of model_det and model_semi if-then-elses is almost identical. +% The handling of model_non if-then-elses is also quite similar. % %---------------------------------------------------------------------------% %---------------------------------------------------------------------------% @@ -31,302 +36,249 @@ :- mode ite_gen__generate_nondet_ite(in, in, in, in, out, in, out) is det. %---------------------------------------------------------------------------% + :- implementation. :- import_module code_gen, code_util, options, globals. :- import_module bool, set, tree, list, map, std_util, require. -ite_gen__generate_det_ite(CondGoal, ThenGoal, ElseGoal, StoreMap, Instr) --> - code_info__get_globals(Options), - { CondGoal = _Goal - CondGoalInfo }, - { goal_info_cont_lives(CondGoalInfo, MaybeLives) }, - { - MaybeLives = yes(Vars0) - -> - Vars = Vars0 - ; - error("ite_gen__generate_det_ite: no cont_lives!") - }, - code_info__make_known_failure_cont(Vars, no, ModContCode), - { - globals__lookup_bool_option(Options, - reclaim_heap_on_semidet_failure, yes), - code_util__goal_may_allocate_heap(CondGoal) - -> - ReclaimHeap = yes - ; - ReclaimHeap = no - }, - code_info__maybe_save_hp(ReclaimHeap, HPSaveCode), - % Grab the instmap - % generate the semi-deterministic test goal - code_info__get_instmap(InstMap), +ite_gen__generate_det_ite(CondGoal, ThenGoal, ElseGoal, StoreMap, Code) --> + ite_gen__generate_basic_ite(CondGoal, ThenGoal, ElseGoal, StoreMap, + model_det, Code). - % Store the current solver state before the condition of - % the ite - { globals__lookup_bool_option(Options, - constraints, UseConstraints) }, - code_info__maybe_save_ticket(UseConstraints, SaveTicketCode), - code_gen__generate_goal(model_semi, CondGoal, TestCode0), - { TestCode = tree(SaveTicketCode, TestCode0) }, - code_info__grab_code_info(CodeInfo), - code_info__pop_failure_cont, - code_info__maybe_pop_stack(ReclaimHeap, HPPopCode), - % Discard solver ticket if the condition succeeded - code_info__maybe_discard_ticket(UseConstraints, PopTicketCode), - code_gen__generate_forced_goal(model_det, ThenGoal, StoreMap, - ThenGoalCode0), - { ThenGoalCode = tree(PopTicketCode, ThenGoalCode0) }, - % generate code that executes the then condition - % and branches to the end of the if-then-else - code_info__slap_code_info(CodeInfo), - code_info__restore_failure_cont(RestoreContCode), - % restore the instmap - code_info__set_instmap(InstMap), - code_info__maybe_restore_hp(ReclaimHeap, HPRestoreCode), - % Restore the solver ticket if the condition failed - code_info__maybe_restore_ticket_and_pop(UseConstraints, - RestoreTicketCode), - code_gen__generate_forced_goal(model_det, ElseGoal, StoreMap, - ElseGoalCode0), - { ElseGoalCode = tree(RestoreTicketCode, ElseGoalCode0) }, - code_info__get_next_label(EndLab), - % place the label marking the start of the then code, - % then execute the then goal, and then mark the end - % of the if-then-else - { CondCode = tree( - HPSaveCode, - TestCode - ) }, - { ThenCode = tree( - tree(HPPopCode, ThenGoalCode), - node([ goto(label(EndLab)) - - "Jump to the end of if-then-else" ]) - ) }, - { ElseCode = tree( - tree( - tree(RestoreContCode, HPRestoreCode), - ElseGoalCode - ), - node([label(EndLab) - "end of if-then-else"]) - ) }, - % generate the then condition - { Instr = tree( - tree(ModContCode, CondCode), - tree(ThenCode, ElseCode) - ) }, - code_info__remake_with_store_map(StoreMap). +ite_gen__generate_semidet_ite(CondGoal, ThenGoal, ElseGoal, StoreMap, Code) --> + ite_gen__generate_basic_ite(CondGoal, ThenGoal, ElseGoal, StoreMap, + model_semi, Code). %---------------------------------------------------------------------------% -ite_gen__generate_semidet_ite(CondGoal, ThenGoal, ElseGoal, StoreMap, Instr) - --> - code_info__get_globals(Options), - { CondGoal = _Goal - CondGoalInfo }, - { goal_info_cont_lives(CondGoalInfo, MaybeLives) }, - { - MaybeLives = yes(Vars0) - -> - Vars = Vars0 - ; - error("ite_gen__generate_det_ite: no cont_lives!") - }, - code_info__make_known_failure_cont(Vars, no, ModContCode), - { - globals__lookup_bool_option(Options, - reclaim_heap_on_semidet_failure, yes), - code_util__goal_may_allocate_heap(CondGoal) - -> - ReclaimHeap = yes - ; - ReclaimHeap = no - }, - code_info__maybe_save_hp(ReclaimHeap, HPSaveCode), - % generate the semi-deterministic test goal - code_info__get_instmap(InstMap), +:- pred ite_gen__generate_basic_ite(hlds__goal, hlds__goal, hlds__goal, + store_map, code_model, code_tree, code_info, code_info). +:- mode ite_gen__generate_basic_ite(in, in, in, in, in, out, in, out) is det. - % Store the current solver state before the condition of - % the ite - { globals__lookup_bool_option(Options, - constraints, UseConstraints) }, - code_info__maybe_save_ticket(UseConstraints, StoreTicketCode), - code_gen__generate_goal(model_semi, CondGoal, CondCode0), - { CondCode = tree(StoreTicketCode, CondCode0) }, - code_info__grab_code_info(CodeInfo), - code_info__maybe_pop_stack(ReclaimHeap, HPPopCode), - code_info__pop_failure_cont, - % Pop the solver ticket if the condition succeeded - code_info__maybe_discard_ticket(UseConstraints, PopTicketCode), - code_gen__generate_forced_goal(model_semi, ThenGoal, StoreMap, - ThenGoalCode0), - { ThenGoalCode = tree(PopTicketCode, ThenGoalCode0) }, - code_info__slap_code_info(CodeInfo), - code_info__restore_failure_cont(RestoreContCode), - % restore the instmap - code_info__set_instmap(InstMap), - code_info__maybe_restore_hp(ReclaimHeap, HPRestoreCode), - % Restore the solver ticket if the condition failed - code_info__maybe_restore_ticket_and_pop(UseConstraints, - RestoreTicketCode), - code_gen__generate_forced_goal(model_semi, ElseGoal, StoreMap, - ElseGoalCode0), - { ElseGoalCode = tree(RestoreTicketCode, ElseGoalCode0) }, - code_info__get_next_label(EndLab), - { TestCode = tree( - tree(ModContCode, HPSaveCode), - CondCode - ) }, - { ThenCode = tree( - tree( - HPPopCode, - ThenGoalCode - ), - node([ goto(label(EndLab)) - - "Jump to the end of if-then-else" ]) - ) }, - { ElseCode = tree( - tree( - tree(RestoreContCode, HPRestoreCode), - ElseGoalCode - ), - node([label(EndLab) - "end of if-then-else"]) - ) }, - % generate the then condition - { Instr = tree(TestCode, tree(ThenCode, ElseCode)) }, - code_info__remake_with_store_map(StoreMap). +ite_gen__generate_basic_ite(CondGoal0, ThenGoal, ElseGoal, StoreMap, CodeModel, + Code) --> -%---------------------------------------------------------------------------% - -ite_gen__generate_nondet_ite(CondGoal, ThenGoal, ElseGoal, StoreMap, Instr) - --> - code_info__get_globals(Options), - { - globals__lookup_bool_option(Options, - reclaim_heap_on_semidet_failure, yes), - code_util__goal_may_allocate_heap(CondGoal) - -> - ReclaimHeap = yes - ; - ReclaimHeap = no - }, - { CondGoal = _ - CondGoalInfo }, - { goal_info_get_code_model(CondGoalInfo, CondCodeModel) }, + % Set up for the possible failure of the condition + { CondGoal0 = CondExpr - CondInfo0 }, + { goal_info_get_resume_point(CondInfo0, Resume) }, ( - { CondCodeModel = model_non } + { Resume = resume_point(ResumeVarsPrime, ResumeLocsPrime) } -> + { ResumeVars = ResumeVarsPrime}, + { ResumeLocs = ResumeLocsPrime} + ; + { error("condition of an if-then-else has no resume point") } + ), + code_info__make_known_failure_cont(ResumeVars, ResumeLocs, no, + no, _, ModContCode), + % The next line is to enable Cond to pass the + % pre_goal_update sanity check + { goal_info_set_resume_point(CondInfo0, no_resume_point, CondInfo) }, + { CondGoal = CondExpr - CondInfo }, + + % Maybe save the heap state current before the condition; + % this ought to be after we make the failure continuation + % because that causes the cache to get flushed + code_info__get_globals(Globals), + { + globals__lookup_bool_option(Globals, + reclaim_heap_on_semidet_failure, yes), + code_util__goal_may_allocate_heap(CondGoal) + -> + ReclaimHeap = yes + ; + ReclaimHeap = no + }, + code_info__maybe_save_hp(ReclaimHeap, SaveHPCode), + + % Maybe save the solver state current before the condition + { globals__lookup_bool_option(Globals, constraints, Constraints) }, + code_info__maybe_save_ticket(Constraints, SaveTicketCode), + + code_info__grab_code_info(CodeInfo), + + % Generate the condition as a semi-deterministic goal + code_info__push_resume_point_vars(ResumeVars), + code_gen__generate_goal(model_semi, CondGoal, CondCode), + code_info__pop_resume_point_vars, + + % Kill again any variables that have become zombies + code_info__pickup_zombies(Zombies), + code_info__make_vars_dead(Zombies), + + code_info__pop_failure_cont, + + % Discard hp and solver ticket if the condition succeeded + code_info__maybe_pop_stack(ReclaimHeap, PopHPCode), + code_info__maybe_discard_ticket(Constraints, PopTicketCode), + + % Generate the then branch + code_gen__generate_goal(CodeModel, ThenGoal, ThenCode), + code_info__generate_branch_end(CodeModel, StoreMap, ThenSaveCode), + + % Generate the entry to the else branch + code_info__slap_code_info(CodeInfo), + code_info__restore_failure_cont(RestoreContCode), + code_info__maybe_restore_hp(ReclaimHeap, RestoreHPCode), + code_info__maybe_restore_ticket_and_pop(Constraints, RestoreTicketCode), + + % Generate the else branch + code_gen__generate_goal(CodeModel, ElseGoal, ElseCode), + code_info__generate_branch_end(CodeModel, StoreMap, ElseSaveCode), + + code_info__get_next_label(EndLab), + { JumpToEndCode = node([goto(label(EndLab)) + - "Jump to the end of if-then-else"]) }, + { EndLabelCode = node([label(EndLab) - "end of if-then-else"]) }, + { Code = tree(ModContCode, + tree(SaveHPCode, + tree(SaveTicketCode, + tree(CondCode, + tree(PopHPCode, + tree(PopTicketCode, + tree(ThenCode, + tree(ThenSaveCode, + tree(JumpToEndCode, + tree(RestoreContCode, + tree(RestoreHPCode, + tree(RestoreTicketCode, + tree(ElseCode, + tree(ElseSaveCode, + EndLabelCode)))))))))))))) + }, + code_info__remake_with_store_map(StoreMap). + +%---------------------------------------------------------------------------% + +ite_gen__generate_nondet_ite(CondGoal0, ThenGoal, ElseGoal, StoreMap, Code) --> + + % Set up for the possible failure of the condition + { CondGoal0 = CondExpr - CondInfo0 }, + { goal_info_get_code_model(CondInfo0, CondCodeModel) }, + ( { CondCodeModel = model_non } -> { NondetCond = yes } ; { NondetCond = no } ), - { goal_info_cont_lives(CondGoalInfo, MaybeLives) }, - { - MaybeLives = yes(Vars0) - -> - Vars = Vars0 - ; - error("ite_gen__generate_det_ite: no cont_lives!") - }, - code_info__make_known_failure_cont(Vars, NondetCond, ModContCode), + { goal_info_get_resume_point(CondInfo0, Resume) }, ( - { NondetCond = yes } + { Resume = resume_point(ResumeVarsPrime, ResumeLocsPrime) } -> - % prevent the condition from hijacking the redoip slot - % We could improve the efficiency of this - code_info__unset_failure_cont, + { ResumeVars = ResumeVarsPrime}, + { ResumeLocs = ResumeLocsPrime} + ; + { error("condition of an if-then-else has no resume point") } + ), + code_info__make_known_failure_cont(ResumeVars, ResumeLocs, NondetCond, + no, _, ModContCode), + % The next line is to enable Cond to pass the + % pre_goal_update sanity check + { goal_info_set_resume_point(CondInfo0, no_resume_point, CondInfo) }, + { CondGoal = CondExpr - CondInfo }, + + % Prevent a nondet condition from hijacking the redoip slot + % We could improve the efficiency of this + ( { NondetCond = yes } -> + code_info__unset_failure_cont(FlushEnclosingResumeVarsCode), code_info__save_maxfr(MaxfrLval0, SaveMaxfrCode), { MaybeMaxfrLval = yes(MaxfrLval0) } ; - { MaybeMaxfrLval = no }, - { SaveMaxfrCode = empty } + { FlushEnclosingResumeVarsCode = empty }, + { SaveMaxfrCode = empty }, + { MaybeMaxfrLval = no } ), - code_info__maybe_save_hp(ReclaimHeap, HPSaveCode), - ( - { NondetCond = yes } - -> - % we need to save variables on the stack here since the - % condition might do a redo() without saving them - { set__to_sorted_list(Vars, VarList) }, - code_gen__ensure_vars_are_saved(VarList, EnsureCode) - ; - { EnsureCode = empty } - ), - code_info__get_instmap(InstMap), - % Store the current solver state before the condition of - % the ite - { globals__lookup_bool_option(Options, - constraints, UseConstraints) }, - code_info__maybe_save_ticket(UseConstraints, StoreTicketCode), - code_gen__generate_goal(model_non, CondGoal, CondCode0), - { CondCode = tree(EnsureCode, - tree(StoreTicketCode, CondCode0)) }, - code_info__grab_code_info(CodeInfo), - code_info__maybe_pop_stack(ReclaimHeap, HPPopCode), - code_info__pop_failure_cont, - ( - { MaybeMaxfrLval = yes(MaxfrLval) } + % Maybe save the heap state current before the condition; + % this ought to be after we make the failure continuation + % because that causes the cache to get flushed + code_info__get_globals(Globals), + { + globals__lookup_bool_option(Globals, + reclaim_heap_on_semidet_failure, yes), + code_util__goal_may_allocate_heap(CondGoal) -> - code_info__do_soft_cut(MaxfrLval, HackStackCode), - code_info__unset_failure_cont + ReclaimHeap = yes ; - { HackStackCode = empty } + ReclaimHeap = no + }, + code_info__maybe_save_hp(ReclaimHeap, SaveHPCode), + + % Maybe save the current solver state before the condition + { globals__lookup_bool_option(Globals, constraints, Constraints) }, + code_info__maybe_save_ticket(Constraints, SaveTicketCode), + + code_info__grab_code_info(CodeInfo), + + % Generate the condition as a non-deterministic goal + % XXX surely we ought to pass CondCodeModel here? + code_info__push_resume_point_vars(ResumeVars), + code_gen__generate_goal(model_non, CondGoal, CondCode), + code_info__pop_resume_point_vars, + + code_info__pop_failure_cont, + ( { MaybeMaxfrLval = yes(MaxfrLval) } -> + code_info__do_soft_cut(MaxfrLval, SoftCutCode), + code_info__unset_failure_cont(FlushCode) + ; + { SoftCutCode = empty }, + { FlushCode = empty } ), - ( - { NondetCond = yes } - -> + + % Kill again any variables that have become zombies + code_info__pickup_zombies(Zombies), + code_info__make_vars_dead(Zombies), + + % Discard hp and maybe solver ticket if the condition succeeded + code_info__maybe_pop_stack(ReclaimHeap, PopHPCode), + ( { NondetCond = yes } -> % We cannot discard the solver ticket if the % condition can be backtracked into. - code_info__maybe_pop_stack(UseConstraints, PopTicketCode) + code_info__maybe_pop_stack(Constraints, PopTicketCode) ; % Discard the solver ticket if the condition succeeded % and we will not backtrack into the condition - code_info__maybe_discard_ticket(UseConstraints, PopTicketCode) + code_info__maybe_discard_ticket(Constraints, PopTicketCode) ), - code_gen__generate_forced_goal(model_non, ThenGoal, StoreMap, - ThenGoalCode0), - { ThenGoalCode = tree(PopTicketCode, ThenGoalCode0) }, + + % Generate the then branch + code_gen__generate_goal(model_non, ThenGoal, ThenCode), + code_info__generate_branch_end(model_non, StoreMap, ThenSaveCode), + + % Generate the entry to the else branch code_info__slap_code_info(CodeInfo), code_info__restore_failure_cont(RestoreContCode), - % restore the instmap - code_info__set_instmap(InstMap), - code_info__maybe_restore_hp(ReclaimHeap, HPRestoreCode), - % Restore the solver ticket if the condition failed - code_info__maybe_restore_ticket_and_pop(UseConstraints, - RestoreTicketCode), - code_gen__generate_forced_goal(model_non, ElseGoal, StoreMap, - ElseGoalCode0), - { ElseGoalCode = tree(RestoreTicketCode, ElseGoalCode0) }, + code_info__maybe_restore_hp(ReclaimHeap, RestoreHPCode), + code_info__maybe_restore_ticket_and_pop(Constraints, RestoreTicketCode), + + % Generate the else branch + code_gen__generate_goal(model_non, ElseGoal, ElseCode), + code_info__generate_branch_end(model_non, StoreMap, ElseSaveCode), + code_info__get_next_label(EndLab), - code_info__remake_with_store_map(StoreMap), - { TestCode = tree( - tree( - tree(ModContCode, SaveMaxfrCode), - HPSaveCode - ), - CondCode - ) }, - { ThenCode = tree( - tree( - tree( - HackStackCode, - HPPopCode - ), - ThenGoalCode - ), - node([ goto(label(EndLab)) - - "Jump to the end of if-then-else" ]) - ) }, - { ElseCode = tree( - tree( - tree( - RestoreContCode, - HPRestoreCode - ), - ElseGoalCode - ), - node([label(EndLab) - "end of if-then-else"]) - ) }, - % generate the then condition - { Instr = tree(TestCode, tree(ThenCode, ElseCode)) }. + { JumpToEndCode = node([goto(label(EndLab)) + - "Jump to the end of if-then-else"]) }, + { EndLabelCode = node([label(EndLab) - "end of if-then-else"]) }, + { Code = tree(ModContCode, + tree(FlushEnclosingResumeVarsCode, + tree(SaveMaxfrCode, + tree(SaveHPCode, + tree(SaveTicketCode, + tree(CondCode, + tree(SoftCutCode, + tree(FlushCode, + tree(PopHPCode, + tree(PopTicketCode, + tree(ThenCode, + tree(ThenSaveCode, + tree(JumpToEndCode, + tree(RestoreContCode, + tree(RestoreHPCode, + tree(RestoreTicketCode, + tree(ElseCode, + tree(ElseSaveCode, + EndLabelCode)))))))))))))))))) + }, + code_info__remake_with_store_map(StoreMap). %---------------------------------------------------------------------------% diff --git a/compiler/live_vars.m b/compiler/live_vars.m index b5b4b9238..c265f592b 100644 --- a/compiler/live_vars.m +++ b/compiler/live_vars.m @@ -3,13 +3,19 @@ % This file may only be copied under the terms of the GNU General % Public License - see the file COPYING in the Mercury distribution. %-----------------------------------------------------------------------------% - -% Main author: conway. - -% Computes the `stack_slots', i.e. which variables are either live across a -% call or live at the start of a disjunction, allocates a stack slot for -% each of these variables, and stores this information in the stack_slots -% structure in the proc_info. +% +% File live_vars.m +% +% Main authors: conway, zs. +% +% This module allocates stack slots to the variables that need to be saved +% either across a call or across a goal that may fail. +% +% The jobs is done in two steps. First we traverse the predicate definition +% looking for sets of variables that must be saved on the stack at the same +% time. Then we use a graph colouring algorithm to find an allocation of +% stack slots (colours) to variables such that in each set of variables +% that must be saved at the same time, each variable has a different colour. %-----------------------------------------------------------------------------% @@ -29,7 +35,7 @@ :- implementation. :- import_module llds, arg_info, prog_data, hlds_goal, hlds_data, mode_util. -:- import_module code_aux, globals, graph_colour, instmap. +:- import_module liveness, code_aux, globals, graph_colour, instmap. :- import_module list, map, set, std_util, assoc_list. :- import_module int, term, require. @@ -39,10 +45,11 @@ allocate_stack_slots_in_proc(ProcInfo0, ModuleInfo, ProcInfo) :- proc_info_goal(ProcInfo0, Goal0), proc_info_interface_code_model(ProcInfo0, CodeModel), - detect_initial_liveness(ProcInfo0, ModuleInfo, Liveness0), + initial_liveness(ProcInfo0, ModuleInfo, Liveness0), set__init(LiveSets0), - build_live_sets_in_goal(Goal0, Liveness0, LiveSets0, - ModuleInfo, ProcInfo0, _Liveness, LiveSets), + set__init(ResumeVars0), + build_live_sets_in_goal(Goal0, Liveness0, ResumeVars0, LiveSets0, + ModuleInfo, ProcInfo0, _Liveness, _ResumeVars, LiveSets), graph_colour__group_elements(LiveSets, ColourSets), set__to_sorted_list(ColourSets, ColourList), allocate_stack_slots(ColourList, CodeModel, StackSlots), @@ -56,20 +63,18 @@ allocate_stack_slots_in_proc(ProcInfo0, ModuleInfo, ProcInfo) :- % of the goal. The liveness information is computed from the liveness % delta annotations. -:- pred build_live_sets_in_goal(hlds__goal, - liveness_info, set(set(var)), module_info, - proc_info, liveness_info, set(set(var))). -:- mode build_live_sets_in_goal(in, in, in, in, in, out, out) is det. +:- pred build_live_sets_in_goal(hlds__goal, set(var), set(var), set(set(var)), + module_info, proc_info, set(var), set(var), set(set(var))). +:- mode build_live_sets_in_goal(in, in, in, in, in, in, out, out, out) is det. -build_live_sets_in_goal(Goal0 - GoalInfo, Liveness0, - LiveSets0, ModuleInfo, ProcInfo, - Liveness, LiveSets) :- +build_live_sets_in_goal(Goal0 - GoalInfo, Liveness0, ResumeVars0, LiveSets0, + ModuleInfo, ProcInfo, Liveness, ResumeVars, LiveSets) :- goal_info_pre_births(GoalInfo, PreBirths), goal_info_pre_deaths(GoalInfo, PreDeaths), goal_info_post_births(GoalInfo, PostBirths), goal_info_post_deaths(GoalInfo, PostDeaths), - set__difference(Liveness0, PreDeaths, Liveness1), + set__difference(Liveness0, PreDeaths, Liveness1), set__union(Liveness1, PreBirths, Liveness2), % % if the goal is atomic, we want to apply the postdeaths @@ -84,29 +89,35 @@ build_live_sets_in_goal(Goal0 - GoalInfo, Liveness0, Liveness3 = Liveness2 ), - goal_info_get_code_model(GoalInfo, CodeModel), - goal_info_nondet_lives(GoalInfo, NondetLives0), goal_info_get_resume_point(GoalInfo, ResumePoint), ( - ResumePoint = none, - NondetLives = NondetLives0, + ResumePoint = no_resume_point, + ResumeVars1 = ResumeVars0, LiveSets1 = LiveSets0 ; - ResumePoint = orig_only(_), - NondetLives = NondetLives0, - LiveSets1 = LiveSets0 - ; - ResumePoint = stack_only(ResumeVars), - set__union(NondetLives0, ResumeVars, NondetLives), - set__insert(LiveSets0, ResumeVars, LiveSets1) - ; - ResumePoint = orig_and_stack(ResumeVars), - set__union(NondetLives0, ResumeVars, NondetLives), - set__insert(LiveSets0, ResumeVars, LiveSets1) + ResumePoint = resume_point(ResumePointVars, Locs), + ( + Locs = orig_only, + ResumeVars1 = ResumeVars0, + LiveSets1 = LiveSets0 + ; + Locs = stack_only, + set__union(ResumeVars0, ResumePointVars, ResumeVars1), + set__insert(LiveSets0, ResumeVars1, LiveSets1) + ; + Locs = orig_and_stack, + set__union(ResumeVars0, ResumePointVars, ResumeVars1), + set__insert(LiveSets0, ResumeVars1, LiveSets1) + ; + Locs = stack_and_orig, + set__union(ResumeVars0, ResumePointVars, ResumeVars1), + set__insert(LiveSets0, ResumeVars1, LiveSets1) + ) ), - build_live_sets_in_goal_2(Goal0, NondetLives, Liveness3, LiveSets1, - CodeModel, ModuleInfo, ProcInfo, Liveness4, LiveSets2), + build_live_sets_in_goal_2(Goal0, Liveness3, ResumeVars1, LiveSets1, + GoalInfo, ModuleInfo, ProcInfo, + Liveness4, ResumeVars, LiveSets2), ( goal_is_atomic(Goal0) @@ -116,7 +127,7 @@ build_live_sets_in_goal(Goal0 - GoalInfo, Liveness0, set__difference(Liveness4, PostDeaths, Liveness5) ), - set__union(Liveness5, PostBirths, Liveness), + set__union(Liveness5, PostBirths, Liveness), % Add extra interference for variables that become live % and variables that be come dead in this goal. @@ -134,294 +145,300 @@ build_live_sets_in_goal(Goal0 - GoalInfo, Liveness0, ). %-----------------------------------------------------------------------------% + % Here we process each of the different sorts of goals. % `Liveness' is the set of live variables, i.e. vars which % have been referenced and may be referenced again (during % forward execution). - % `LiveSets' is the interference graph, i.e. the set of sets - % of variables which need to be on the stack at the same time. - % `NondetLives' is the set of variables that may or may not be + % `ResumeVars' is the set of variables that may or may not be % `live' during the current forward execution but will become % live again on backtracking. + % `LiveSets' is the interference graph, i.e. the set of sets + % of variables which need to be on the stack at the same time. -:- pred build_live_sets_in_goal_2(hlds__goal_expr, set(var), liveness_info, - set(set(var)), code_model, module_info, proc_info, - liveness_info, set(set(var))). -:- mode build_live_sets_in_goal_2(in, in, in, in, in, in, in, out, out) is det. +:- pred build_live_sets_in_goal_2(hlds__goal_expr, set(var), set(var), + set(set(var)), hlds__goal_info, module_info, proc_info, + set(var), set(var), set(set(var))). +:- mode build_live_sets_in_goal_2(in, in, in, in, in, in, in, out, out, out) + is det. -build_live_sets_in_goal_2(conj(Goals0), _NondetLives, Liveness0, LiveSets0, - _CodeModel, ModuleInfo, ProcInfo, Liveness, LiveSets) :- - build_live_sets_in_conj(Goals0, Liveness0, LiveSets0, - ModuleInfo, ProcInfo, Liveness, LiveSets). +build_live_sets_in_goal_2(conj(Goals0), Liveness0, ResumeVars0, LiveSets0, + _, ModuleInfo, ProcInfo, Liveness, ResumeVars, LiveSets) :- + build_live_sets_in_conj(Goals0, Liveness0, ResumeVars0, LiveSets0, + ModuleInfo, ProcInfo, Liveness, ResumeVars, LiveSets). -build_live_sets_in_goal_2(disj(Goals0, _), NondetLives, Liveness0, LiveSets0, - CodeModel, ModuleInfo, ProcInfo, Liveness, LiveSets) :- - ( CodeModel = model_non -> - % All the currently live variables need to be saved - % on the stack at the start of a nondet disjunction, since we - % may need them on backtracking. Therefore they need to - % be on the stack at the same time, and hence we insert - % them as an interference set into LiveSets. - set__union(Liveness0, NondetLives, LiveVars), - set__insert(LiveSets0, LiveVars, LiveSets1) - ; - LiveSets1 = LiveSets0 - ), - build_live_sets_in_disj(Goals0, Liveness0, LiveSets1, - ModuleInfo, ProcInfo, Liveness, LiveSets). +build_live_sets_in_goal_2(disj(Goals0, _), Liveness0, ResumeVars0, LiveSets0, + GoalInfo, ModuleInfo, ProcInfo, Liveness, ResumeVars, LiveSets) + :- + build_live_sets_in_disj(Goals0, Liveness0, ResumeVars0, LiveSets0, + GoalInfo, ModuleInfo, ProcInfo, Liveness, ResumeVars, LiveSets). -build_live_sets_in_goal_2(switch(_Var, _Det, Cases0, _), - _NondetLives, Liveness0, LiveSets0, _CodeModel, - ModuleInfo, ProcInfo, Liveness, LiveSets) :- - build_live_sets_in_cases(Cases0, Liveness0, LiveSets0, - ModuleInfo, ProcInfo, Liveness, LiveSets). +build_live_sets_in_goal_2(switch(_, _, Cases0, _), Liveness0, + ResumeVars0, LiveSets0, _, ModuleInfo, ProcInfo, Liveness, + ResumeVars, LiveSets) :- + build_live_sets_in_cases(Cases0, Liveness0, ResumeVars0, LiveSets0, + ModuleInfo, ProcInfo, Liveness, ResumeVars, LiveSets). build_live_sets_in_goal_2(if_then_else(_Vars, Cond0, Then0, Else0, _), - NondetLives, Liveness0, LiveSets0, _CodeModel, - ModuleInfo, ProcInfo, Liveness, LiveSets) :- - set__union(Liveness0, NondetLives, LiveVars), - ( code_aux__contains_only_builtins(Cond0) -> - LiveSets0A = LiveSets0 + Liveness0, ResumeVars0, LiveSets0, _, ModuleInfo, ProcInfo, + Liveness, ResumeVars, LiveSets) :- + build_live_sets_in_goal(Cond0, Liveness0, ResumeVars0, LiveSets0, + ModuleInfo, ProcInfo, Liveness1, ResumeVars1, LiveSets1), + build_live_sets_in_goal(Then0, Liveness1, ResumeVars1, LiveSets1, + ModuleInfo, ProcInfo, _Liveness2, ResumeVars2, LiveSets2), + build_live_sets_in_goal(Else0, Liveness0, ResumeVars0, LiveSets2, + ModuleInfo, ProcInfo, Liveness, ResumeVars3, LiveSets), + set__union(ResumeVars2, ResumeVars3, ResumeVars). + +build_live_sets_in_goal_2(not(Goal0), Liveness0, ResumeVars0, LiveSets0, + _, ModuleInfo, ProcInfo, Liveness, ResumeVars0, LiveSets) :- + build_live_sets_in_goal(Goal0, Liveness0, ResumeVars0, LiveSets0, + ModuleInfo, ProcInfo, Liveness, _, LiveSets). + +build_live_sets_in_goal_2(some(_Vs, Goal0), Liveness0, ResumeVars0, LiveSets0, + GoalInfo, ModuleInfo, ProcInfo, Liveness, ResumeVars, LiveSets) + :- + build_live_sets_in_goal(Goal0, Liveness0, ResumeVars0, LiveSets0, + ModuleInfo, ProcInfo, Liveness, ResumeVars1, LiveSets), + + % If the "some" goal cannot succeed more than once, + % then execution cannot backtrack into the inner goal once control + % has left it. Therefore the code following the "some" can reuse + % any stack slots needed by resume points in the inner goal. + + goal_info_get_code_model(GoalInfo, CodeModel), + ( CodeModel = model_non -> + ResumeVars = ResumeVars1 ; - set__insert(LiveSets0, LiveVars, LiveSets0A) - ), - build_live_sets_in_goal(Cond0, Liveness0, LiveSets0A, - ModuleInfo, ProcInfo, Liveness1, LiveSets1), - build_live_sets_in_goal(Then0, Liveness1, LiveSets1, - ModuleInfo, ProcInfo, _Liveness2, LiveSets2), - build_live_sets_in_goal(Else0, Liveness0, LiveSets2, - ModuleInfo, ProcInfo, Liveness, LiveSets). + ResumeVars = ResumeVars0 + ). -build_live_sets_in_goal_2(not(Goal0), NondetLives, Liveness0, LiveSets0, - _CodeModel, ModuleInfo, ProcInfo, Liveness, LiveSets) :- - set__union(Liveness0, NondetLives, LiveVars), - set__insert(LiveSets0, LiveVars, LiveSets1), - build_live_sets_in_goal(Goal0, Liveness0, LiveSets1, - ModuleInfo, ProcInfo, Liveness, LiveSets). - -build_live_sets_in_goal_2(some(_Vs, Goal0), _NondetLives, Liveness0, LiveSets0, - _CodeModel, ModuleInfo, ProcInfo, Liveness, LiveSets) :- - build_live_sets_in_goal(Goal0, Liveness0, LiveSets0, - ModuleInfo, ProcInfo, Liveness, LiveSets). - -build_live_sets_in_goal_2(higher_order_call(_PredVar, ArgVars, Types, Modes, - Det), - NondetLives, Liveness, LiveSets0, - _CodeModel, ModuleInfo, ProcInfo, Liveness, LiveSets) :- +build_live_sets_in_goal_2(higher_order_call(_, ArgVars, Types, Modes, Det), + Liveness, ResumeVars0, LiveSets0, + GoalInfo, ModuleInfo, ProcInfo, + Liveness, ResumeVars, LiveSets) :- % The variables which need to be saved onto the stack % before the call are all the variables that are live % after the call, except for the output arguments produced - % by the call, plus all the variables that are nondet - % live at the call. + % by the call, plus all the variables that may be needed + % at an enclosing resumption point. + % To figure out which variables are output, we use the arg_info; % but it shouldn't matter which arg convention we're using, % so we can just pass convention `simple' to make_arg_infos. - determinism_to_code_model(Det, CodeModel), - make_arg_infos(simple, Types, Modes, CodeModel, ModuleInfo, ArgInfos), + determinism_to_code_model(Det, CallModel), + make_arg_infos(simple, Types, Modes, CallModel, ModuleInfo, ArgInfos), find_output_vars_from_arg_info(ArgVars, ArgInfos, OutVars), - set__difference(Liveness, OutVars, LiveVars0), - set__union(LiveVars0, NondetLives, LiveVars1), + set__difference(Liveness, OutVars, InputLiveness), + set__union(InputLiveness, ResumeVars0, StackVars0), % Might need to add more live variables with accurate GC. - maybe_add_accurate_gc_typeinfos(ModuleInfo, ProcInfo, - OutVars, LiveVars1, LiveVars), + maybe_add_accurate_gc_typeinfos(ModuleInfo, ProcInfo, + OutVars, StackVars0, StackVars), - set__insert(LiveSets0, LiveVars, LiveSets). + set__insert(LiveSets0, StackVars, LiveSets), + % If this is a nondet call, then all the stack slots we need + % must be protected against reuse in following code. -build_live_sets_in_goal_2( - call(PredId, ProcId, ArgVars, Builtin, _, _), - NondetLives, Liveness, LiveSets0, - _CodeModel, ModuleInfo, ProcInfo, Liveness, LiveSets) :- + goal_info_get_code_model(GoalInfo, CodeModel), + ( CodeModel = model_non -> + ResumeVars = StackVars % includes ResumeVars0 + ; + ResumeVars = ResumeVars0 + ). + +build_live_sets_in_goal_2(call(PredId, ProcId, ArgVars, Builtin, _, _), + Liveness, ResumeVars0, LiveSets0, + GoalInfo, ModuleInfo, ProcInfo, + Liveness, ResumeVars, LiveSets) :- ( hlds__is_builtin_is_inline(Builtin) -> + ResumeVars = ResumeVars0, LiveSets = LiveSets0 ; % The variables which need to be saved onto the stack % before the call are all the variables that are live % after the call, except for the output arguments produced - % by the call, plus all the variables that are nondet - % live at the call. + % by the call, plus all the variables that may be needed + % at an enclosing resumption point. + find_output_vars(PredId, ProcId, ArgVars, ModuleInfo, OutVars), - set__difference(Liveness, OutVars, LiveVars0), - set__union(LiveVars0, NondetLives, LiveVars1), + set__difference(Liveness, OutVars, InputLiveness), + set__union(InputLiveness, ResumeVars0, StackVars0), % Might need to add more live variables with accurate GC. - maybe_add_accurate_gc_typeinfos(ModuleInfo, - ProcInfo, OutVars, LiveVars1, LiveVars), + maybe_add_accurate_gc_typeinfos(ModuleInfo, + ProcInfo, OutVars, StackVars0, StackVars), - set__insert(LiveSets0, LiveVars, LiveSets) + set__insert(LiveSets0, StackVars, LiveSets), + + % If this is a nondet call, then all the stack slots we need + % must be protected against reuse in following code. + + goal_info_get_code_model(GoalInfo, CodeModel), + ( CodeModel = model_non -> + ResumeVars = StackVars % includes ResumeVars0 + ; + ResumeVars = ResumeVars0 + ) ). -build_live_sets_in_goal_2(unify(_,_,_,D,_), NondetLives, Liveness, LiveSets0, - _CodeModel, _ModuleInfo, _ProcInfo, Liveness, LiveSets) :- +build_live_sets_in_goal_2(unify(_,_,_,D,_), Liveness, ResumeVars0, LiveSets0, + _, _, _, Liveness, ResumeVars0, LiveSets) :- ( D = complicated_unify(_, _) -> - % we have to save all live variables - % (and nondet-live variables) - % across complicated unifications. - set__union(Liveness, NondetLives, LiveVars), + % we have to save all live and protected variables + % across complicated unifications. + set__union(Liveness, ResumeVars0, LiveVars), set__insert(LiveSets0, LiveVars, LiveSets) ; LiveSets = LiveSets0 ). -build_live_sets_in_goal_2( - pragma_c_code(_C_Code, IsRecursive, PredId, ProcId, Args, - _ArgNameMap), - NondetLives, Liveness, LiveSets0, _Model, ModuleInfo, ProcInfo, - Liveness, LiveSets) :- +build_live_sets_in_goal_2(pragma_c_code(_, IsRec, PredId, ProcId, Args, _), + Liveness, ResumeVars0, LiveSets0, + GoalInfo, ModuleInfo, ProcInfo, + Liveness, ResumeVars, LiveSets) :- - ( IsRecursive = non_recursive -> + goal_info_get_code_model(GoalInfo, CodeModel), + ( + CodeModel \= model_non, + IsRec = non_recursive + -> % We don't need to save any variables onto the stack - % before a pragma_c_code if we know that it is not going to call - % back Mercury code, because C code won't clobber the registers. + % before a pragma_c_code if we know that it can't succeed + % more than once and that it is not going to call back + % Mercury code, because C code won't clobber the registers. + ResumeVars = ResumeVars0, LiveSets = LiveSets0 ; % The variables which need to be saved onto the stack - % before the c_code execution are all the variables that are - % live after the c_code execution, except for the output - % arguments produced by the c_code, plus all the variables - % that are nondet live at the c_code. + % before the call are all the variables that are live + % after the call, except for the output arguments produced + % by the call, plus all the variables that may be needed + % at an enclosing resumption point. find_output_vars(PredId, ProcId, Args, ModuleInfo, OutVars), - set__difference(Liveness, OutVars, LiveVars0), - set__union(LiveVars0, NondetLives, LiveVars1), + set__difference(Liveness, OutVars, InputLiveness), + set__union(InputLiveness, ResumeVars0, StackVars0), % Might need to add more live variables with accurate GC. - maybe_add_accurate_gc_typeinfos(ModuleInfo, - ProcInfo, OutVars, LiveVars1, LiveVars), + maybe_add_accurate_gc_typeinfos(ModuleInfo, + ProcInfo, OutVars, StackVars0, StackVars), - set__insert(LiveSets0, LiveVars, LiveSets) + set__insert(LiveSets0, StackVars, LiveSets), + + ( CodeModel = model_non -> + ResumeVars = StackVars % includes ResumeVars0 + ; + ResumeVars = ResumeVars0 + ) ). %-----------------------------------------------------------------------------% -:- pred build_live_sets_in_conj(list(hlds__goal), liveness_info, - set(set(var)), module_info, proc_info, - liveness_info, set(set(var))). -:- mode build_live_sets_in_conj(in, in, in, in, in, out, out) is det. +:- pred build_live_sets_in_conj(list(hlds__goal), set(var), set(var), + set(set(var)), module_info, proc_info, set(var), set(var), + set(set(var))). +:- mode build_live_sets_in_conj(in, in, in, in, in, in, out, out, out) is det. -build_live_sets_in_conj([], Liveness, LiveVars, - _ModuleInfo, _ProcInfo, Liveness, LiveVars). -build_live_sets_in_conj([Goal0 | Goals0], Liveness0, LiveVars0, - ModuleInfo, ProcInfo, Liveness, LiveVars) :- +build_live_sets_in_conj([], Liveness, ResumeVars, LiveSets, + _ModuleInfo, _ProcInfo, Liveness, ResumeVars, LiveSets). +build_live_sets_in_conj([Goal0 | Goals0], Liveness0, ResumeVars0, LiveSets0, + ModuleInfo, ProcInfo, Liveness, ResumeVars, LiveSets) :- ( Goal0 = _ - GoalInfo, goal_info_get_instmap_delta(GoalInfo, InstMapDelta), instmap_delta_is_unreachable(InstMapDelta) -> - build_live_sets_in_goal(Goal0, Liveness0, - LiveVars0, ModuleInfo, ProcInfo, - Liveness, LiveVars) + build_live_sets_in_goal(Goal0, + Liveness0, ResumeVars0, LiveSets0, ModuleInfo, ProcInfo, + Liveness, ResumeVars, LiveSets) ; - build_live_sets_in_goal(Goal0, Liveness0, - LiveVars0, ModuleInfo, ProcInfo, - Liveness1, LiveVars1), - build_live_sets_in_conj(Goals0, Liveness1, - LiveVars1, ModuleInfo, ProcInfo, - Liveness, LiveVars) + build_live_sets_in_goal(Goal0, + Liveness0, ResumeVars0, LiveSets0, ModuleInfo, ProcInfo, + Liveness1, ResumeVars1, LiveSets1), + build_live_sets_in_conj(Goals0, + Liveness1, ResumeVars1, LiveSets1, ModuleInfo, ProcInfo, + Liveness, ResumeVars, LiveSets) ). %-----------------------------------------------------------------------------% -:- pred build_live_sets_in_disj(list(hlds__goal), liveness_info, - set(set(var)), module_info, proc_info, liveness_info, +:- pred build_live_sets_in_disj(list(hlds__goal), set(var), set(var), + set(set(var)), hlds__goal_info, module_info, proc_info, + set(var), set(var), set(set(var))). +:- mode build_live_sets_in_disj(in, in, in, in, in, in, in, out, out, out) + is det. + +build_live_sets_in_disj([], Liveness, ResumeVars, LiveSets, _, _, _, + Liveness, ResumeVars, LiveSets). +build_live_sets_in_disj([Goal0 | Goals0], Liveness0, ResumeVars0, LiveSets0, + GoalInfo, ModuleInfo, ProcInfo, + Liveness, ResumeVars, LiveSets) :- + build_live_sets_in_goal(Goal0, Liveness0, ResumeVars0, LiveSets0, + ModuleInfo, ProcInfo, Liveness, ResumeVars1, LiveSets1), + build_live_sets_in_disj(Goals0, Liveness0, ResumeVars0, LiveSets1, + GoalInfo, ModuleInfo, ProcInfo, + _Liveness2, ResumeVars2, LiveSets), + goal_info_get_code_model(GoalInfo, CodeModel), + ( CodeModel = model_non -> + set__union(ResumeVars1, ResumeVars2, ResumeVars3), + goal_info_get_resume_point(GoalInfo, Resume), + ( + Resume = resume_point(ResumePointVars, _), + set__union(ResumeVars3, ResumePointVars, ResumeVars) + ; + Resume = no_resume_point, + ResumeVars = ResumeVars3 + ) + ; + ResumeVars = ResumeVars0 + ). + +%-----------------------------------------------------------------------------% + +:- pred build_live_sets_in_cases(list(case), set(var), set(var), + set(set(var)), module_info, proc_info, set(var), set(var), set(set(var))). -:- mode build_live_sets_in_disj(in, in, in, in, in, out, out) is det. +:- mode build_live_sets_in_cases(in, in, in, in, in, in, out, out, out) is det. -build_live_sets_in_disj([], Liveness, LiveSets, - _ModuleInfo, _ProcInfo, Liveness, LiveSets). -build_live_sets_in_disj([Goal0 | Goals0], Liveness0, LiveSets0, - ModuleInfo, ProcInfo, Liveness, LiveSets) :- - build_live_sets_in_goal(Goal0, Liveness0, LiveSets0, - ModuleInfo, ProcInfo, Liveness, LiveSets1), - build_live_sets_in_disj(Goals0, Liveness0, LiveSets1, - ModuleInfo, ProcInfo, _Liveness2, LiveSets). - % set__union(Liveness1, Liveness2, Liveness). - % This predicate call is unnecessary because the post-deaths and - % pre-births sets *should* be taking care of everything. - -%-----------------------------------------------------------------------------% - -:- pred build_live_sets_in_cases(list(case), liveness_info, - set(set(var)), module_info, proc_info, - liveness_info, set(set(var))). -:- mode build_live_sets_in_cases(in, in, in, in, in, out, out) is det. - -build_live_sets_in_cases([], Liveness, LiveSets, - _ModuleInfo, _ProcInfo, Liveness, LiveSets). -build_live_sets_in_cases([case(_Cons, Goal0) | Goals0], Liveness0, - LiveSets0, ModuleInfo, ProcInfo, - Liveness, LiveSets) :- - build_live_sets_in_goal(Goal0, Liveness0, LiveSets0, - ModuleInfo, ProcInfo, Liveness, LiveSets1), - build_live_sets_in_cases(Goals0, Liveness0, LiveSets1, - ModuleInfo, ProcInfo, _Liveness2, LiveSets). - % set__union(Liveness1, Liveness2, Liveness). - % This predicate call is unnecessary because the post-deaths and - % pre-births sets *should* be taking care of everything. +build_live_sets_in_cases([], Liveness, ResumeVars, LiveSets, _, _, + Liveness, ResumeVars, LiveSets). +build_live_sets_in_cases([case(_Cons, Goal0) | Goals0], + Liveness0, ResumeVars0, LiveSets0, ModuleInfo, ProcInfo, + Liveness, ResumeVars, LiveSets) :- + build_live_sets_in_goal(Goal0, Liveness0, ResumeVars0, LiveSets0, + ModuleInfo, ProcInfo, Liveness, ResumeVars1, LiveSets1), + build_live_sets_in_cases(Goals0, Liveness0, ResumeVars0, LiveSets1, + ModuleInfo, ProcInfo, _Liveness2, ResumeVars2, LiveSets), + set__union(ResumeVars1, ResumeVars2, ResumeVars). %-----------------------------------------------------------------------------% %-----------------------------------------------------------------------------% -:- pred detect_initial_liveness(proc_info, module_info, set(var)). -:- mode detect_initial_liveness(in, in, out) is det. - -detect_initial_liveness(ProcInfo, ModuleInfo, Liveness) :- - proc_info_headvars(ProcInfo, Vars), - proc_info_argmodes(ProcInfo, Modes), - proc_info_vartypes(ProcInfo, VarTypes), - map__apply_to_list(Vars, VarTypes, Types), - set__init(Liveness0), - ( - detect_initial_liveness_2(Vars, Modes, Types, ModuleInfo, - Liveness0, Liveness1) - -> - Liveness = Liveness1 - ; - error("detect_initial_liveness: list length mis-match") - ). - -:- pred detect_initial_liveness_2(list(var), list(mode), list(type), - module_info, set(var), set(var)). -:- mode detect_initial_liveness_2(in, in, in, in, in, out) is semidet. - -detect_initial_liveness_2([], [], [], _ModuleInfo, Liveness, Liveness). -detect_initial_liveness_2([V | Vs], [M | Ms], [T | Ts], ModuleInfo, - Liveness0, Liveness) :- - ( - mode_to_arg_mode(ModuleInfo, M, T, top_in) - -> - set__insert(Liveness0, V, Liveness1) - ; - Liveness1 = Liveness0 - ), - detect_initial_liveness_2(Vs, Ms, Ts, ModuleInfo, Liveness1, Liveness). - -%-----------------------------------------------------------------------------% - - % If doing accurate garbage collection, any typeinfos for - % output variables or live variables are also live. - % This is because if you want to collect, you need to - % know what shape the polymorphic args of the variables + % If doing accurate garbage collection, any typeinfos for + % output variables or live variables are also live. + % This is because if you want to collect, you need to + % know what shape the polymorphic args of the variables % are, so you need the typeinfos to be present on the stack. - % The live variables obviously need their typeinfos + % The live variables obviously need their typeinfos % live, but the output variables also need their typeinfos % saved (otherwise we would throw out typeinfos and might % need one at a continuation point just after a call). - % maybe_add_accurate_gc_typeinfos takes a set of vars - % (output vars) and a set of live vars and if we - % are doing accurate GC, add the appropriate typeinfo variables to the + % maybe_add_accurate_gc_typeinfos takes a set of vars + % (output vars) and a set of live vars and if we + % are doing accurate GC, add the appropriate typeinfo variables to the % set of variables. If not, return the live vars unchanged. % Make sure you get the output vars first, and the live vars second, @@ -431,16 +448,16 @@ detect_initial_liveness_2([V | Vs], [M | Ms], [T | Ts], ModuleInfo, set(var), set(var), set(var)). :- mode maybe_add_accurate_gc_typeinfos(in, in, in, in, out) is det. -maybe_add_accurate_gc_typeinfos(ModuleInfo, ProcInfo, OutVars, +maybe_add_accurate_gc_typeinfos(ModuleInfo, ProcInfo, OutVars, LiveVars1, LiveVars) :- module_info_globals(ModuleInfo, Globals), globals__get_gc_method(Globals, GC_Method), - ( + ( GC_Method = accurate -> - proc_info_get_used_typeinfos_setwise(ProcInfo, LiveVars1, + proc_info_get_used_typeinfos_setwise(ProcInfo, LiveVars1, TypeInfoVarsLive), - proc_info_get_used_typeinfos_setwise(ProcInfo, OutVars, + proc_info_get_used_typeinfos_setwise(ProcInfo, OutVars, TypeInfoVarsOut), set__union(LiveVars1, TypeInfoVarsOut, LiveVars2), set__union(LiveVars2, TypeInfoVarsLive, LiveVars) diff --git a/compiler/livemap.m b/compiler/livemap.m index f2561135d..6098badf9 100644 --- a/compiler/livemap.m +++ b/compiler/livemap.m @@ -3,13 +3,13 @@ % This file may only be copied under the terms of the GNU General % Public License - see the file COPYING in the Mercury distribution. %-----------------------------------------------------------------------------% - -% livemap.m +% +% File: livemap.m +% +% Main author: zs. % % This module builds up a map that gives the set of live lvals at each label. -% Author: zs. - %-----------------------------------------------------------------------------% :- module livemap. diff --git a/compiler/liveness.m b/compiler/liveness.m index 85990ee52..adc767fa2 100644 --- a/compiler/liveness.m +++ b/compiler/liveness.m @@ -3,19 +3,44 @@ % This file may only be copied under the terms of the GNU General % Public License - see the file COPYING in the Mercury distribution. %-----------------------------------------------------------------------------% - +% +% File: liveness.m +% % Main authors: conway, zs. - +% % This module traverses the goal for each procedure, and adds % liveness annotations to the goal_info for each sub-goal. - -% Note - the concept of `liveness' here is different to that -% used in the mode analysis. The mode analysis is concerned -% with the liveness of what is *pointed* to by a variable, for -% the purpose of avoiding aliasing and for structure re-use -% optimization, whereas here we are concerned with the liveness -% of the variable itself, for the purposes of minimizing stack -% slot usage and for register re-use. +% +% Note - the concept of `liveness' here is different to that used in +% mode analysis. Mode analysis is concerned with the liveness of what +% is *pointed* to by a variable, for the purpose of avoiding and/or keeping +% track of aliasing and for structure re-use optimization, whereas here +% we are concerned with the liveness of the variable itself, for the +% purposes of optimizing stack slot and register usage. +% +% We compute liveness related information in three distinct passes. +% +% The first pass, detect_liveness_in_goal, finds the first occurrence +% of each variable on each computation path. Goals containing the first +% occurrence of a variable include that variable in their pre-birth +% set. In branched structures, branches whose endpoint is not reachable +% include a post-birth set listing the variables that should have been +% born in that branch but haven't. +% +% The second pass, detect_deadness_in_goal, finds the last occurrence +% of each variable on each computation path. Goals containing the last +% occurrence of a variable include that variable in their post-death +% set. In branched structures, branches in which a variable is not +% used at all include a pre-death set listing the variables that +% have died in parallel branches. Note that when using accurate gc, +% a variable holding a typeinfo is live while any variable described +% (in whole or in part) by that typeinfo is live. +% +% The third pass, detect_resume_points_in_goal, finds goals that +% establish resume points and attaches to them a resume_point +% annotation listing the variables that may be referenced by the +% code at that resume point as well as the nature of the required +% entry labels. %-----------------------------------------------------------------------------% @@ -24,13 +49,15 @@ :- interface. :- import_module hlds_module, hlds_pred. +:- import_module io. % Add liveness annotations to the goal of the procedure. % This consists of the {pre,post}{birth,death} sets and - % (for the time being) the nondetlive set. + % resume point information. -:- pred detect_liveness_proc(proc_info, module_info, proc_info). -:- mode detect_liveness_proc(in, in, out) is det. +:- pred detect_liveness_proc(pred_id, proc_id, module_info, + proc_info, proc_info, io__state, io__state). +:- mode detect_liveness_proc(in, in, in, in, out, di, uo) is det. % Return the set of variables live at the start of the procedure. @@ -42,76 +69,51 @@ :- implementation. -:- import_module hlds_goal, llds, mode_util, quantification, instmap. -:- import_module prog_data, globals. -:- import_module list, map, set, std_util, term, assoc_list, require. +:- import_module hlds_goal, hlds_data, llds, quantification, instmap. +:- import_module hlds_out, mode_util, code_util. +:- import_module prog_data, globals, passes_aux. +:- import_module bool, list, map, set, std_util, term, assoc_list, require. +:- import_module varset, string. -%-----------------------------------------------------------------------------% +detect_liveness_proc(PredId, ProcId, ModuleInfo, ProcInfo0, ProcInfo) --> + write_proc_progress_message("% Computing liveness in ", PredId, ProcId, + ModuleInfo), + { proc_info_goal(ProcInfo0, Goal0) }, + { proc_info_variables(ProcInfo0, Varset) }, + { proc_info_vartypes(ProcInfo0, VarTypes) }, + { live_info_init(ModuleInfo, ProcInfo0, VarTypes, Varset, LiveInfo) }, - % We compute liveness related information in several distinct passes. - % - % The first pass, detect_liveness_in_goal, finds the first occurrence - % of each variable on each computation path. Goals containing the first - % occurrence of a variable include that variable in their pre-birth - % set. In branched structures, branches whose endpoint is not reachable - % include a post-birth set listing the variables that should have been - % born in that branch but haven't. - % - % The second pass, detect_deadness_in_goal, finds the last occurrence - % of each variable on each computation path. Goals containing the last - % occurrence of a variable include that variable in their post-death - % set. In branched structures, branches in which a variable is not - % used at all include a pre-death set listing the variables that - % have died in parallel branches. Note that when using accurate gc, - % a variable holding a typeinfo is live while any variable described - % (in whole or oin part) by that typeinfo is live. - % - % The third pass, detect_resume_points_in_goal, finds goals that - % establish resume points and attaches to them a resume_point - % annotation listing the variables that me be referenced by the - % code at that resume point. - % - % The fourth pass fills in the nondet-live fields. It will not - % be necessary once notes/ALLOCATION is fully implemented. + { initial_liveness(ProcInfo0, ModuleInfo, Liveness0) }, + { detect_liveness_in_goal(Goal0, Liveness0, LiveInfo, + _, Goal1) }, -detect_liveness_proc(ProcInfo0, ModuleInfo, ProcInfo) :- - proc_info_goal(ProcInfo0, Goal0), - proc_info_vartypes(ProcInfo0, VarTypes), + { initial_deadness(ProcInfo0, ModuleInfo, Deadness0) }, + { detect_deadness_in_goal(Goal1, Deadness0, LiveInfo, _, Goal2) }, - initial_liveness(ProcInfo0, ModuleInfo, Liveness0), - detect_liveness_in_goal(Goal0, Liveness0, VarTypes, ModuleInfo, - _, Goal1), + { set__init(ResumeVars0) }, + { detect_resume_points_in_goal(Goal2, Liveness0, LiveInfo, + ResumeVars0, Goal, _) }, - initial_deadness(ProcInfo0, ModuleInfo, Deadness0), - detect_deadness_in_goal(Goal1, Deadness0, ModuleInfo, ProcInfo0, - _, Goal2), - - set__init(ResumeVars0), - detect_resume_points_in_goal(Goal2, Liveness0, ResumeVars0, - Goal3, _), - - set__init(Extras0), - add_nondet_lives_to_goal(Goal3, Liveness0, Extras0, Goal, _, _), - - proc_info_set_goal(ProcInfo0, Goal, ProcInfo1), - proc_info_set_liveness_info(ProcInfo1, Liveness0, ProcInfo). + { proc_info_set_goal(ProcInfo0, Goal, ProcInfo1) }, + { proc_info_set_liveness_info(ProcInfo1, Liveness0, ProcInfo) }. %-----------------------------------------------------------------------------% %-----------------------------------------------------------------------------% -:- pred detect_liveness_in_goal(hlds__goal, liveness_info, map(var, type), - module_info, liveness_info, hlds__goal). -:- mode detect_liveness_in_goal(in, in, in, in, out, out) is det. +:- pred detect_liveness_in_goal(hlds__goal, set(var), live_info, + set(var), hlds__goal). +:- mode detect_liveness_in_goal(in, in, in, out, out) is det. + +detect_liveness_in_goal(Goal0 - GoalInfo0, Liveness0, LiveInfo, + Liveness, Goal - GoalInfo) :- -detect_liveness_in_goal(Goal0 - GoalInfo0, Liveness0, VarTypes, ModuleInfo, - Liveness, Goal - GoalInfo) :- % work out which variables get born in this goal goal_info_get_nonlocals(GoalInfo0, NonLocals), set__difference(NonLocals, Liveness0, NewVarsSet), set__to_sorted_list(NewVarsSet, NewVarsList), goal_info_get_instmap_delta(GoalInfo0, InstMapDelta), set__init(Births0), - find_binding_occurrences(NewVarsList, VarTypes, ModuleInfo, + find_binding_occurrences(NewVarsList, LiveInfo, InstMapDelta, Births0, Births), set__union(Liveness0, Births, Liveness), ( @@ -122,96 +124,67 @@ detect_liveness_in_goal(Goal0 - GoalInfo0, Liveness0, VarTypes, ModuleInfo, Goal = Goal0 ; set__init(PreBirths), - detect_liveness_in_goal_2(Goal0, Liveness0, VarTypes, - ModuleInfo, Liveness1, Goal), + detect_liveness_in_goal_2(Goal0, Liveness0, NonLocals, + LiveInfo, Liveness1, Goal), set__difference(Births, Liveness1, PostBirths) ), goal_info_set_pre_births(GoalInfo0, PreBirths, GoalInfo1), goal_info_set_post_births(GoalInfo1, PostBirths, GoalInfo). - % Here we process each of the different sorts of goals. - -%-----------------------------------------------------------------------------% - - % Given a list of variables and an instmap delta, determine - % which of those variables become bound (according to the instmap - % delta) and insert them into the accumulated set of bound vars. - -:- pred find_binding_occurrences(list(var), map(var, type), module_info, - instmap_delta, set(var), set(var)). -:- mode find_binding_occurrences(in, in, in, in, in, out) is det. - -find_binding_occurrences([], _, _, _, BoundVars, BoundVars). -find_binding_occurrences([Var | Vars], VarTypes, ModuleInfo, InstMapDelta, - BoundVars0, BoundVars) :- - map__lookup(VarTypes, Var, Type), - instmap_delta_lookup_var(InstMapDelta, Var, Inst), - ( mode_to_arg_mode(ModuleInfo, (free -> Inst), Type, top_out) -> - set__insert(BoundVars0, Var, BoundVars1) - ; - BoundVars1 = BoundVars0 - ), - find_binding_occurrences(Vars, VarTypes, ModuleInfo, InstMapDelta, - BoundVars1, BoundVars). - %-----------------------------------------------------------------------------% % Here we process each of the different sorts of goals. -:- pred detect_liveness_in_goal_2(hlds__goal_expr, liveness_info, - map(var, type), module_info, liveness_info, hlds__goal_expr). +:- pred detect_liveness_in_goal_2(hlds__goal_expr, set(var), set(var), + live_info, set(var), hlds__goal_expr). :- mode detect_liveness_in_goal_2(in, in, in, in, out, out) is det. -detect_liveness_in_goal_2(conj(Goals0), Liveness0, VarTypes, ModuleInfo, +detect_liveness_in_goal_2(conj(Goals0), Liveness0, _, LiveInfo, Liveness, conj(Goals)) :- - detect_liveness_in_conj(Goals0, Liveness0, VarTypes, ModuleInfo, - Liveness, Goals). + detect_liveness_in_conj(Goals0, Liveness0, LiveInfo, Liveness, Goals). -detect_liveness_in_goal_2(disj(Goals0, SM), Liveness0, VarTypes, ModuleInfo, +detect_liveness_in_goal_2(disj(Goals0, SM), Liveness0, NonLocals, LiveInfo, Liveness, disj(Goals, SM)) :- set__init(Union0), - detect_liveness_in_disj(Goals0, Liveness0, VarTypes, ModuleInfo, - Union0, Union, Goals), + detect_liveness_in_disj(Goals0, Liveness0, NonLocals, LiveInfo, + Union0, Union, Goals), set__union(Liveness0, Union, Liveness). -detect_liveness_in_goal_2(not(Goal0), Liveness0, VarTypes, ModuleInfo, +detect_liveness_in_goal_2(not(Goal0), Liveness0, _, LiveInfo, Liveness, not(Goal)) :- - detect_liveness_in_goal(Goal0, Liveness0, VarTypes, ModuleInfo, - Liveness, Goal). + detect_liveness_in_goal(Goal0, Liveness0, LiveInfo, Liveness, Goal). -detect_liveness_in_goal_2(switch(Var, Det, Cases0, SM), Liveness0, - VarTypes, ModuleInfo, Liveness, switch(Var, Det, Cases, SM)) :- +detect_liveness_in_goal_2(switch(Var, Det, Cases0, SM), Liveness0, NonLocals, + LiveInfo, Liveness, switch(Var, Det, Cases, SM)) :- set__init(Union0), - detect_liveness_in_cases(Cases0, Liveness0, VarTypes, ModuleInfo, - Union0, Union, Cases), + detect_liveness_in_cases(Cases0, Liveness0, NonLocals, LiveInfo, + Union0, Union, Cases), set__union(Liveness0, Union, Liveness). detect_liveness_in_goal_2(if_then_else(Vars, Cond0, Then0, Else0, SM), - Liveness0, VT, M, Liveness, + Liveness0, NonLocals, LiveInfo, Liveness, if_then_else(Vars, Cond, Then, Else, SM)) :- - detect_liveness_in_goal(Cond0, Liveness0, VT, M, LivenessCond, Cond), - detect_liveness_in_goal(Then0, LivenessCond, VT, M, LivenessThen, - Then1), - detect_liveness_in_goal(Else0, Liveness0, VT, M, LivenessElse, Else1), + detect_liveness_in_goal(Cond0, Liveness0, LiveInfo, LivenessCond, Cond), + detect_liveness_in_goal(Then0, LivenessCond, LiveInfo, LivenessThen, + Then1), + detect_liveness_in_goal(Else0, Liveness0, LiveInfo, LivenessElse, + Else1), - set__difference(LivenessThen, LivenessCond, ProducedInThen), - set__difference(LivenessElse, Liveness0, ProducedInElse), + set__union(LivenessThen, LivenessElse, Liveness), + set__intersect(Liveness, NonLocals, NonLocalLiveness), - set__difference(ProducedInElse, ProducedInThen, ResidueThen), - set__difference(ProducedInThen, ProducedInElse, ResidueElse), + set__difference(NonLocalLiveness, LivenessThen, ResidueThen), + set__difference(NonLocalLiveness, LivenessElse, ResidueElse), stuff_liveness_residue_after_goal(Then1, ResidueThen, Then), - stuff_liveness_residue_after_goal(Else1, ResidueElse, Else), + stuff_liveness_residue_after_goal(Else1, ResidueElse, Else). - set__union(LivenessThen, LivenessElse, Liveness). - -detect_liveness_in_goal_2(some(Vars, Goal0), Liveness0, VarTypes, ModuleInfo, +detect_liveness_in_goal_2(some(Vars, Goal0), Liveness0, _, LiveInfo, Liveness, some(Vars, Goal)) :- - detect_liveness_in_goal(Goal0, Liveness0, VarTypes, ModuleInfo, - Liveness, Goal). + detect_liveness_in_goal(Goal0, Liveness0, LiveInfo, Liveness, Goal). detect_liveness_in_goal_2(higher_order_call(A,B,C,D,E), L, _, _, L, - higher_order_call(A,B,C,D,E)). + higher_order_call(A,B,C,D,E)). detect_liveness_in_goal_2(call(A,B,C,D,E,F), L, _, _, L, call(A,B,C,D,E,F)). @@ -222,15 +195,14 @@ detect_liveness_in_goal_2(pragma_c_code(A,B,C,D,E,F), L, _, _, L, %-----------------------------------------------------------------------------% -:- pred detect_liveness_in_conj(list(hlds__goal), set(var), map(var, type), - module_info, set(var), list(hlds__goal)). -:- mode detect_liveness_in_conj(in, in, in, in, out, out) is det. +:- pred detect_liveness_in_conj(list(hlds__goal), set(var), live_info, + set(var), list(hlds__goal)). +:- mode detect_liveness_in_conj(in, in, in, out, out) is det. -detect_liveness_in_conj([], Liveness, _VarTypes, _ModuleInfo, Liveness, []). -detect_liveness_in_conj([Goal0 | Goals0], Liveness0, VarTypes, ModuleInfo, - Liveness, [Goal | Goals]) :- - detect_liveness_in_goal(Goal0, Liveness0, VarTypes, ModuleInfo, - Liveness1, Goal), +detect_liveness_in_conj([], Liveness, _LiveInfo, Liveness, []). +detect_liveness_in_conj([Goal0 | Goals0], Liveness0, LiveInfo, Liveness, + [Goal | Goals]) :- + detect_liveness_in_goal(Goal0, Liveness0, LiveInfo, Liveness1, Goal), ( Goal0 = _ - GoalInfo, goal_info_get_instmap_delta(GoalInfo, InstmapDelta), @@ -239,56 +211,57 @@ detect_liveness_in_conj([Goal0 | Goals0], Liveness0, VarTypes, ModuleInfo, Goals = Goals0, Liveness = Liveness1 ; - detect_liveness_in_conj(Goals0, Liveness1, VarTypes, - ModuleInfo, Liveness, Goals) + detect_liveness_in_conj(Goals0, Liveness1, LiveInfo, + Liveness, Goals) ). %-----------------------------------------------------------------------------% -:- pred detect_liveness_in_disj(list(hlds__goal), set(var), map(var, type), - module_info, set(var), set(var), list(hlds__goal)). +:- pred detect_liveness_in_disj(list(hlds__goal), set(var), set(var), + live_info, set(var), set(var), list(hlds__goal)). :- mode detect_liveness_in_disj(in, in, in, in, in, out, out) is det. -detect_liveness_in_disj([], _Liveness, _VarTypes, _ModuleInfo, Union, - Union, []). -detect_liveness_in_disj([Goal0 | Goals0], Liveness, VarTypes, ModuleInfo, - Union0, Union, [Goal | Goals]) :- - detect_liveness_in_goal(Goal0, Liveness, VarTypes, ModuleInfo, - Liveness1, Goal1), +detect_liveness_in_disj([], _Liveness, _NonLocals, _LiveInfo, + Union, Union, []). +detect_liveness_in_disj([Goal0 | Goals0], Liveness, NonLocals, LiveInfo, + Union0, Union, [Goal | Goals]) :- + detect_liveness_in_goal(Goal0, Liveness, LiveInfo, Liveness1, Goal1), set__union(Union0, Liveness1, Union1), - detect_liveness_in_disj(Goals0, Liveness, VarTypes, ModuleInfo, - Union1, Union, Goals), - set__difference(Union, Liveness1, Residue), + detect_liveness_in_disj(Goals0, Liveness, NonLocals, LiveInfo, + Union1, Union, Goals), + set__intersect(Union, NonLocals, NonLocalUnion), + set__difference(NonLocalUnion, Liveness1, Residue), stuff_liveness_residue_after_goal(Goal1, Residue, Goal). %-----------------------------------------------------------------------------% -:- pred detect_liveness_in_cases(list(case), set(var), map(var, type), - module_info, set(var), set(var), list(case)). +:- pred detect_liveness_in_cases(list(case), set(var), set(var), + live_info, set(var), set(var), list(case)). :- mode detect_liveness_in_cases(in, in, in, in, in, out, out) is det. -detect_liveness_in_cases([], _Liveness, _VarTypes, _ModuleInfo, Union, - Union, []). -detect_liveness_in_cases([case(Cons, Goal0) | Goals0], Liveness, VarTypes, - ModuleInfo, Union0, Union, [case(Cons, Goal) | Goals]) :- - detect_liveness_in_goal(Goal0, Liveness, VarTypes, ModuleInfo, - Liveness1, Goal1), +detect_liveness_in_cases([], _Liveness, _NonLocals, _LiveInfo, + Union, Union, []). +detect_liveness_in_cases([case(Cons, Goal0) | Goals0], Liveness, NonLocals, + LiveInfo, Union0, Union, [case(Cons, Goal) | Goals]) :- + detect_liveness_in_goal(Goal0, Liveness, LiveInfo, Liveness1, Goal1), set__union(Union0, Liveness1, Union1), - detect_liveness_in_cases(Goals0, Liveness, VarTypes, ModuleInfo, + detect_liveness_in_cases(Goals0, Liveness, NonLocals, LiveInfo, Union1, Union, Goals), - set__difference(Union, Liveness1, Residue), + set__intersect(Union, NonLocals, NonLocalUnion), + set__difference(NonLocalUnion, Liveness1, Residue), stuff_liveness_residue_after_goal(Goal1, Residue, Goal). %-----------------------------------------------------------------------------% %-----------------------------------------------------------------------------% -:- pred detect_deadness_in_goal(hlds__goal, liveness_info, module_info, - proc_info, liveness_info, hlds__goal). -:- mode detect_deadness_in_goal(in, in, in, in, out, out) is det. +:- pred detect_deadness_in_goal(hlds__goal, set(var), live_info, + set(var), hlds__goal). +:- mode detect_deadness_in_goal(in, in, in, out, out) is det. -detect_deadness_in_goal(Goal0 - GoalInfo0, Deadness0, ModuleInfo, ProcInfo, - Deadness, Goal - GoalInfo) :- +detect_deadness_in_goal(Goal0 - GoalInfo0, Deadness0, LiveInfo, Deadness, + Goal - GoalInfo) :- goal_info_get_nonlocals(GoalInfo0, NonLocals), + live_info_get_module_info(LiveInfo, ModuleInfo), module_info_globals(ModuleInfo, Globals), globals__get_gc_method(Globals, GCmethod), ( @@ -297,6 +270,7 @@ detect_deadness_in_goal(Goal0 - GoalInfo0, Deadness0, ModuleInfo, ProcInfo, ( GCmethod = accurate -> + live_info_get_proc_info(LiveInfo, ProcInfo), proc_info_get_used_typeinfos_setwise(ProcInfo, NonLocals, TypeInfoVars), set__union(NonLocals, TypeInfoVars, NonLocals1) @@ -312,6 +286,7 @@ detect_deadness_in_goal(Goal0 - GoalInfo0, Deadness0, ModuleInfo, ProcInfo, ( GCmethod = accurate -> + live_info_get_proc_info(LiveInfo, ProcInfo), proc_info_get_used_typeinfos_setwise(ProcInfo, NonLocals, TypeInfoVars), set__union(DeadnessNonlocals, TypeInfoVars, @@ -321,7 +296,7 @@ detect_deadness_in_goal(Goal0 - GoalInfo0, Deadness0, ModuleInfo, ProcInfo, ), set__init(PostDeaths), detect_deadness_in_goal_2(Goal0, GoalInfo0, Deadness0, - ModuleInfo, ProcInfo, Deadness1, Goal), + LiveInfo, Deadness1, Goal), set__difference(Deadness, Deadness1, PreDeaths) ), goal_info_set_post_deaths(GoalInfo0, PostDeaths, GoalInfo1), @@ -330,169 +305,418 @@ detect_deadness_in_goal(Goal0 - GoalInfo0, Deadness0, ModuleInfo, ProcInfo, % Here we process each of the different sorts of goals. :- pred detect_deadness_in_goal_2(hlds__goal_expr, hlds__goal_info, - liveness_info, module_info, proc_info, liveness_info, hlds__goal_expr). -:- mode detect_deadness_in_goal_2(in, in, in, in, in, out, out) is det. + set(var), live_info, set(var), hlds__goal_expr). +:- mode detect_deadness_in_goal_2(in, in, in, in, out, out) is det. -detect_deadness_in_goal_2(conj(Goals0), _, Deadness0, ModuleInfo, ProcInfo, - Deadness, conj(Goals)) :- - detect_deadness_in_conj(Goals0, Deadness0, ModuleInfo, ProcInfo, +detect_deadness_in_goal_2(conj(Goals0), _, Deadness0, LiveInfo, + Deadness, conj(Goals)) :- + detect_deadness_in_conj(Goals0, Deadness0, LiveInfo, Goals, Deadness). -detect_deadness_in_goal_2(disj(Goals0, SM), GoalInfo, Deadness0, ModuleInfo, - ProcInfo, Deadness, disj(Goals, SM)) :- - goal_info_get_code_model(GoalInfo, CodeModel), - ( CodeModel = model_non -> - set__init(Union0), - detect_deadness_in_nondet_disj(Goals0, Deadness0, ModuleInfo, - ProcInfo, Union0, Union, Goals), - set__union(Deadness0, Union, Deadness) - ; - detect_deadness_in_pruned_disj(Goals0, GoalInfo, - Deadness0, ModuleInfo, - ProcInfo, Goals, Deadness) - ). +detect_deadness_in_goal_2(disj(Goals0, SM), GoalInfo, Deadness0, + LiveInfo, Deadness, disj(Goals, SM)) :- + set__init(Union0), + goal_info_get_nonlocals(GoalInfo, NonLocals), + detect_deadness_in_disj(Goals0, Deadness0, NonLocals, + LiveInfo, Union0, Union, Goals), + set__union(Deadness0, Union, Deadness). -detect_deadness_in_goal_2(not(Goal0), _, Deadness0, ModuleInfo, ProcInfo, +detect_deadness_in_goal_2(not(Goal0), _, Deadness0, LiveInfo, Deadness, not(Goal)) :- - detect_deadness_in_goal(Goal0, Deadness0, ModuleInfo, ProcInfo, - Deadness, Goal). + detect_deadness_in_goal(Goal0, Deadness0, LiveInfo, Deadness, Goal). -detect_deadness_in_goal_2(if_then_else(Vars, Cond0, Then0, Else0, SM), _, - Deadness0, ModuleInfo, ProcInfo, Deadness, +detect_deadness_in_goal_2(if_then_else(Vars, Cond0, Then0, Else0, SM), + GoalInfo, Deadness0, LiveInfo, Deadness, if_then_else(Vars, Cond, Then, Else, SM)) :- - detect_deadness_in_goal(Then0, Deadness0, ModuleInfo, ProcInfo, - DeadnessThen, Then1), - detect_deadness_in_goal(Else0, Deadness0, ModuleInfo, ProcInfo, + detect_deadness_in_goal(Else0, Deadness0, LiveInfo, DeadnessElse, Else1), - set__union(DeadnessThen, DeadnessElse, DeadnessThenElse), - detect_deadness_in_goal(Cond0, DeadnessThenElse, ModuleInfo, ProcInfo, - Deadness, Cond), - set__difference(DeadnessElse, DeadnessThen, ResidueThen), - stuff_deadness_residue_before_goal(Then1, ResidueThen, Then), - set__difference(DeadnessThen, DeadnessElse, ResidueElse), + detect_deadness_in_goal(Then0, Deadness0, LiveInfo, + DeadnessThen, Then), + detect_deadness_in_goal(Cond0, DeadnessThen, LiveInfo, + DeadnessCond, Cond1), + + goal_info_get_nonlocals(GoalInfo, NonLocals), + set__union(DeadnessCond, DeadnessElse, Deadness), + set__intersect(Deadness, NonLocals, NonLocalDeadness), + + set__difference(NonLocalDeadness, DeadnessCond, ResidueCond), + set__difference(NonLocalDeadness, DeadnessElse, ResidueElse), + + stuff_deadness_residue_before_goal(Cond1, ResidueCond, Cond), stuff_deadness_residue_before_goal(Else1, ResidueElse, Else). -detect_deadness_in_goal_2(switch(Var, Det, Cases0, SM), _, Deadness0, - ModuleInfo, ProcInfo, Deadness, - switch(Var, Det, Cases, SM)) :- +detect_deadness_in_goal_2(switch(Var, Det, Cases0, SM), GoalInfo, Deadness0, + LiveInfo, Deadness, switch(Var, Det, Cases, SM)) :- set__init(Union0), - detect_deadness_in_cases(Var, Cases0, Deadness0, ModuleInfo, ProcInfo, + goal_info_get_nonlocals(GoalInfo, NonLocals), + detect_deadness_in_cases(Var, Cases0, Deadness0, NonLocals, LiveInfo, Union0, Union, Cases), set__union(Deadness0, Union, Deadness). -detect_deadness_in_goal_2(some(Vars, Goal0), _, Deadness0, ModuleInfo, - ProcInfo, Deadness, some(Vars, Goal)) :- - detect_deadness_in_goal(Goal0, Deadness0, ModuleInfo, ProcInfo, - Deadness, Goal). +detect_deadness_in_goal_2(some(Vars, Goal0), _, Deadness0, LiveInfo, + Deadness, some(Vars, Goal)) :- + detect_deadness_in_goal(Goal0, Deadness0, LiveInfo, Deadness, Goal). -detect_deadness_in_goal_2(higher_order_call(A,B,C,D,E), _, Dn, _, _, Dn, +detect_deadness_in_goal_2(higher_order_call(A,B,C,D,E), _, Dn, _, Dn, higher_order_call(A,B,C,D,E)). -detect_deadness_in_goal_2(call(A,B,C,D,E,F), _, Dn, _, _, Dn, +detect_deadness_in_goal_2(call(A,B,C,D,E,F), _, Dn, _, Dn, call(A,B,C,D,E,F)). -detect_deadness_in_goal_2(unify(A,B,C,D,E), _, Dn, _, _, Dn, unify(A,B,C,D,E)). +detect_deadness_in_goal_2(unify(A,B,C,D,E), _, Dn, _, Dn, unify(A,B,C,D,E)). -detect_deadness_in_goal_2(pragma_c_code(A,B,C,D,E,F), _, Dn, _, _, Dn, +detect_deadness_in_goal_2(pragma_c_code(A,B,C,D,E,F), _, Dn, _, Dn, pragma_c_code(A,B,C,D,E,F)). %-----------------------------------------------------------------------------% -:- pred detect_deadness_in_conj(list(hlds__goal), set(var), module_info, - proc_info, list(hlds__goal), set(var)). -:- mode detect_deadness_in_conj(in, in, in, in, out, out) is det. +:- pred detect_deadness_in_conj(list(hlds__goal), set(var), live_info, + list(hlds__goal), set(var)). +:- mode detect_deadness_in_conj(in, in, in, out, out) is det. -detect_deadness_in_conj([], Deadness, _ModuleInfo, _ProcInfo, [], Deadness). -detect_deadness_in_conj([Goal0 | Goals0], Deadness0, ModuleInfo, ProcInfo, - [Goal | Goals], Deadness) :- +detect_deadness_in_conj([], Deadness, _LiveInfo, [], Deadness). +detect_deadness_in_conj([Goal0 | Goals0], Deadness0, LiveInfo, + [Goal | Goals], Deadness) :- ( Goal0 = _ - GoalInfo, goal_info_get_instmap_delta(GoalInfo, InstmapDelta), instmap_delta_is_unreachable(InstmapDelta) -> Goals = Goals0, - detect_deadness_in_goal(Goal0, Deadness0, ModuleInfo, ProcInfo, - Deadness, Goal) + detect_deadness_in_goal(Goal0, Deadness0, LiveInfo, + Deadness, Goal) ; - detect_deadness_in_conj(Goals0, Deadness0, ModuleInfo, ProcInfo, - Goals, Deadness1), - detect_deadness_in_goal(Goal0, Deadness1, ModuleInfo, ProcInfo, - Deadness, Goal) + detect_deadness_in_conj(Goals0, Deadness0, LiveInfo, + Goals, Deadness1), + detect_deadness_in_goal(Goal0, Deadness1, LiveInfo, + Deadness, Goal) ). %-----------------------------------------------------------------------------% - % for a pruned disj, for simplicity we handle things pretty - % conservatively: we assume that none of the non-local variables - % die until the very end of the disjunction. +:- pred detect_deadness_in_disj(list(hlds__goal), set(var), set(var), + live_info, set(var), set(var), list(hlds__goal)). +:- mode detect_deadness_in_disj(in, in, in, in, in, out, out) is det. -:- pred detect_deadness_in_pruned_disj(list(hlds__goal), hlds__goal_info, - set(var), module_info, proc_info, list(hlds__goal), set(var)). -:- mode detect_deadness_in_pruned_disj(in, in, in, in, in, out, out) is det. - -detect_deadness_in_pruned_disj(Goals0, GoalInfo, Deadness0, ModuleInfo, - ProcInfo, Goals, Deadness) :- - goal_info_get_nonlocals(GoalInfo, NonLocals), - set__difference(NonLocals, Deadness0, PostDeaths), - set__union(Deadness0, PostDeaths, Deadness), - detect_deadness_in_pruned_disj_2(Goals0, Deadness, PostDeaths, - ModuleInfo, ProcInfo, Goals). - -:- pred detect_deadness_in_pruned_disj_2(list(hlds__goal), set(var), set(var), - module_info, proc_info, list(hlds__goal)). -:- mode detect_deadness_in_pruned_disj_2(in, in, in, in, in, out) is det. - -detect_deadness_in_pruned_disj_2([], _Deadness0, _PostDeaths, - _ModuleInfo, _PInfo, []). -detect_deadness_in_pruned_disj_2([Goal0 | Goals0], Deadness0, PostDeaths, - ModuleInfo, ProcInfo, [Goal | Goals]) :- - detect_deadness_in_goal(Goal0, Deadness0, ModuleInfo, ProcInfo, - _Deadness1, Goal1), - stuff_deadness_residue_after_goal(Goal1, PostDeaths, Goal), - detect_deadness_in_pruned_disj_2(Goals0, Deadness0, PostDeaths, - ModuleInfo, ProcInfo, Goals). - -%-----------------------------------------------------------------------------% - -:- pred detect_deadness_in_nondet_disj(list(hlds__goal), set(var), module_info, - proc_info, set(var), set(var), list(hlds__goal)). -:- mode detect_deadness_in_nondet_disj(in, in, in, in, in, out, out) is det. - -detect_deadness_in_nondet_disj([], _Deadness, _ModuleInfo, _PInfo, Union, - Union, []). -detect_deadness_in_nondet_disj([Goal0 | Goals0], Deadness, ModuleInfo, ProcInfo, +detect_deadness_in_disj([], _Deadness, _NonLocals, _LiveInfo, + Union, Union, []). +detect_deadness_in_disj([Goal0 | Goals0], Deadness, NonLocals, LiveInfo, Union0, Union, [Goal | Goals]) :- - detect_deadness_in_goal(Goal0, Deadness, ModuleInfo, ProcInfo, - Deadness1, Goal1), + detect_deadness_in_goal(Goal0, Deadness, LiveInfo, Deadness1, Goal1), set__union(Union0, Deadness1, Union1), - detect_deadness_in_nondet_disj(Goals0, Deadness, ModuleInfo, ProcInfo, + detect_deadness_in_disj(Goals0, Deadness, NonLocals, LiveInfo, Union1, Union, Goals), - set__difference(Union, Deadness1, Residue), + set__intersect(Union, NonLocals, NonLocalUnion), + set__difference(NonLocalUnion, Deadness1, Residue), stuff_deadness_residue_before_goal(Goal1, Residue, Goal). %-----------------------------------------------------------------------------% -:- pred detect_deadness_in_cases(var, list(case), set(var), module_info, - proc_info, set(var), set(var), list(case)). +:- pred detect_deadness_in_cases(var, list(case), set(var), set(var), + live_info, set(var), set(var), list(case)). :- mode detect_deadness_in_cases(in, in, in, in, in, in, out, out) is det. -detect_deadness_in_cases(_Var, [], _Deadness, _ModuleInfo, _ProcInfo, - Union, Union, []). +detect_deadness_in_cases(_Var, [], _Deadness, _NonLocals, _LiveInfo, + Union, Union, []). detect_deadness_in_cases(SwitchVar, [case(Cons, Goal0) | Goals0], Deadness0, - ModuleInfo, ProcInfo, Union0, Union, - [case(Cons, Goal) | Goals]) :- - detect_deadness_in_goal(Goal0, Deadness0, ModuleInfo, ProcInfo, - Deadness1, Goal1), + NonLocals, LiveInfo, Union0, Union, + [case(Cons, Goal) | Goals]) :- + detect_deadness_in_goal(Goal0, Deadness0, LiveInfo, Deadness1, Goal1), set__union(Union0, Deadness1, Union1), - detect_deadness_in_cases(SwitchVar, Goals0, Deadness0, ModuleInfo, - ProcInfo, Union1, Union2, Goals), + detect_deadness_in_cases(SwitchVar, Goals0, Deadness0, NonLocals, + LiveInfo, Union1, Union2, Goals), % If the switch variable does not become dead in a case % it must be put in the pre-death set of that case. set__insert(Union2, SwitchVar, Union), - set__difference(Union, Deadness1, Residue), + set__intersect(Union, NonLocals, NonLocalUnion), + set__difference(NonLocalUnion, Deadness1, Residue), stuff_deadness_residue_before_goal(Goal1, Residue, Goal). %-----------------------------------------------------------------------------% +%-----------------------------------------------------------------------------% + +:- pred detect_resume_points_in_goal(hlds__goal, set(var), live_info, set(var), + hlds__goal, set(var)). +:- mode detect_resume_points_in_goal(in, in, in, in, out, out) is det. + +detect_resume_points_in_goal(Goal0 - GoalInfo0, Liveness0, LiveInfo, + ResumeVars0, Goal - GoalInfo0, Liveness) :- + goal_info_pre_births(GoalInfo0, PreBirths0), + goal_info_post_births(GoalInfo0, PostBirths0), + goal_info_pre_deaths(GoalInfo0, PreDeaths0), + goal_info_post_deaths(GoalInfo0, PostDeaths0), + + set__difference(Liveness0, PreDeaths0, Liveness1), + set__union(Liveness1, PreBirths0, Liveness2), + + detect_resume_points_in_goal_2(Goal0, GoalInfo0, Liveness2, LiveInfo, + ResumeVars0, Goal, Liveness3), + + set__difference(Liveness3, PostDeaths0, Liveness4), + set__union(Liveness4, PostBirths0, Liveness). + +:- pred detect_resume_points_in_goal_2(hlds__goal_expr, hlds__goal_info, + set(var), live_info, set(var), hlds__goal_expr, set(var)). +:- mode detect_resume_points_in_goal_2(in, in, in, in, in, out, out) is det. + +detect_resume_points_in_goal_2(conj(Goals0), _, Liveness0, LiveInfo, + ResumeVars0, conj(Goals), Liveness) :- + detect_resume_points_in_conj(Goals0, Liveness0, LiveInfo, ResumeVars0, + Goals, Liveness). + +detect_resume_points_in_goal_2(disj(Goals0, SM), GoalInfo, Liveness0, LiveInfo, + ResumeVars0, disj(Goals, SM), Liveness) :- + goal_info_get_code_model(GoalInfo, CodeModel), + ( CodeModel = model_non -> + detect_resume_points_in_non_disj(Goals0, Liveness0, LiveInfo, + ResumeVars0, Goals, Liveness, _) + ; + detect_resume_points_in_pruned_disj(Goals0, Liveness0, LiveInfo, + ResumeVars0, Goals, Liveness, _) + ). + +detect_resume_points_in_goal_2(switch(Var, CF, Cases0, SM), _, Liveness0, + LiveInfo, ResumeVars0, switch(Var, CF, Cases, SM), Liveness) :- + detect_resume_points_in_cases(Cases0, Liveness0, LiveInfo, ResumeVars0, + Cases, Liveness). + +detect_resume_points_in_goal_2(if_then_else(Vars, Cond0, Then0, Else0, SM), + GoalInfo0, Liveness0, LiveInfo, ResumeVars0, + if_then_else(Vars, Cond, Then, Else, SM), LivenessThen) :- + + % compute the set of variables that may be needed at the start + % of the else part and attach this set to the condition + Else0 = _ElseExpr0 - ElseInfo0, + goal_info_pre_deaths(ElseInfo0, ElsePreDeath0), + set__difference(Liveness0, ElsePreDeath0, CondResumeVars0), + set__union(CondResumeVars0, ResumeVars0, CondResumeVars), + + ( + code_util__cannot_stack_flush(Cond0), + goal_info_get_code_model(GoalInfo0, CodeModel), + CodeModel \= model_non + -> + CondResumeLocs = orig_only + ; + code_util__cannot_fail_before_stack_flush(Cond0) + -> + CondResumeLocs = stack_only + ; + CondResumeLocs = stack_and_orig + ), + + CondResume = resume_point(CondResumeVars, CondResumeLocs), + goal_set_resume_point(Cond0, CondResume, Cond1), + + detect_resume_points_in_goal(Cond1, Liveness0, LiveInfo, + CondResumeVars, Cond, LivenessCond), + detect_resume_points_in_goal(Then0, LivenessCond, LiveInfo, + ResumeVars0, Then, LivenessThen), + detect_resume_points_in_goal(Else0, Liveness0, LiveInfo, + ResumeVars0, Else, LivenessElse), + + ( + set__equal(LivenessThen, LivenessElse) + -> + true + ; + live_info_get_varset(LiveInfo, Varset), + set__to_sorted_list(LivenessThen, ThenVarsList), + set__to_sorted_list(LivenessElse, ElseVarsList), + list__map(varset__lookup_name(Varset), + ThenVarsList, ThenVarNames), + list__map(varset__lookup_name(Varset), + ElseVarsList, ElseVarNames), + Pad = lambda([S0::in, S::out] is det, + string__append(S0, " ", S)), + list__map(Pad, ThenVarNames, PaddedThenNames), + list__map(Pad, ElseVarNames, PaddedElseNames), + string__append_list(PaddedThenNames, ThenNames), + string__append_list(PaddedElseNames, ElseNames), + string__append_list([ + "branches of if-then-else disagree on liveness\nThen: ", + ThenNames, "\nElse: ", ElseNames], Msg), + error(Msg) + ). + +detect_resume_points_in_goal_2(some(Vars, Goal0), _, Liveness0, LiveInfo, + ResumeVars0, some(Vars, Goal), Liveness) :- + detect_resume_points_in_goal(Goal0, Liveness0, LiveInfo, ResumeVars0, + Goal, Liveness). + +detect_resume_points_in_goal_2(not(Goal0), _, Liveness0, LiveInfo, ResumeVars0, + not(Goal), Liveness) :- + detect_resume_points_in_goal(Goal0, Liveness0, LiveInfo, ResumeVars0, + _, Liveness), + set__union(Liveness, ResumeVars0, ResumeVars1), + detect_resume_points_in_goal(Goal0, Liveness0, LiveInfo, ResumeVars1, + Goal1, _Liveness), + % attach the set of variables alive after the negation + % as the resume point set of the negated goal + Resume = resume_point(ResumeVars1, stack_and_orig), + goal_set_resume_point(Goal1, Resume, Goal). + +detect_resume_points_in_goal_2(higher_order_call(A,B,C,D,E), _, Liveness, _, _, + higher_order_call(A,B,C,D,E), Liveness). + +detect_resume_points_in_goal_2(call(A,B,C,D,E,F), _, Liveness, _, _, + call(A,B,C,D,E,F), Liveness). + +detect_resume_points_in_goal_2(unify(A,B,C,D,E), _, Liveness, _, _, + unify(A,B,C,D,E), Liveness). + +detect_resume_points_in_goal_2(pragma_c_code(A,B,C,D,E,F), _, Liveness, _, _, + pragma_c_code(A,B,C,D,E,F), Liveness). + +:- pred detect_resume_points_in_conj(list(hlds__goal), set(var), live_info, + set(var), list(hlds__goal), set(var)). +:- mode detect_resume_points_in_conj(in, in, in, in, out, out) is det. + +detect_resume_points_in_conj([], Liveness, _, _, [], Liveness). +detect_resume_points_in_conj([Goal0 | Goals0], Liveness0, LiveInfo, + ResumeVars0, [Goal | Goals], Liveness) :- + detect_resume_points_in_goal(Goal0, Liveness0, LiveInfo, ResumeVars0, + Goal, Liveness1), + detect_resume_points_in_conj(Goals0, Liveness1, LiveInfo, ResumeVars0, + Goals, Liveness). + + % There are only two differences in the handling of pruned disjs + % versus nondet disjs. First, for nondet disjunctions we always + % generate code for all disjuncts, whereas for pruned disjunctions + % we stop generating code after the first cannot_fail disjunct. + % Second, an empty pruned disjunction is legal, while an empty + % nondet disjunction isn't. + +:- pred detect_resume_points_in_non_disj(list(hlds__goal), set(var), + live_info, set(var), list(hlds__goal), set(var), set(var)). +:- mode detect_resume_points_in_non_disj(in, in, in, in, out, out, out) is det. + +detect_resume_points_in_non_disj([], _, _, _, _, _, _) :- + error("empty nondet disjunction"). +detect_resume_points_in_non_disj([Goal0 | Goals0], Liveness0, LiveInfo, + ResumeVars0, [Goal | Goals], Liveness, Needed) :- + ( + % If there are any more disjuncts, then this disjunct + % establishes a resumption point. + Goals0 = [_ | _] + -> + detect_resume_points_in_non_disj(Goals0, Liveness0, LiveInfo, + ResumeVars0, Goals, LivenessRest, NeededRest), + detect_resume_points_in_non_last_disjunct(Goal0, no, + Liveness0, LivenessRest, LiveInfo, ResumeVars0, Goal, + Liveness, NeededRest, Needed) + ; + detect_resume_points_in_last_disjunct(Goal0, Liveness0, + LiveInfo, ResumeVars0, Goal, Liveness, Needed), + Goals = Goals0 + ). + +:- pred detect_resume_points_in_pruned_disj(list(hlds__goal), set(var), + live_info, set(var), list(hlds__goal), set(var), set(var)). +:- mode detect_resume_points_in_pruned_disj(in, in, in, in, out, out, out) + is det. + +detect_resume_points_in_pruned_disj([], Liveness, _, _, [], Liveness, Needed) :- + set__init(Needed). +detect_resume_points_in_pruned_disj([Goal0 | Goals0], Liveness0, LiveInfo, + ResumeVars0, [Goal | Goals], Liveness, Needed) :- + Goal0 = _ - GoalInfo0, + goal_info_get_determinism(GoalInfo0, Detism0), + determinism_components(Detism0, CanFail0, _), + ( + % This disjunct establishes a resumption point only if + % there are more disjuncts *and* this one can fail. + % If there are more disjuncts but this one can't fail, + % then the code generator will ignore any later disjuncts, + % so this one will be effectively the last. + CanFail0 = can_fail, + Goals0 = [_ | _] + -> + detect_resume_points_in_pruned_disj(Goals0, Liveness0, LiveInfo, + ResumeVars0, Goals, LivenessRest, NeededRest), + detect_resume_points_in_non_last_disjunct(Goal0, yes, + Liveness0, LivenessRest, LiveInfo, ResumeVars0, Goal, + Liveness, NeededRest, Needed) + ; + detect_resume_points_in_last_disjunct(Goal0, Liveness0, + LiveInfo, ResumeVars0, Goal, Liveness, Needed), + Goals = Goals0 + ). + +:- pred detect_resume_points_in_non_last_disjunct(hlds__goal, bool, + set(var), set(var), live_info, set(var), hlds__goal, + set(var), set(var), set(var)). +:- mode detect_resume_points_in_non_last_disjunct(in, in, in, in, in, in, + out, out, in, out) is det. + +detect_resume_points_in_non_last_disjunct(Goal0, MayUseOrigOnly, + Liveness0, LivenessRest, LiveInfo, ResumeVars0, Goal, + Liveness, NeededRest, Needed) :- + % we must save a variable across this disjunct if it is + % needed in a later disjunct or in an enclosing resume point + + set__union(NeededRest, ResumeVars0, ResumeVars1), + detect_resume_points_in_goal(Goal0, Liveness0, LiveInfo, + ResumeVars1, Goal1, Liveness), + + ( + MayUseOrigOnly = yes, + code_util__cannot_stack_flush(Goal1) + -> + ResumeLocs = orig_only + ; + code_util__cannot_fail_before_stack_flush(Goal1) + -> + ResumeLocs = stack_only + ; + ResumeLocs = stack_and_orig + ), + + Resume = resume_point(ResumeVars1, ResumeLocs), + goal_set_resume_point(Goal1, Resume, Goal), + + Goal = _ - GoalInfo, + goal_info_pre_deaths(GoalInfo, PreDeaths), + set__difference(Liveness0, PreDeaths, NeededFirst), + set__union(NeededFirst, NeededRest, Needed), + + require(set__equal(Liveness, LivenessRest), + "branches of disjunction disagree on liveness"). + +:- pred detect_resume_points_in_last_disjunct(hlds__goal, set(var), live_info, + set(var), hlds__goal, set(var), set(var)). +:- mode detect_resume_points_in_last_disjunct(in, in, in, in, out, out, out) + is det. + +detect_resume_points_in_last_disjunct(Goal0, Liveness0, LiveInfo, + ResumeVars0, Goal, Liveness, Needed) :- + detect_resume_points_in_goal(Goal0, Liveness0, LiveInfo, + ResumeVars0, Goal, Liveness), + Goal = _ - GoalInfo, + goal_info_pre_deaths(GoalInfo, PreDeaths), + set__difference(Liveness0, PreDeaths, Needed). + +:- pred detect_resume_points_in_cases(list(case), set(var), live_info, + set(var), list(case), set(var)). +:- mode detect_resume_points_in_cases(in, in, in, in, out, out) is det. + +detect_resume_points_in_cases([], Liveness, _, _, [], Liveness). +detect_resume_points_in_cases([case(ConsId, Goal0) | Cases0], Liveness0, + LiveInfo, ResumeVars0, + [case(ConsId, Goal) | Cases], LivenessFirst) :- + detect_resume_points_in_goal(Goal0, Liveness0, LiveInfo, ResumeVars0, + Goal, LivenessFirst), + ( Cases0 = [_ | _] -> + detect_resume_points_in_cases(Cases0, Liveness0, LiveInfo, + ResumeVars0, Cases, LivenessRest), + require(set__equal(LivenessFirst, LivenessRest), + "branches of switch disagree on liveness") + ; + Cases = Cases0 + ). + +%-----------------------------------------------------------------------------% +%-----------------------------------------------------------------------------% initial_liveness(ProcInfo, ModuleInfo, Liveness) :- proc_info_headvars(ProcInfo, Vars), @@ -577,8 +801,7 @@ initial_deadness_2([V | Vs], [M | Ms], [T | Ts], ModuleInfo, %-----------------------------------------------------------------------------% %-----------------------------------------------------------------------------% -:- pred stuff_liveness_residue_after_goal(hlds__goal, liveness_info, - hlds__goal). +:- pred stuff_liveness_residue_after_goal(hlds__goal, set(var), hlds__goal). :- mode stuff_liveness_residue_after_goal(in, in, out) is det. stuff_liveness_residue_after_goal(Goal - GoalInfo0, Residue, Goal - GoalInfo) :- @@ -586,10 +809,7 @@ stuff_liveness_residue_after_goal(Goal - GoalInfo0, Residue, Goal - GoalInfo) :- set__union(PostBirths0, Residue, PostBirths), goal_info_set_post_births(GoalInfo0, PostBirths, GoalInfo). -%-----------------------------------------------------------------------------% - -:- pred stuff_deadness_residue_before_goal(hlds__goal, liveness_info, - hlds__goal). +:- pred stuff_deadness_residue_before_goal(hlds__goal, set(var), hlds__goal). :- mode stuff_deadness_residue_before_goal(in, in, out) is det. stuff_deadness_residue_before_goal(Goal - GoalInfo0, Residue, Goal - GoalInfo) @@ -599,421 +819,66 @@ stuff_deadness_residue_before_goal(Goal - GoalInfo0, Residue, Goal - GoalInfo) goal_info_set_pre_deaths(GoalInfo0, PreDeaths, GoalInfo). %-----------------------------------------------------------------------------% - -:- pred detect_resume_points_in_goal(hlds__goal, set(var), set(var), - hlds__goal, set(var)). -:- mode detect_resume_points_in_goal(in, in, in, out, out) is det. - -detect_resume_points_in_goal(Goal0 - GoalInfo0, Liveness0, ResumeVars0, - Goal - GoalInfo0, Liveness) :- - goal_info_pre_births(GoalInfo0, PreBirths0), - goal_info_post_births(GoalInfo0, PostBirths0), - goal_info_pre_deaths(GoalInfo0, PreDeaths0), - goal_info_post_deaths(GoalInfo0, PostDeaths0), - - set__difference(Liveness0, PreDeaths0, Liveness1), - set__union(Liveness1, PreBirths0, Liveness2), - - detect_resume_points_in_goal_2(Goal0, Liveness2, ResumeVars0, - Goal, Liveness3), - - set__difference(Liveness3, PostDeaths0, Liveness4), - set__union(Liveness4, PostBirths0, Liveness). - -:- pred detect_resume_points_in_goal_2(hlds__goal_expr, set(var), set(var), - hlds__goal_expr, set(var)). -:- mode detect_resume_points_in_goal_2(in, in, in, out, out) is det. - -detect_resume_points_in_goal_2(conj(Goals0), Liveness0, ResumeVars0, - conj(Goals), Liveness) :- - detect_resume_points_in_conj(Goals0, Liveness0, ResumeVars0, - Goals, Liveness). - -detect_resume_points_in_goal_2(disj(Goals0, SM), Liveness0, ResumeVars0, - disj(Goals, SM), Liveness) :- - detect_resume_points_in_disj(Goals0, Liveness0, ResumeVars0, - Goals, Liveness). - -detect_resume_points_in_goal_2(switch(Var, CF, Cases0, SM), Liveness0, - ResumeVars0, switch(Var, CF, Cases, SM), Liveness) :- - detect_resume_points_in_cases(Cases0, Liveness0, ResumeVars0, - Cases, Liveness). - -detect_resume_points_in_goal_2(if_then_else(Vars, Cond0, Then0, Else0, SM), - Liveness0, ResumeVars0, - if_then_else(Vars, Cond, Then, Else, SM), LivenessThen) :- - - % find out which variables die in the condition. - % these variables will eventually be in the pre-death set of the else. - % the traversal has no other effect. - detect_resume_points_in_goal(Cond0, Liveness0, ResumeVars0, - _Cond, Liveness1), - set__difference(Liveness0, Liveness1, DeathInCond), - set__difference(Liveness0, DeathInCond, LivenessStartElse), - - % compute the set of variables that may be needed at the start - % of the else part and attach this set to the condition - Else0 = _ElseExpr0 - ElseInfo0, - goal_info_pre_deaths(ElseInfo0, ElsePreDeath0), - set__difference(LivenessStartElse, ElsePreDeath0, CondResumeVars0), - set__union(CondResumeVars0, ResumeVars0, CondResumeVars), - goal_set_resume_point(Cond0, orig_and_stack(CondResumeVars), - Cond1), - - detect_resume_points_in_goal(Cond1, Liveness0, CondResumeVars, - Cond, LivenessCond), - detect_resume_points_in_goal(Then0, LivenessCond, ResumeVars0, - Then, LivenessThen), - detect_resume_points_in_goal(Else0, LivenessStartElse, ResumeVars0, - Else, LivenessElse), - require(set__equal(LivenessThen, LivenessElse), - "branches of if-then-else disagree on liveness"). - -detect_resume_points_in_goal_2(some(Vars, Goal0), Liveness0, ResumeVars0, - some(Vars, Goal), Liveness) :- - detect_resume_points_in_goal(Goal0, Liveness0, ResumeVars0, - Goal, Liveness). - -detect_resume_points_in_goal_2(not(Goal0), Liveness0, ResumeVars0, - not(Goal), Liveness) :- - detect_resume_points_in_goal(Goal0, Liveness0, ResumeVars0, - _, Liveness), - set__union(Liveness, ResumeVars0, ResumeVars1), - detect_resume_points_in_goal(Goal0, Liveness0, ResumeVars1, - Goal1, _Liveness), - % attach the set of variables alive after the negation - % as the resume point set of the negated goal - goal_set_resume_point(Goal1, orig_and_stack(ResumeVars1), Goal). - -detect_resume_points_in_goal_2(higher_order_call(A,B,C,D,E), Liveness, _, - higher_order_call(A,B,C,D,E), Liveness). - -detect_resume_points_in_goal_2(call(A,B,C,D,E,F), Liveness, _, - call(A,B,C,D,E,F), Liveness). - -detect_resume_points_in_goal_2(unify(A,B,C,D,E), Liveness, _, - unify(A,B,C,D,E), Liveness). - -detect_resume_points_in_goal_2(pragma_c_code(A,B,C,D,E,F), Liveness, _, - pragma_c_code(A,B,C,D,E,F), Liveness). - -:- pred detect_resume_points_in_conj(list(hlds__goal), set(var), set(var), - list(hlds__goal), set(var)). -:- mode detect_resume_points_in_conj(in, in, in, out, out) is det. - -detect_resume_points_in_conj([], Liveness, _, [], Liveness). -detect_resume_points_in_conj([Goal0 | Goals0], Liveness0, ResumeVars0, - [Goal | Goals], Liveness) :- - detect_resume_points_in_goal(Goal0, Liveness0, ResumeVars0, - Goal, Liveness1), - detect_resume_points_in_conj(Goals0, Liveness1, ResumeVars0, - Goals, Liveness). - -:- pred detect_resume_points_in_disj(list(hlds__goal), set(var), set(var), - list(hlds__goal), set(var)). -:- mode detect_resume_points_in_disj(in, in, in, out, out) is det. - -detect_resume_points_in_disj([], Liveness, _, [], Liveness). -detect_resume_points_in_disj([Goal0 | Goals0], Liveness0, ResumeVars0, - [Goal | Goals], LivenessFirst) :- - ( Goals0 = [_ | _] -> - % if there are any more disjuncts, then this disjunct - % establishes a resumption point, so we record the set - % of variables that may be needed at that point. - % The Liveness0 set works, but may be an overestimate. - set__union(Liveness0, ResumeVars0, ResumeVars1), - detect_resume_points_in_goal(Goal0, Liveness0, ResumeVars1, - Goal1, LivenessFirst), - goal_set_resume_point(Goal1, orig_and_stack(ResumeVars1), Goal), - detect_resume_points_in_disj(Goals0, Liveness0, ResumeVars0, - Goals, LivenessRest), - require(set__equal(LivenessFirst, LivenessRest), - "branches of disjunction disagree on liveness") - ; - detect_resume_points_in_goal(Goal0, Liveness0, ResumeVars0, - Goal, LivenessFirst), - Goals = Goals0 - ). - -:- pred detect_resume_points_in_cases(list(case), set(var), set(var), - list(case), set(var)). -:- mode detect_resume_points_in_cases(in, in, in, out, out) is det. - -detect_resume_points_in_cases([], Liveness, _, [], Liveness). -detect_resume_points_in_cases([case(ConsId, Goal0) | Cases0], Liveness0, - ResumeVars0, [case(ConsId, Goal) | Cases], LivenessFirst) :- - detect_resume_points_in_goal(Goal0, Liveness0, ResumeVars0, - Goal, LivenessFirst), - ( Cases0 = [_ | _] -> - detect_resume_points_in_cases(Cases0, Liveness0, ResumeVars0, - Cases, LivenessRest), - require(set__equal(LivenessFirst, LivenessRest), - "branches of switch disagree on liveness") - ; - Cases = Cases0 - ). - -%-----------------------------------------------------------------------------% -% -% The rest of this file should not be needed after the implementation of -% notes/ALLOCATION. -% %-----------------------------------------------------------------------------% -:- pred stuff_deadness_residue_after_goal(hlds__goal, liveness_info, - hlds__goal). -:- mode stuff_deadness_residue_after_goal(in, in, out) is det. + % Given a list of variables and an instmap delta, determine + % which of those variables become bound (according to the instmap + % delta) and insert them into the accumulated set of bound vars. -stuff_deadness_residue_after_goal(Goal0 - GoalInfo0, Residue, Goal - GoalInfo) - :- - ( goal_is_atomic(Goal0) -> - % XXX the following code is a bit dodgy... - % For atomic goals, we can't just put the deadness residue - % in the post-delta-liveness, because the code generator - % would then try to apply it prematurely in the pre_goal_update. - % Instead, we create a singleton conjunction, so that we - % can use the post-delta-liveness of the (non-atomic) - % conjunction. This technique is probably a bit fragile, - % since other parts of the compiler aren't very keen on - % singleton disjunctions. - % - % This code does NOT overwrite any part of the goal_info - % attached to the original goal. It merely uses it as a - % basis for constructing the goal_info to be attached - % to the conjunction being created. - Goal = conj([Goal0 - GoalInfo0]), - set__init(PreBirths), - set__init(PostBirths), - set__init(PreDeaths), - goal_info_set_pre_births(GoalInfo0, PreBirths, GoalInfo1), - goal_info_set_post_births(GoalInfo1, PostBirths, GoalInfo2), - goal_info_set_pre_deaths(GoalInfo2, PreDeaths, GoalInfo3), - goal_info_set_post_deaths(GoalInfo3, Residue, GoalInfo) +:- pred find_binding_occurrences(list(var), live_info, + instmap_delta, set(var), set(var)). +:- mode find_binding_occurrences(in, in, in, in, out) is det. + +find_binding_occurrences([], _, _, BoundVars, BoundVars). +find_binding_occurrences([Var | Vars], LiveInfo, InstMapDelta, + BoundVars0, BoundVars) :- + live_info_get_var_types(LiveInfo, VarTypes), + live_info_get_module_info(LiveInfo, ModuleInfo), + map__lookup(VarTypes, Var, Type), + instmap_delta_lookup_var(InstMapDelta, Var, Inst), + ( mode_to_arg_mode(ModuleInfo, (free -> Inst), Type, top_out) -> + set__insert(BoundVars0, Var, BoundVars1) ; - Goal = Goal0, - goal_info_post_deaths(GoalInfo0, PostDeaths0), - set__union(PostDeaths0, Residue, PostDeaths), - goal_info_set_post_deaths(GoalInfo0, PostDeaths, GoalInfo) - ). - -%-----------------------------------------------------------------------------% - - % traverses a goal, annotating it with information about which vars - % are live on backtracking. `Liveness' is the variables that are - % are live for the purposes of forward execution, i.e. they have - % been bound and may be referenced again (during forward execution). - % `Extras' is the variables that may be referenced on backtracking. - % Each goal is annotated with the variables that will be live on - % backtracking. - -:- pred add_nondet_lives_to_goal(hlds__goal, set(var), - set(var), hlds__goal, set(var), set(var)). -:- mode add_nondet_lives_to_goal(in, in, in, out, out, out) is det. - -add_nondet_lives_to_goal(Goal0 - GoalInfo0, Liveness0, - Extras0, Goal - GoalInfo, Liveness, Extras) :- - goal_info_pre_births(GoalInfo0, PreBirths0), - goal_info_post_births(GoalInfo0, PostBirths0), - goal_info_pre_deaths(GoalInfo0, PreDeaths0), - goal_info_post_deaths(GoalInfo0, PostDeaths0), - - set__difference(Liveness0, PreDeaths0, Liveness1), - set__union(Liveness1, PreBirths0, Liveness2), - - goal_info_get_code_model(GoalInfo0, GoalModel), - ( - GoalModel = model_non, - Goal0 = disj(_, _) - -> - % It is an invariant that for compound goals, the - % prebirths set should be empty - require(set__empty(PreBirths0), "Nonempty prebirth set in disj"), - % If the goal is a nondet disj then all the variables - % that are live at the start of the disj will be - % needed at later disjuncts (a conservative approximation) - set__union(Extras0, Liveness2, Extras1) - ; - GoalModel = model_non, - ( - Goal0 = call(_,_,_,_,_,_) - ; - Goal0 = higher_order_call(_,_,_,_,_) - ) - -> - % If the goal is a nondet call then all the variables - % that are live across the call become nondet live. - set__difference(Liveness2, PostDeaths0, LivenessAcross), - set__union(Extras0, LivenessAcross, Extras1) - ; - Extras1 = Extras0 + BoundVars1 = BoundVars0 ), - - add_nondet_lives_to_goal_2(Goal0, Liveness2, Extras1, - GoalModel, Goal, Liveness3, Extras), - - set__difference(Liveness3, PostDeaths0, Liveness4), - set__union(Liveness4, PostBirths0, Liveness), - - goal_info_set_nondet_lives(GoalInfo0, Extras1, GoalInfo). - -:- pred add_nondet_lives_to_goal_2(hlds__goal_expr, set(var), set(var), - code_model, hlds__goal_expr, set(var), set(var)). -:- mode add_nondet_lives_to_goal_2(in, in, in, in, out, out, out) is det. - -add_nondet_lives_to_goal_2(conj(Goals0), Liveness0, Extras0, _, - conj(Goals), Liveness, Extras) :- - add_nondet_lives_to_conj(Goals0, Liveness0, Extras0, - Goals, Liveness, Extras). - -add_nondet_lives_to_goal_2(disj(Goals0, SM), Liveness0, Extras0, _, - disj(Goals, SM), Liveness, Extras) :- - ExtrasAcc = Extras0, - add_nondet_lives_to_disj(Goals0, Liveness0, Extras0, - Goals, Liveness, ExtrasAcc, Extras). - -add_nondet_lives_to_goal_2(switch(Var, CF, Goals0, SM), Liveness0, Extras0, _, - switch(Var, CF, Goals, SM), Liveness, Extras) :- - ExtrasAcc = Extras0, - add_nondet_lives_to_switch(Goals0, Liveness0, Extras0, - Goals, Liveness, ExtrasAcc, Extras). - -add_nondet_lives_to_goal_2(if_then_else(Vars, Cond0, Then0, Else0, SM), - Liveness0, Extras0, _, if_then_else(Vars, Cond, Then, Else, SM), - Liveness, Extras) :- - add_nondet_lives_to_goal(Cond0, Liveness0, Extras0, - Cond, Liveness1, Extras1), - add_nondet_lives_to_goal(Then0, Liveness1, Extras1, - Then1, LivenessThen, Extras2), - add_nondet_lives_to_goal(Else0, Liveness1, Extras0, - Else1, Liveness, Extras3), - set__union(Extras2, Extras3, Extras), - - % things that become nondet live in the Else - % but not the Then and which are not live at - % the end of the Then have to become automagically - % live at the end of the Then. - set__difference(Extras, Extras2, ElseOnlyExtras0), - set__difference(ElseOnlyExtras0, LivenessThen, ElseOnlyExtras), - stuff_liveness_residue_after_goal(Then1, ElseOnlyExtras, Then), - - % things that become nondet live in the Then - % but not the Else and which are not live at the - % end of the Else have to become automagically - % live at the end of the Else. - set__difference(Extras, Extras3, ThenOnlyExtras0), - set__difference(ThenOnlyExtras0, Liveness, ThenOnlyExtras), - stuff_liveness_residue_after_goal(Else1, ThenOnlyExtras, Else). - - % Nondet lives cannot escape from a commit - % so we have to work out if this quantifier is a commit or not. -add_nondet_lives_to_goal_2(some(Vars, Goal0), Liveness0, Extras0, OuterModel, - some(Vars, Goal), Liveness, Extras) :- - add_nondet_lives_to_goal(Goal0, Liveness0, Extras0, - Goal, Liveness, Extras1), - Goal0 = _ - GoalInfo, - goal_info_get_code_model(GoalInfo, InnerModel), - ( - % is this a commit? - OuterModel \= model_non, - InnerModel = model_non - -> - % if it is, then we revert to the original - % set of nondet live variables - Extras = Extras0 - ; - Extras = Extras1 - ). - - % Nondet lives cannot escape from a negation -add_nondet_lives_to_goal_2(not(Goal0), Liveness0, Extras0, _, - not(Goal), Liveness, Extras0) :- - add_nondet_lives_to_goal(Goal0, Liveness0, Extras0, - Goal, Liveness, _). - -add_nondet_lives_to_goal_2(higher_order_call(A,B,C,D,E), Liveness, Extras, _, - higher_order_call(A,B,C,D,E), Liveness, Extras). - -add_nondet_lives_to_goal_2(call(A,B,C,D,E,F), Liveness, Extras, _, - call(A,B,C,D,E,F), Liveness, Extras). - -add_nondet_lives_to_goal_2(unify(A,B,C,D,E), Liveness, Extras, _, - unify(A,B,C,D,E), Liveness, Extras). - -add_nondet_lives_to_goal_2(pragma_c_code(A,B,C,D,E,F), Liveness, Extras, _, - pragma_c_code(A,B,C,D,E,F), Liveness, Extras). - - -:- pred add_nondet_lives_to_conj(list(hlds__goal), set(var), set(var), - list(hlds__goal), set(var), set(var)). -:- mode add_nondet_lives_to_conj(in, in, in, out, out, out) is det. - -add_nondet_lives_to_conj([], Liveness, Extras, [], Liveness, Extras). -add_nondet_lives_to_conj([G0 | Gs0], Liveness0, Extras0, - [G | Gs], Liveness, Extras) :- - add_nondet_lives_to_goal(G0, Liveness0, Extras0, G, Liveness1, Extras1), - ( - G0 = _ - GoalInfo, - goal_info_get_instmap_delta(GoalInfo, InstmapDelta), - instmap_delta_is_unreachable(InstmapDelta) - -> - Gs = Gs0, - Liveness = Liveness1, - Extras = Extras1 - ; - add_nondet_lives_to_conj(Gs0, Liveness1, Extras1, - Gs, Liveness, Extras) - ). - -:- pred add_nondet_lives_to_disj(list(hlds__goal), set(var), set(var), - list(hlds__goal), set(var), set(var), set(var)). -:- mode add_nondet_lives_to_disj(in, in, in, out, out, in, out) is det. - - % We have to add post-births of variables that become nondet - % live in some disjuncts but are neither live nor nondet live at - % the end of others. - -add_nondet_lives_to_disj([], Liveness, _Extras0, [], Liveness, Extras, Extras). - % For the last disjunct, we optimize what variables are nondet- - % live by observing that in the last disjunct we no longer need - % to save the inputs onto the stack for later disjuncts (since - % there aren't any). -add_nondet_lives_to_disj([G0], Liveness0, Extras0, - [G], Liveness, ExtrasAcc, Extras) :- - add_nondet_lives_to_goal(G0, Liveness0, Extras0, - G1, Liveness, Extras1), - set__union(ExtrasAcc, Extras1, Extras), - set__difference(ExtrasAcc, Extras1, OtherGoalExtras0), - set__difference(OtherGoalExtras0, Liveness, OtherGoalExtras), - stuff_liveness_residue_after_goal(G1, OtherGoalExtras, G). -add_nondet_lives_to_disj([G0 | Gs0], Liveness0, Extras0, - [G | Gs], Liveness, ExtrasAcc0, Extras) :- - % make this clause mutually exclusive with the previous one - Gs0 = [_ | _], - add_nondet_lives_to_goal(G0, Liveness0, Extras0, G1, Liveness, Extras2), - set__union(ExtrasAcc0, Extras2, ExtrasAcc), - add_nondet_lives_to_disj(Gs0, Liveness0, Extras0, - Gs, _Liveness1, ExtrasAcc, Extras), - set__difference(Extras, Extras2, OtherGoalExtras0), - set__difference(OtherGoalExtras0, Liveness, OtherGoalExtras), - stuff_liveness_residue_after_goal(G1, OtherGoalExtras, G). - -:- pred add_nondet_lives_to_switch(list(case), set(var), set(var), - list(case), set(var),set(var), set(var)). -:- mode add_nondet_lives_to_switch(in, in, in, out, out, in, out) is det. - -add_nondet_lives_to_switch([], Liveness, _Extras0, - [], Liveness, Extras, Extras). -add_nondet_lives_to_switch([case(ConsId, G0) | Gs0], Liveness0, Extras0, - [case(ConsId, G) | Gs], Liveness, ExtrasAcc0, Extras) :- - - add_nondet_lives_to_goal(G0, Liveness0, Extras0, G1, Liveness, Extras2), - set__union(ExtrasAcc0, Extras2, ExtrasAcc), - - add_nondet_lives_to_switch(Gs0, Liveness0, Extras0, - Gs, _Liveness1, ExtrasAcc, Extras), - set__difference(Extras, Extras2, OtherGoalExtras0), - set__difference(OtherGoalExtras0, Liveness, OtherGoalExtras), - stuff_liveness_residue_after_goal(G1, OtherGoalExtras, G). + find_binding_occurrences(Vars, LiveInfo, InstMapDelta, + BoundVars1, BoundVars). %-----------------------------------------------------------------------------% %-----------------------------------------------------------------------------% + +:- type live_info ---> live_info( + module_info, + proc_info, + map(var, type), + varset + ). + +:- pred live_info_init(module_info, proc_info, map(var, type), + varset, live_info). +:- mode live_info_init(in, in, in, in, out) is det. + +live_info_init(ModuleInfo, ProcInfo, VarTypes, Varset, + live_info(ModuleInfo, ProcInfo, VarTypes, Varset)). + +:- pred live_info_get_module_info(live_info, module_info). +:- mode live_info_get_module_info(in, out) is det. + +live_info_get_module_info(live_info(ModuleInfo, _, _, _), ModuleInfo). + +:- pred live_info_get_proc_info(live_info, proc_info). +:- mode live_info_get_proc_info(in, out) is det. + +live_info_get_proc_info(live_info(_, ProcInfo, _, _), ProcInfo). + +:- pred live_info_get_var_types(live_info, map(var, type)). +:- mode live_info_get_var_types(in, out) is det. + +live_info_get_var_types(live_info(_, _, VarTypes, _), VarTypes). + +:- pred live_info_get_varset(live_info, varset). +:- mode live_info_get_varset(in, out) is det. + +live_info_get_varset(live_info(_, _, _, Varset), Varset). + +%-----------------------------------------------------------------------------% diff --git a/compiler/lookup_switch.m b/compiler/lookup_switch.m index 3dfbff05c..522e1e987 100644 --- a/compiler/lookup_switch.m +++ b/compiler/lookup_switch.m @@ -197,8 +197,7 @@ lookup_switch__generate_constants([Case|Cases], Vars, CodeModel, { Case = case(_, int_constant(CaseTag), _, Goal) }, code_info__grab_code_info(CodeInfo), code_gen__generate_goal(CodeModel, Goal, Code), - code_info__get_live_variables(LiveList), - { set__list_to_set(LiveList, Liveness) }, + code_info__get_liveness_info(Liveness), { lookup_switch__code_is_empty(Code) }, lookup_switch__get_case_rvals(Vars, CaseRvals), { CaseVal = CaseTag - CaseRvals }, @@ -313,12 +312,16 @@ lookup_switch__generate(Var, OutVars, CaseValues, { MLiveness = no }, { error("lookup_switch__generate: no liveness!") } ), - code_info__generate_forced_saves(StoreMap, LookupCode), + code_info__generate_branch_end(model_det, StoreMap, LookupCode), % Assemble to code together { Comment = node([comment("lookup switch") - ""]) }, - { Code = tree(tree(tree(Comment, VarCode), RangeCheck), - tree(CheckBitVec, LookupCode)) }, - code_info__remake_with_store_map(StoreMap). + { Code = + tree(Comment, + tree(VarCode, + tree(RangeCheck, + tree(CheckBitVec, + LookupCode)))) + }. %------------------------------------------------------------------------------% diff --git a/compiler/mercury_compile.m b/compiler/mercury_compile.m index 71568d35f..e5c87b308 100644 --- a/compiler/mercury_compile.m +++ b/compiler/mercury_compile.m @@ -733,7 +733,7 @@ mercury_compile__backend_pass_by_preds_4(ProcInfo0, ProcId, PredId, ; { ProcInfo3 = ProcInfo2 } ), - { detect_liveness_proc(ProcInfo3, ModuleInfo1, ProcInfo4) }, + detect_liveness_proc(PredId, ProcId, ModuleInfo1, ProcInfo3, ProcInfo4), { allocate_stack_slots_in_proc(ProcInfo4, ModuleInfo1, ProcInfo5) }, { store_alloc_in_proc(ProcInfo5, ModuleInfo1, ProcInfo6) }, { module_info_get_shapes(ModuleInfo1, Shapes0) }, @@ -1268,11 +1268,11 @@ mercury_compile__maybe_followcode(HLDS0, Verbose, Stats, HLDS) --> :- mode mercury_compile__compute_liveness(in, in, in, out, di, uo) is det. mercury_compile__compute_liveness(HLDS0, Verbose, Stats, HLDS) --> - maybe_write_string(Verbose, "% Computing liveness..."), + maybe_write_string(Verbose, "% Computing liveness...\n"), maybe_flush_output(Verbose), - process_all_nonimported_procs(update_proc(detect_liveness_proc), + process_all_nonimported_procs(update_proc_io(detect_liveness_proc), HLDS0, HLDS), - maybe_write_string(Verbose, " done.\n"), + maybe_write_string(Verbose, "% done.\n"), maybe_report_stats(Stats). :- pred mercury_compile__compute_stack_vars(module_info, bool, bool, diff --git a/compiler/middle_rec.m b/compiler/middle_rec.m index 00935993d..c81a557df 100644 --- a/compiler/middle_rec.m +++ b/compiler/middle_rec.m @@ -118,42 +118,31 @@ middle_rec__generate_switch(Var, BaseConsId, Base, Recursive, StoreMap, { code_util__make_local_entry_label(ModuleInfo, PredId, ProcId, no, EntryLabel) }, - code_aux__pre_goal_update(SwitchGoalInfo, no, PreFlushCode), - - { tree__flatten(PreFlushCode, PreFlushListList) }, - { list__condense(PreFlushListList, PreFlushList) }, - { require(lambda([] is semidet, (PreFlushList = [])), - "PreFlushList is not empty in middle_rec") }, - + code_aux__pre_goal_update(SwitchGoalInfo, no), unify_gen__generate_tag_test(Var, BaseConsId, branch_on_success, BaseLabel, EntryTestCode), { tree__flatten(EntryTestCode, EntryTestListList) }, { list__condense(EntryTestListList, EntryTestList) }, code_info__grab_code_info(CodeInfo), - code_gen__generate_forced_goal(model_det, Base, StoreMap, - BaseCodeFrag), + code_gen__generate_goal(model_det, Base, BaseGoalCode), + code_info__generate_branch_end(model_det, StoreMap, BaseSaveCode), code_info__slap_code_info(CodeInfo), - code_gen__generate_forced_goal(model_det, Recursive, StoreMap, - RecCodeFrag), + code_gen__generate_goal(model_det, Recursive, RecGoalCode), + code_info__generate_branch_end(model_det, StoreMap, RecSaveCode), - code_aux__post_goal_update(SwitchGoalInfo, PostFlushCode), + code_aux__post_goal_update(SwitchGoalInfo), code_info__remake_with_store_map(StoreMap), - { tree__flatten(PostFlushCode, PostFlushListList) }, - { list__condense(PostFlushListList, PostFlushList) }, - { require(lambda([] is semidet, (PostFlushList = [])), - "PostFlushList is not empty in middle_rec") }, - code_info__get_arginfo(ArgModes), code_info__get_headvars(HeadVars), { assoc_list__from_corresponding_lists(HeadVars, ArgModes, Args) }, - code_info__setup_call(Args, callee, EpilogFrag), + code_info__setup_call(Args, callee, EpilogCode), { code_gen__output_args(Args, LiveArgs) }, - { BaseCode = tree(BaseCodeFrag, EpilogFrag) }, - { RecCode = tree(RecCodeFrag, EpilogFrag) }, + { BaseCode = tree(BaseGoalCode, tree(BaseSaveCode, EpilogCode)) }, + { RecCode = tree(RecGoalCode, tree(RecSaveCode, EpilogCode)) }, { LiveValCode = [livevals(LiveArgs) - ""] }, { tree__flatten(BaseCode, BaseListList) }, diff --git a/compiler/store_alloc.m b/compiler/store_alloc.m index 701ad2e09..31823dac3 100644 --- a/compiler/store_alloc.m +++ b/compiler/store_alloc.m @@ -61,49 +61,55 @@ store_alloc_in_proc(ProcInfo0, ModuleInfo, ProcInfo) :- proc_info_goal(ProcInfo0, Goal2) ), initial_liveness(ProcInfo0, ModuleInfo, Liveness0), - store_alloc_in_goal(Goal2, Liveness0, ModuleInfo, Goal, _Liveness), + set__init(ResumeVars0), + store_alloc_in_goal(Goal2, Liveness0, ResumeVars0, ModuleInfo, Goal, _), proc_info_set_goal(ProcInfo0, Goal, ProcInfo). %-----------------------------------------------------------------------------% -:- pred store_alloc_in_goal(hlds__goal, liveness_info, module_info, +:- pred store_alloc_in_goal(hlds__goal, liveness_info, set(var), module_info, hlds__goal, liveness_info). -:- mode store_alloc_in_goal(in, in, in, out, out) is det. +:- mode store_alloc_in_goal(in, in, in, in, out, out) is det. -store_alloc_in_goal(Goal0 - GoalInfo0, Liveness0, ModuleInfo, +store_alloc_in_goal(Goal0 - GoalInfo0, Liveness0, ResumeVars0, ModuleInfo, Goal - GoalInfo0, Liveness) :- goal_info_pre_births(GoalInfo0, PreBirths), goal_info_pre_deaths(GoalInfo0, PreDeaths), goal_info_post_births(GoalInfo0, PostBirths), goal_info_post_deaths(GoalInfo0, PostDeaths), - goal_info_nondet_lives(GoalInfo0, NondetLives0), set__difference(Liveness0, PreDeaths, Liveness1), set__union(Liveness1, PreBirths, Liveness2), - store_alloc_in_goal_2(Goal0, Liveness2, NondetLives0, - ModuleInfo, Goal1, Liveness3), + store_alloc_in_goal_2(Goal0, Liveness2, ResumeVars0, ModuleInfo, + Goal1, Liveness3), set__difference(Liveness3, PostDeaths, Liveness4), % If any variables magically become live in the PostBirths, % then they have to mundanely become live in a parallel goal, % so we don't need to allocate anything for them here. + % + % Any variables that become magically live at the end of the goal + % should not be included in the store map. set__union(Liveness4, PostBirths, Liveness), ( Goal1 = switch(Var, CanFail, Cases, FollowVars) -> - set__to_sorted_list(Liveness, LiveVars), - store_alloc_allocate_storage(LiveVars, FollowVars, StoreMap), + set__union(Liveness4, ResumeVars0, MappedSet), + set__to_sorted_list(MappedSet, MappedVars), + store_alloc_allocate_storage(MappedVars, FollowVars, StoreMap), Goal = switch(Var, CanFail, Cases, StoreMap) ; Goal1 = if_then_else(Vars, Cond, Then, Else, FollowVars) -> - set__to_sorted_list(Liveness, LiveVars), - store_alloc_allocate_storage(LiveVars, FollowVars, StoreMap), + set__union(Liveness4, ResumeVars0, MappedSet), + set__to_sorted_list(MappedSet, MappedVars), + store_alloc_allocate_storage(MappedVars, FollowVars, StoreMap), Goal = if_then_else(Vars, Cond, Then, Else, StoreMap) ; Goal1 = disj(Disjuncts, FollowVars) -> - set__to_sorted_list(Liveness, LiveVars), - store_alloc_allocate_storage(LiveVars, FollowVars, StoreMap), + set__union(Liveness4, ResumeVars0, MappedSet), + set__to_sorted_list(MappedSet, MappedVars), + store_alloc_allocate_storage(MappedVars, FollowVars, StoreMap), Goal = disj(Disjuncts, StoreMap) ; Goal = Goal1 @@ -117,44 +123,46 @@ store_alloc_in_goal(Goal0 - GoalInfo0, Liveness0, ModuleInfo, set(var), module_info, hlds__goal_expr, liveness_info). :- mode store_alloc_in_goal_2(in, in, in, in, out, out) is det. -store_alloc_in_goal_2(conj(Goals0), Liveness0, _NondetLives, ModuleInfo, +store_alloc_in_goal_2(conj(Goals0), Liveness0, ResumeVars0, ModuleInfo, conj(Goals), Liveness) :- - store_alloc_in_conj(Goals0, Liveness0, ModuleInfo, Goals, Liveness). + store_alloc_in_conj(Goals0, Liveness0, ResumeVars0, ModuleInfo, + Goals, Liveness). -store_alloc_in_goal_2(disj(Goals0, FV), Liveness0, _NondetLives, ModuleInfo, +store_alloc_in_goal_2(disj(Goals0, FV), Liveness0, ResumeVars0, ModuleInfo, disj(Goals, FV), Liveness) :- - store_alloc_in_disj(Goals0, Liveness0, ModuleInfo, Goals, Liveness). + store_alloc_in_disj(Goals0, Liveness0, ResumeVars0, ModuleInfo, + Goals, Liveness). -store_alloc_in_goal_2(not(Goal0), Liveness0, NondetLives, ModuleInfo, +store_alloc_in_goal_2(not(Goal0), Liveness0, _ResumeVars0, ModuleInfo, not(Goal), Liveness) :- - store_alloc_in_goal(Goal0, Liveness0, ModuleInfo, Goal1, Liveness), - Goal1 = GoalGoal - GoalInfo0, - set__union(Liveness, NondetLives, ContLives), - goal_info_set_cont_lives(GoalInfo0, yes(ContLives), GoalInfo), - Goal = GoalGoal - GoalInfo. + Goal0 = _ - GoalInfo0, + goal_info_get_resume_point(GoalInfo0, ResumeNot), + goal_info_resume_vars_and_loc(ResumeNot, ResumeNotVars, _), + store_alloc_in_goal(Goal0, Liveness0, ResumeNotVars, ModuleInfo, + Goal, Liveness). -store_alloc_in_goal_2(switch(Var, Det, Cases0, FV), Liveness0, _NondetLives, +store_alloc_in_goal_2(switch(Var, Det, Cases0, FV), Liveness0, ResumeVars0, ModuleInfo, switch(Var, Det, Cases, FV), Liveness) :- - store_alloc_in_cases(Cases0, Liveness0, ModuleInfo, Cases, Liveness). + store_alloc_in_cases(Cases0, Liveness0, ResumeVars0, ModuleInfo, + Cases, Liveness). store_alloc_in_goal_2(if_then_else(Vars, Cond0, Then0, Else0, FV), - Liveness0, NondetLives, ModuleInfo, + Liveness0, ResumeVars0, ModuleInfo, if_then_else(Vars, Cond, Then, Else, FV), Liveness) :- - store_alloc_in_goal(Cond0, Liveness0, ModuleInfo, Cond1, Liveness1), - Cond1 = CondGoal - GoalInfo0, - Else0 = _ElseGoal - ElseGoalInfo, - goal_info_pre_deaths(ElseGoalInfo, Deaths), - set__intersect(Liveness1, Liveness0, ContLiveness0), - set__difference(ContLiveness0, Deaths, ContLiveness), - set__union(ContLiveness, NondetLives, ContLives), - goal_info_set_cont_lives(GoalInfo0, yes(ContLives), GoalInfo), - Cond = CondGoal - GoalInfo, - store_alloc_in_goal(Then0, Liveness1, ModuleInfo, Then, Liveness), - store_alloc_in_goal(Else0, Liveness1, ModuleInfo, Else, _Liveness2). + Cond0 = _ - CondGoalInfo0, + goal_info_get_resume_point(CondGoalInfo0, ResumeCond), + goal_info_resume_vars_and_loc(ResumeCond, ResumeCondVars, _), + store_alloc_in_goal(Cond0, Liveness0, ResumeCondVars, ModuleInfo, + Cond, Liveness1), + store_alloc_in_goal(Then0, Liveness1, ResumeVars0, ModuleInfo, + Then, Liveness), + store_alloc_in_goal(Else0, Liveness0, ResumeVars0, ModuleInfo, + Else, _Liveness2). -store_alloc_in_goal_2(some(Vars, Goal0), Liveness0, _, ModuleInfo, +store_alloc_in_goal_2(some(Vars, Goal0), Liveness0, ResumeVars0, ModuleInfo, some(Vars, Goal), Liveness) :- - store_alloc_in_goal(Goal0, Liveness0, ModuleInfo, Goal, Liveness). + store_alloc_in_goal(Goal0, Liveness0, ResumeVars0, ModuleInfo, + Goal, Liveness). store_alloc_in_goal_2(higher_order_call(A, B, C, D, E), Liveness, _, _, higher_order_call(A, B, C, D, E), Liveness). @@ -170,12 +178,12 @@ store_alloc_in_goal_2(pragma_c_code(A, B, C, D, E, F), Liveness, _, _, %-----------------------------------------------------------------------------% -:- pred store_alloc_in_conj(list(hlds__goal), liveness_info, +:- pred store_alloc_in_conj(list(hlds__goal), liveness_info, set(var), module_info, list(hlds__goal), liveness_info). -:- mode store_alloc_in_conj(in, in, in, out, out) is det. +:- mode store_alloc_in_conj(in, in, in, in, out, out) is det. -store_alloc_in_conj([], Liveness, _M, [], Liveness). -store_alloc_in_conj([Goal0 | Goals0], Liveness0, ModuleInfo, +store_alloc_in_conj([], Liveness, _R, _M, [], Liveness). +store_alloc_in_conj([Goal0 | Goals0], Liveness0, ResumeVars0, ModuleInfo, [Goal | Goals], Liveness) :- ( % XXX should be threading the instmap @@ -183,39 +191,51 @@ store_alloc_in_conj([Goal0 | Goals0], Liveness0, ModuleInfo, goal_info_get_instmap_delta(GoalInfo, InstMapDelta), instmap_delta_is_unreachable(InstMapDelta) -> - store_alloc_in_goal(Goal0, Liveness0, ModuleInfo, + store_alloc_in_goal(Goal0, Liveness0, ResumeVars0, ModuleInfo, Goal, Liveness), Goals = Goals0 ; - store_alloc_in_goal(Goal0, Liveness0, ModuleInfo, + store_alloc_in_goal(Goal0, Liveness0, ResumeVars0, ModuleInfo, Goal, Liveness1), - store_alloc_in_conj(Goals0, Liveness1, ModuleInfo, + store_alloc_in_conj(Goals0, Liveness1, ResumeVars0, ModuleInfo, Goals, Liveness) ). %-----------------------------------------------------------------------------% -:- pred store_alloc_in_disj(list(hlds__goal), liveness_info, module_info, - list(hlds__goal), liveness_info). -:- mode store_alloc_in_disj(in, in, in, out, out) is det. +:- pred store_alloc_in_disj(list(hlds__goal), liveness_info, set(var), + module_info, list(hlds__goal), liveness_info). +:- mode store_alloc_in_disj(in, in, in, in, out, out) is det. -store_alloc_in_disj([], Liveness, _ModuleInfo, [], Liveness). -store_alloc_in_disj([Goal0 | Goals0], Liveness0, ModuleInfo, +store_alloc_in_disj([], Liveness, _ResumeVars0, _ModuleInfo, [], Liveness). +store_alloc_in_disj([Goal0 | Goals0], Liveness0, ResumeVars0, ModuleInfo, [Goal | Goals], Liveness) :- - store_alloc_in_goal(Goal0, Liveness0, ModuleInfo, Goal, Liveness), - store_alloc_in_disj(Goals0, Liveness0, ModuleInfo, Goals, _Liveness1). + Goal0 = _ - GoalInfo0, + goal_info_get_resume_point(GoalInfo0, ResumeGoal), + ( + ResumeGoal = no_resume_point, + ResumeGoalVars = ResumeVars0 + ; + ResumeGoal = resume_point(ResumeGoalVars, _) + ), + store_alloc_in_goal(Goal0, Liveness0, ResumeGoalVars, ModuleInfo, + Goal, Liveness), + store_alloc_in_disj(Goals0, Liveness0, ResumeVars0, ModuleInfo, + Goals, _Liveness1). %-----------------------------------------------------------------------------% -:- pred store_alloc_in_cases(list(case), liveness_info, module_info, - list(case), liveness_info). -:- mode store_alloc_in_cases(in, in, in, out, out) is det. +:- pred store_alloc_in_cases(list(case), liveness_info, set(var), + module_info, list(case), liveness_info). +:- mode store_alloc_in_cases(in, in, in, in, out, out) is det. -store_alloc_in_cases([], Liveness, _ModuleInfo, [], Liveness). -store_alloc_in_cases([case(Cons, Goal0) | Goals0], Liveness0, +store_alloc_in_cases([], Liveness, _ResumeVars0, _ModuleInfo, [], Liveness). +store_alloc_in_cases([case(Cons, Goal0) | Goals0], Liveness0, ResumeVars0, ModuleInfo, [case(Cons, Goal) | Goals], Liveness) :- - store_alloc_in_goal(Goal0, Liveness0, ModuleInfo, Goal, Liveness), - store_alloc_in_cases(Goals0, Liveness0, ModuleInfo, Goals, _Liveness1). + store_alloc_in_goal(Goal0, Liveness0, ResumeVars0, ModuleInfo, + Goal, Liveness), + store_alloc_in_cases(Goals0, Liveness0, ResumeVars0, ModuleInfo, + Goals, _Liveness1). %-----------------------------------------------------------------------------% %-----------------------------------------------------------------------------% diff --git a/compiler/string_switch.m b/compiler/string_switch.m index 4403422fb..0432e96de 100644 --- a/compiler/string_switch.m +++ b/compiler/string_switch.m @@ -122,10 +122,13 @@ string_switch__generate(Cases, Var, CodeModel, _CanFail, StoreMap, ]) }, % Collect all the generated code fragments together - { Code = tree(tree(VarCode, tree(HashLookupCode, FailCode)), - tree(JumpCode, SlotsCode)) - }, - code_info__remake_with_store_map(StoreMap). + { Code = + tree(VarCode, + tree(HashLookupCode, + tree(FailCode, + tree(JumpCode, + SlotsCode)))) + }. :- pred string_switch__hash_cases(cases_list, int, map(int, cases_list)). :- mode string_switch__hash_cases(in, in, out) is det. @@ -297,22 +300,29 @@ string_switch__gen_hash_slot(Slot, TblSize, HashSlotMap, CodeModel, StoreMap, { LabelCode = node([ label(Label) - Comment ]) }, - { FinishCode = node([ - goto(label(EndLabel)) - "jump to end of switch" - ]) }, ( { string_switch__this_is_last_case(Slot, TblSize, HashSlotMap) } -> - code_gen__generate_forced_goal(CodeModel, Goal, - StoreMap, GoalCode) + code_gen__generate_goal(CodeModel, Goal, GoalCode), + code_info__generate_branch_end(CodeModel, StoreMap, + SaveCode) ; code_info__grab_code_info(CodeInfo), - code_gen__generate_forced_goal(CodeModel, Goal, - StoreMap, GoalCode), + code_gen__generate_goal(CodeModel, Goal, GoalCode), + code_info__generate_branch_end(CodeModel, StoreMap, + SaveCode), code_info__slap_code_info(CodeInfo) ), - { Code = tree(LabelCode, tree(GoalCode, FinishCode)) } + { FinishCode = node([ + goto(label(EndLabel)) - "jump to end of switch" + ]) }, + { Code = + tree(LabelCode, + tree(GoalCode, + tree(SaveCode, + FinishCode))) + } ; { StringRval = const(int_const(0)) }, { Label = FailLabel }, diff --git a/compiler/switch_gen.m b/compiler/switch_gen.m index cbdcf077a..42343e6f9 100644 --- a/compiler/switch_gen.m +++ b/compiler/switch_gen.m @@ -59,9 +59,9 @@ :- implementation. :- import_module dense_switch, string_switch, tag_switch, lookup_switch. -:- import_module bool, int, string, list, map, tree, std_util, require. -:- import_module llds, code_gen, unify_gen, type_util, code_util. +:- import_module llds, code_gen, unify_gen, code_aux, type_util, code_util. :- import_module globals, options. +:- import_module bool, int, string, list, map, tree, std_util, require. :- type switch_category ---> atomic_switch @@ -214,22 +214,27 @@ switch_gen__priority(base_type_info_constant(_, _, _), 6).% should never occur % code to do a tag-test and fall through to the next case in % the event of failure. % - % Each case except the last consists of a tag test, followed by - % the goal for that case, followed by a branch to the end of - % the switch. The goal is generated as a "forced" goal which - % ensures that all variables which are live at the end of the - % case get stored in their stack slot. For the last case, if - % the switch is locally deterministic, then we don't need to - % generate the tag test, and we never need to generate the - % branch to the end of the switch. After the last case, we put - % the end-of-switch label which other cases branch to after - % their case goals. + % Each case except the last consists of % - % In the important special case of a det switch with two cases, - % we put the code for the second case first, since it is much - % more likely to be the recursive and therefore frequently executed - % case, while using a negated form of the test for the first case, - % since this is cheaper. + % a tag test, jumping to the next case if it fails + % the goal for that case + % code to move variables to where the store map says they + % ought to be + % a branch to the end of the switch. + % + % For the last case, if the switch covers all cases that can occur, + % we don't need to generate the tag test, and we never need to + % generate the branch to the end of the switch. + % + % After the last case, we put the end-of-switch label which other + % cases branch to after their case goals. + % + % In the important special case of a det switch with two cases, in + % which one case is a base case while the other is singly recursive, + % we put the code for the recursive case first, while using a negated + % form of the test for the base case. This form of the test is almost + % always cheaper, and putting the frequently executed case first + % minimizes the number of pipeline branches caused by taken branches. :- pred switch_gen__generate_all_cases(list(extended_case), var, code_model, can_fail, store_map, label, code_tree, code_info, code_info). @@ -242,38 +247,64 @@ switch_gen__generate_all_cases(Cases, Var, CodeModel, CanFail, StoreMap, ( { CodeModel = model_det }, { CanFail = cannot_fail }, - { Cases = [Case1, Case2] } - -> + { Cases = [Case1, Case2] }, { Case1 = case(_, _, Cons1, Goal1) }, - unify_gen__generate_tag_test(Var, Cons1, branch_on_failure, - NextLab, TestCode), - code_info__grab_code_info(CodeInfo), - code_gen__generate_forced_goal(CodeModel, Goal1, StoreMap, - Case1Code), - - { Case2 = case(_, _, _Cons2, Goal2) }, - code_info__slap_code_info(CodeInfo), - code_gen__generate_forced_goal(CodeModel, Goal2, StoreMap, - Case2Code), - + { Case2 = case(_, _, Cons2, Goal2) } + -> + =(TestCodeInfo), + { + code_aux__contains_only_builtins(Goal2), + code_aux__contains_simple_recursive_call(Goal1, + TestCodeInfo, _) + -> + RareGoal = Goal2, + FreqGoal = Goal1, + TestCons = Cons2 + ; + RareGoal = Goal1, + FreqGoal = Goal2, + TestCons = Cons1 + }, + % XXX we should be using the predicate for generating + % the tag test in a reversed form in the first place + unify_gen__generate_tag_test(Var, TestCons, branch_on_failure, + NextLabel, TestCode), { tree__flatten(TestCode, TestListList) }, { list__condense(TestListList, TestList) }, { code_util__negate_the_test(TestList, RealTestList) }, - { Code = tree( - tree( - VarCode, - tree( - node(RealTestList), - Case2Code)), - tree( - node([ - goto(label(EndLabel)) - - "skip to the end of the switch", - label(NextLab) - "next case" ]), - tree( - Case1Code, - node([ label(EndLabel) - - "End of switch" ])))) + { RealTestCode = node(RealTestList) }, + + code_info__grab_code_info(CodeInfo), + code_gen__generate_goal(CodeModel, FreqGoal, FreqGoalCode), + code_info__generate_branch_end(CodeModel, StoreMap, + FreqSaveCode), + + { MiddleCode = node([ + goto(label(EndLabel)) - + "skip to the end of the switch", + label(NextLabel) - + "next case" + ]) }, + + code_info__slap_code_info(CodeInfo), + code_gen__generate_goal(CodeModel, RareGoal, RareGoalCode), + code_info__generate_branch_end(CodeModel, StoreMap, + RareSaveCode), + + { EndCode = node([ + label(EndLabel) - + "end of switch" + ]) }, + + { Code = + tree(VarCode, + tree(RealTestCode, + tree(FreqGoalCode, + tree(FreqSaveCode, + tree(MiddleCode, + tree(RareGoalCode, + tree(RareSaveCode, + EndCode))))))) } ; switch_gen__generate_cases(Cases, Var, CodeModel, CanFail, @@ -296,38 +327,47 @@ switch_gen__generate_cases([], _Var, _CodeModel, CanFail, _StoreMap, ; { FailCode = empty } ), - { Code = tree(FailCode, node([ label(EndLabel) - - "End of switch" ])) }. + { EndCode = node([ + label(EndLabel) - + "end of switch" + ]) }, + { Code = tree(FailCode, EndCode) }. - % A case consists of a tag-test followed by a - % goal and a label for the start of the next case. -switch_gen__generate_cases([case(_, _, Cons, Goal)|Cases], Var, CodeModel, +switch_gen__generate_cases([case(_, _, Cons, Goal) | Cases], Var, CodeModel, CanFail, StoreMap, EndLabel, CasesCode) --> - code_info__grab_code_info(CodeInfo), + code_info__grab_code_info(CodeInfo0), ( { Cases = [_|_] ; CanFail = can_fail } -> unify_gen__generate_tag_test(Var, Cons, branch_on_failure, - NextLab, TestCode), - code_gen__generate_forced_goal(CodeModel, Goal, StoreMap, - ThisCode), + NextLabel, TestCode), + code_gen__generate_goal(CodeModel, Goal, GoalCode), + code_info__generate_branch_end(CodeModel, StoreMap, SaveCode), { ElseCode = node([ - goto(label(EndLabel)) - "skip to the end of the switch", - label(NextLab) - "next case" + goto(label(EndLabel)) - + "skip to the end of the switch", + label(NextLabel) - + "next case" ]) }, - { ThisCaseCode = tree(tree(TestCode, ThisCode), ElseCode) }, + { ThisCaseCode = + tree(TestCode, + tree(GoalCode, + tree(SaveCode, + ElseCode))) + }, code_info__grab_code_info(CodeInfo1), - code_info__slap_code_info(CodeInfo) + code_info__slap_code_info(CodeInfo0) ; - code_gen__generate_forced_goal(CodeModel, Goal, StoreMap, - ThisCaseCode), + code_gen__generate_goal(CodeModel, Goal, GoalCode), + code_info__generate_branch_end(CodeModel, StoreMap, SaveCode), + { ThisCaseCode = tree(GoalCode, SaveCode) }, code_info__grab_code_info(CodeInfo1), - code_info__slap_code_info(CodeInfo) + code_info__slap_code_info(CodeInfo0) ), % generate the rest of the cases. switch_gen__generate_cases(Cases, Var, CodeModel, CanFail, StoreMap, - EndLabel, CasesCode0), - { CasesCode = tree(ThisCaseCode, CasesCode0) }, + EndLabel, OtherCasesCode), + { CasesCode = tree(ThisCaseCode, OtherCasesCode) }, code_info__slap_code_info(CodeInfo1). %------------------------------------------------------------------------------% diff --git a/compiler/tag_switch.m b/compiler/tag_switch.m index 68d6dd08c..34a0cf8af 100644 --- a/compiler/tag_switch.m +++ b/compiler/tag_switch.m @@ -208,8 +208,10 @@ tag_switch__generate_primary_tag_code(GoalMap, Primary, MaxSecondary, StagLoc, { StagLoc = none } -> ( { GoalList = [-1 - Goal] } -> - code_gen__generate_forced_goal(CodeModel, Goal, - StoreMap, Code) + code_gen__generate_goal(CodeModel, Goal, GoalCode), + code_info__generate_branch_end(CodeModel, StoreMap, + SaveCode), + { Code = tree(GoalCode, SaveCode) } ; { error("more than one goal for non-shared tag") } ) @@ -272,8 +274,10 @@ tag_switch__generate_primary_tag_code(GoalMap, Primary, MaxSecondary, StagLoc, tag_switch__generate_secondary_tag_chain([], _SecTagRval, _CodeModel, CanFail, _StoreMap, _EndLabel, FailLabel, Code) --> ( { CanFail = can_fail } -> - { Code = node([goto(label(FailLabel)) - - "secondary tag does not match"]) } + { Code = node([ + goto(label(FailLabel)) - + "secondary tag does not match" + ]) } ; { Code = empty } ). @@ -286,20 +290,28 @@ tag_switch__generate_secondary_tag_chain([Case0 | Cases0], SecTagRval, { TestCode = node([if_val(binop(ne, SecTagRval, const(int_const(Secondary))), label(ElseLabel)) - "test remote sec tag only"]) }, - code_gen__generate_forced_goal(CodeModel, Goal, StoreMap, - GoalCode), + code_gen__generate_goal(CodeModel, Goal, GoalCode), + code_info__generate_branch_end(CodeModel, StoreMap, SaveCode), { ElseCode = node([ - goto(label(EndLabel)) - "skip to end of tag switch", - label(ElseLabel) - "handle next secondary tag"]) }, - { ThisCode = tree(TestCode, tree(GoalCode, ElseCode)) }, + goto(label(EndLabel)) - + "skip to end of tag switch", + label(ElseLabel) - + "handle next secondary tag" + ]) }, + { ThisCode = + tree(TestCode, + tree(GoalCode, + tree(SaveCode, + ElseCode))) }, ( { Cases0 = [_|_] } -> code_info__slap_code_info(CodeInfo) ; [] ) ; - code_gen__generate_forced_goal(CodeModel, Goal, StoreMap, - ThisCode) + code_gen__generate_goal(CodeModel, Goal, GoalCode), + code_info__generate_branch_end(CodeModel, StoreMap, SaveCode), + { ThisCode = tree(GoalCode, SaveCode) } ), tag_switch__generate_secondary_tag_chain(Cases0, SecTagRval, CodeModel, CanFail, StoreMap, EndLabel, FailLabel, OtherCode), @@ -330,26 +342,39 @@ tag_switch__generate_secondary_tag_table(CaseList, CurSecondary, MaxSecondary, { NextSecondary is CurSecondary + 1 }, ( { CaseList = [CurSecondary - Goal | CaseList1] } -> code_info__get_next_label(NewLabel), + { LabelCode = node([ + label(NewLabel) - + "start of a case in tag switch" + ]) }, ( { CaseList1 = [] } -> - code_gen__generate_forced_goal(CodeModel, Goal, - StoreMap, GoalCode) + code_gen__generate_goal(CodeModel, Goal, + GoalCode), + code_info__generate_branch_end(CodeModel, + StoreMap, SaveCode) ; code_info__grab_code_info(CodeInfo), - code_gen__generate_forced_goal(CodeModel, Goal, - StoreMap, GoalCode), + code_gen__generate_goal(CodeModel, Goal, + GoalCode), + code_info__generate_branch_end(CodeModel, + StoreMap, SaveCode), code_info__slap_code_info(CodeInfo) ), - { LabelCode = node([label(NewLabel) - - "start of a case in tag switch"]) }, - { GotoCode = node([goto(label(EndLabel)) - - "branch to end of tag switch"]) }, + { GotoCode = node([ + goto(label(EndLabel)) - + "branch to end of tag switch" + ]) }, tag_switch__generate_secondary_tag_table(CaseList1, NextSecondary, MaxSecondary, CodeModel, StoreMap, EndLabel, FailLabel, OtherLabels, OtherCode), { Labels = [NewLabel | OtherLabels] }, - { Code = tree(tree(LabelCode, GoalCode), - tree(GotoCode, OtherCode)) } + { Code = + tree(LabelCode, + tree(GoalCode, + tree(SaveCode, + tree(GotoCode, + OtherCode)))) + } ; tag_switch__generate_secondary_tag_table(CaseList, NextSecondary, MaxSecondary, CodeModel,