From 08a5f48e2c20f97c9964f26dd5fe7e7f6fe74f91 Mon Sep 17 00:00:00 2001 From: Zoltan Somogyi Date: Wed, 18 Dec 1996 08:56:10 +0000 Subject: [PATCH] Take the code generator a big step closer to notes/ALLOCATION. Estimated hours taken: _____ Take the code generator a big step closer to notes/ALLOCATION. The new code generator emits code that is smaller and faster than the code we used to emit. Nondet liveness is no longer used; nondet live sets are always empty. In code that was being modified anyway, remove its handling. Other uses will be removed later (this keeps this change from being far too big; as it is it is merely too big). Similarly for cont-lives. In several places, clarify the code that gathers several code pieces together. call_gen: Unset the failure continuation and flush the resume vars to their stack slots before nondet calls. Move the code that decides whether a nondet call can be a tailcall to code_info. code_aux: Remove the code to handle resume points, since these are now handled in the specific constructs that need them. Replace it with a sanity check. code_exprn: Add a predicate to place multiple vars. code_gen: Remove the predicate code_gen__generate_forced_goal, since it packaged together some operations that should be executed at different times. Don't unset the failure continuation after every nondet goal; this is now done in the constructs that need it. Modify the handling of negation to use resume point info according to notes/ALLOCATION. Remove the predicate code_gen__ensure_vars_are_saved which was use to save all lives variables to the stack before nondet disjunctions and if-then-elses; we don't do that anymore. code_info: Significantly simplify and document the handling of failure continuations, and make the types involved abstract types. Factor out common code in the handling of det and semi commits. Keep track of "zombies", variables that are dead wrt forward execution but whose values we need because they may be needed at a resume point we can reach. Remove several now unneeded predicates, and introduce new predicates to help other modules. code_util: Add a couple of predicates to check whether ia goal cannot fail before flushing all variables to the stack, and whether a goal cannot flush any variables to the stack. These are used in liveness to decide which entry labels will be needed at resume points. disj_gen: Unify the handling of det and semi disjunctions. Model the code handling of nondet disjunctions on the code handling pruned disjunctions. It is possible that the handling of nondet and pruned disjunctions can also be unified; the new code should make this significantly easier. Make the code conform to notes/ALLOCATION. This means saving only the variables mentioned in the resume_point field, not flushing all live variables to the stack at the start of a nondet disjunction, handling zombies, and using the new method of flushing variables at the ends of branched structures. ite_gen: Unify the handling of det and semi if-then-elses. Model the code handling of nondet if-then-elses on the code handling det/semi if-then-elses. It is possible that the handling of nondet and pruned if-then-elses can also be unified; the new code should make this significantly easier. Make the code conform to notes/ALLOCATION. This means saving only the variables mentioned in the resume_point field, not flushing all live variables to the stack at the start of a nondet if-then-else, handling zombies, and using the new method of flushing variables at the ends of branched structures. Apply the new rules about liveness in if-then-elses, which say that the else part is parallel not to the then part but to the conjunction of the condition and the then part. dense_switch, lookup_switch, string_switch, switch_gen, tag_switch, middle_rec: Use the new method of flushing variables at the ends of branched structures. Don't call remake_with_store map; switch_gen will do so. Fix an old bug in lookup_switch. The code in switch_gen which looked for the special case of a two-way switch used to use a heuristic to decide which one was recursive and which one was a base case. We now check the codes of the cases. hlds_goal: Adjust the structure of the resume_point field to make it easier to use. Add a more convenient access predicate. hlds_out: Don't print the nondet liveness and cont live fields, since they are not used anymore. Comment out the printing of the context field, which is rarely useful. Modify the printing of the resume_point field to conform to its new definition. live_vars: Use the resume_point field, not the nondetlives field, to decide which variables may be needed on backward execution. Remove some code copied from liveness.m. liveness: Put the several pieces of information we thread through the traversal predicates into a single tuple. Don't put variables which are local to one branch of a branched structure into the post-birth sets of other branches. Apply the new rules about liveness in if-then-elses, which say that the else part is parallel not to the then part but to the conjunction of the condition and the then part. Variables that are needed in the else part but not in the condition or the then part now die in at the start of the condition (they will be protected by the resume point on the condition). We now treat pruned and non-pruned disjunctions the same way wrt deadness; the old way was too conservative (it had to be). We still mishandle branches which produce some variables but can't succeed. mercury_compile: Liveness now prints its own progress message with -V; support this. store_alloc: When figuring out what variables need to be saved across calls, make sure that we put in interference arcs between those variables and those that are required by enclosing resume points. Don't compute cont-lives, since they are not used anymore. livemap: Fix the starting comment. --- compiler/call_gen.m | 84 ++- compiler/code_aux.m | 53 +- compiler/code_exprn.m | 15 + compiler/code_gen.m | 175 ++--- compiler/code_info.m | 1406 ++++++++++++++++++++---------------- compiler/code_util.m | 90 +++ compiler/dense_switch.m | 14 +- compiler/disj_gen.m | 564 +++++++-------- compiler/hlds_goal.m | 29 +- compiler/hlds_out.m | 82 +-- compiler/ite_gen.m | 478 ++++++------ compiler/live_vars.m | 495 +++++++------ compiler/livemap.m | 8 +- compiler/liveness.m | 1239 ++++++++++++++----------------- compiler/lookup_switch.m | 15 +- compiler/mercury_compile.m | 8 +- compiler/middle_rec.m | 29 +- compiler/store_alloc.m | 140 ++-- compiler/string_switch.m | 34 +- compiler/switch_gen.m | 166 +++-- compiler/tag_switch.m | 67 +- 21 files changed, 2650 insertions(+), 2541 deletions(-) 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,