Files
mercury/compiler/ite_gen.m
Fergus Henderson 4be69fa961 Eliminated a lot of the dependencies on the the `code_model' type,
Estimated hours taken: 6

Eliminated a lot of the dependencies on the the `code_model' type,
and move that type from llds.m into a new module `code_model'.
The aim of this change is to improve the modularity of the compiler by
reducing the number of places in the compiler front-end that depend
on back-end concepts and the number of places in the MLDS back-end
which depend on the LLDS.

compiler/code_model.m:
	New module.  Contains the code_model type and associated
	procedures.

compiler/llds.m:
	Move the code_model type into code_model.m.

compiler/hlds_goal.m:
	Move the goal_info_get_code_model procedure into code_model.m,
	to avoid having the HLDS modules import code_model.

compiler/hlds_out.m:
	Delete `hlds_out__write_code_model', since it wasn't being used.

compiler/hlds_pred.m:
	Move the proc_info_interface_code_model procedure into code_model.m,
	to avoid having the HLDS modules import code_model.

compiler/goal_path.m:
	When computing the `maybe_cut' field for `some' goals,
	compute it by comparing the determinism rather than by
	comparing the goal_infos.

compiler/unique_modes.m:
	Use determinism and test for soln_count = at_most_many
	rather than using code_model and testing for model_non.

compiler/inlining.m:
	Test for determinism nondet/multi rather than testing
	for code_model model_non.

compiler/hlds_pred.m:
compiler/det_report.m:
	Change valid_code_model_for_eval_method, which succeeded unless
	the eval_method was minimal_model and the code_model was model_det,
	to valid_determinism_for_eval_method, which succeeds unless the
	eval_method is minimal_model and the determinism cannot fail.
	As well as avoiding a dependency on code_model in the HLDS
	modules, this also fixes a bug where det_report could give
	misleading error messages, saying that `multi' was a valid
	determinism for `minimal_model' predicates, when in fact the
	compiler will always report a determinism error if you declare
	a `minimal_model' predicate with determinism `multi'.
	(Actually the code in which this bug occurs is in fact
	unreachable, but this is no doubt also a bug... I'll address
	that one in a separate change.)

compiler/lookup_switch.m:
	Simplify the code a bit by using globals__lookup_*_option
	rather than globals__get_option and then getopt__lookup_option.

compiler/*.m:
	Add `import_module' declarations for `code_model', and in some
	cases remove `import_module' declarations for `llds'.
2000-11-23 04:32:51 +00:00

361 lines
11 KiB
Mathematica

%---------------------------------------------------------------------------%
% Copyright (C) 1994-2000 The University of Melbourne.
% This file may only be copied under the terms of the GNU General
% Public License - see the file COPYING in the Mercury distribution.
%---------------------------------------------------------------------------%
%
% File: ite_gen.m
%
% Main authors: conway, fjh, zs.
%
% The predicates of this module generate code for if-then-elses, and for
% negations (which are cut-down versions of if-then-elses, since not(G)
% is equivalent to (G -> fail ; true)).
%
%---------------------------------------------------------------------------%
:- module ite_gen.
:- interface.
:- import_module hlds_goal, code_model, llds, code_info.
:- pred ite_gen__generate_ite(code_model::in, hlds_goal::in, hlds_goal::in,
hlds_goal::in, store_map::in, code_tree::out,
code_info::in, code_info::out) is det.
:- pred ite_gen__generate_negation(code_model::in, hlds_goal::in,
code_tree::out, code_info::in, code_info::out) is det.
%---------------------------------------------------------------------------%
:- implementation.
:- import_module prog_data, tree, builtin_ops.
:- import_module code_gen, code_util, trace, options, globals, instmap.
:- import_module bool, set, term, list, map, std_util, require.
ite_gen__generate_ite(CodeModel, CondGoal0, ThenGoal, ElseGoal, StoreMap, Code)
-->
{ CondGoal0 = CondExpr - CondInfo0 },
{ goal_info_get_code_model(CondInfo0, CondCodeModel) },
{
CodeModel = model_non,
CondCodeModel \= model_non
->
EffCodeModel = model_semi
;
EffCodeModel = CodeModel
},
{ goal_info_get_resume_point(CondInfo0, Resume) },
{
Resume = resume_point(ResumeVarsPrime, ResumeLocsPrime)
->
ResumeVars = ResumeVarsPrime,
ResumeLocs = ResumeLocsPrime,
% The pre_goal_update sanity check insists on
% no_resume_point, to make sure that all resume
% points have been handled by surrounding code.
goal_info_set_resume_point(CondInfo0, no_resume_point,
CondInfo),
CondGoal = CondExpr - CondInfo
;
error("condition of an if-then-else has no resume point")
},
% Make sure that the variables whose values will be needed
% on backtracking to the else part are materialized into
% registers or stack slots. Their locations are recorded
% in ResumeMap.
code_info__produce_vars(ResumeVars, ResumeMap, FlushCode),
% Maybe save the heap state current before the condition.
% This is after code_info__produce_vars since code that
% flushes the cache may allocate memory we must not "recover".
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, MaybeHpSlot),
% Maybe save the current trail state before the condition
{ globals__lookup_bool_option(Globals, use_trail, UseTrail) },
code_info__maybe_save_ticket(UseTrail, SaveTicketCode,
MaybeTicketSlot),
code_info__remember_position(BranchStart),
code_info__prepare_for_ite_hijack(EffCodeModel, HijackInfo,
PrepareHijackCode),
code_info__make_resume_point(ResumeVars, ResumeLocs, ResumeMap,
ResumePoint),
code_info__effect_resume_point(ResumePoint, EffCodeModel,
EffectResumeCode),
% Generate the condition
trace__maybe_generate_internal_event_code(CondGoal, CondTraceCode),
code_gen__generate_goal(CondCodeModel, CondGoal, CondCode),
code_info__ite_enter_then(HijackInfo, ThenNeckCode, ElseNeckCode),
% Kill again any variables that have become zombies
code_info__pickup_zombies(Zombies),
code_info__make_vars_forward_dead(Zombies),
% Discard hp and prune trail ticket if the condition succeeded
( { CondCodeModel = model_non } ->
% We cannot release the stack slots used for the heap pointer
% and the trail ticket if the condition can be backtracked
% into. Nor can we prune the trail ticket that we allocated,
% since the condition may have allocated other trail tickets
% since then which have not yet been pruned.
code_info__maybe_reset_ticket(
MaybeTicketSlot, solve, ResetTicketCode)
;
code_info__maybe_release_hp(MaybeHpSlot),
code_info__maybe_reset_prune_and_release_ticket(
MaybeTicketSlot, commit, ResetTicketCode)
),
code_info__get_instmap(EndCondInstMap),
( { instmap__is_unreachable(EndCondInstMap) } ->
% If the instmap indicates we cannot reach the then part,
% do not attempt to generate it (may cause aborts).
{ ThenTraceCode = empty },
{ ThenCode = empty },
{ map__init(EmptyStoreMap) },
code_info__generate_branch_end(EmptyStoreMap, no,
MaybeEnd0, ThenSaveCode)
;
% Generate the then branch
trace__maybe_generate_internal_event_code(ThenGoal,
ThenTraceCode),
code_gen__generate_goal(CodeModel, ThenGoal, ThenCode),
code_info__generate_branch_end(StoreMap, no,
MaybeEnd0, ThenSaveCode)
),
% Generate the entry to the else branch
code_info__reset_to_position(BranchStart),
code_info__generate_resume_point(ResumePoint, ResumeCode),
% Restore the heap pointer and solver state if necessary.
code_info__maybe_restore_and_release_hp(MaybeHpSlot, RestoreHpCode),
code_info__maybe_reset_discard_and_release_ticket(
MaybeTicketSlot, undo, RestoreTicketCode),
% Generate the else branch
trace__maybe_generate_internal_event_code(ElseGoal, ElseTraceCode),
code_gen__generate_goal(CodeModel, ElseGoal, ElseCode),
code_info__generate_branch_end(StoreMap, MaybeEnd0, MaybeEnd,
ElseSaveCode),
code_info__get_next_label(EndLabel),
{ JumpToEndCode = node([
goto(label(EndLabel))
- "Jump to the end of if-then-else"
]) },
{ EndLabelCode = node([
label(EndLabel)
- "end of if-then-else"
]) },
{ Code =
tree(FlushCode,
tree(SaveHpCode,
tree(SaveTicketCode,
tree(PrepareHijackCode,
tree(EffectResumeCode,
tree(CondTraceCode,
tree(CondCode,
tree(ThenNeckCode,
tree(ResetTicketCode,
tree(ThenTraceCode,
tree(ThenCode,
tree(ThenSaveCode,
tree(JumpToEndCode,
tree(ResumeCode,
tree(ElseNeckCode,
tree(RestoreHpCode,
tree(RestoreTicketCode,
tree(ElseTraceCode,
tree(ElseCode,
tree(ElseSaveCode,
EndLabelCode))))))))))))))))))))
},
code_info__after_all_branches(StoreMap, MaybeEnd).
%---------------------------------------------------------------------------%
ite_gen__generate_negation(CodeModel, Goal0, Code) -->
{ CodeModel = model_non ->
error("nondet negation")
;
true
},
{ Goal0 = GoalExpr - GoalInfo0 },
{ goal_info_get_resume_point(GoalInfo0, Resume) },
{
Resume = resume_point(ResumeVarsPrime, ResumeLocsPrime)
->
ResumeVars = ResumeVarsPrime,
ResumeLocs = ResumeLocsPrime,
goal_info_set_resume_point(GoalInfo0, no_resume_point,
GoalInfo),
Goal = GoalExpr - GoalInfo
;
error("negated goal has no resume point")
},
% 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__failure_is_direct_branch(CodeAddr),
code_info__get_globals(Globals),
{ globals__lookup_bool_option(Globals, simple_neg, yes) }
->
% Because we are generating the negated goal ourselves,
% we need to apply the pre- and post-goal updates
% that would normally be applied by
% code_gen__generate_goal.
code_info__enter_simple_neg(ResumeVars, GoalInfo, SimpleNeg),
code_info__produce_variable(L, CodeL, ValL),
code_info__produce_variable(R, CodeR, ValR),
code_info__variable_type(L, Type),
{ Type = term__functor(term__atom("string"), [], _) ->
Op = str_eq
; Type = term__functor(term__atom("float"), [], _) ->
Op = float_eq
;
Op = eq
},
{ TestCode = node([
if_val(binop(Op, ValL, ValR), CodeAddr) -
"test inequality"
]) },
code_info__leave_simple_neg(GoalInfo, SimpleNeg),
{ Code = tree(tree(CodeL, CodeR), TestCode) }
;
generate_negation_general(CodeModel, Goal,
ResumeVars, ResumeLocs, Code)
).
% The code of generate_negation_general is a cut-down version
% of the code for if-then-elses.
:- pred generate_negation_general(code_model::in, hlds_goal::in,
set(prog_var)::in, resume_locs::in, code_tree::out,
code_info::in, code_info::out) is det.
generate_negation_general(CodeModel, Goal, ResumeVars, ResumeLocs, Code) -->
code_info__produce_vars(ResumeVars, ResumeMap, FlushCode),
% 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(Goal)
->
ReclaimHeap = yes
;
ReclaimHeap = no
},
code_info__maybe_save_hp(ReclaimHeap, SaveHpCode, MaybeHpSlot),
{ globals__lookup_bool_option(Globals, use_trail, UseTrail) },
code_info__maybe_save_ticket(UseTrail, SaveTicketCode,
MaybeTicketSlot),
code_info__prepare_for_ite_hijack(CodeModel, HijackInfo,
PrepareHijackCode),
code_info__make_resume_point(ResumeVars, ResumeLocs, ResumeMap,
ResumePoint),
code_info__effect_resume_point(ResumePoint, CodeModel,
EffectResumeCode),
% Generate the negated goal as a semi-deterministic goal;
% it cannot be nondet, since mode correctness requires it
% to have no output vars.
trace__maybe_generate_internal_event_code(Goal, EnterTraceCode),
code_gen__generate_goal(model_semi, Goal, GoalCode),
code_info__ite_enter_then(HijackInfo, ThenNeckCode, ElseNeckCode),
% Kill again any variables that have become zombies
code_info__pickup_zombies(Zombies),
code_info__make_vars_forward_dead(Zombies),
code_info__get_forward_live_vars(LiveVars),
( { CodeModel = model_det } ->
% the then branch will never be reached
{ PruneTicketCode = empty },
{ FailTraceCode = empty },
{ FailCode = empty }
;
code_info__remember_position(AfterNegatedGoal),
% The call to reset_ticket(..., commit) here is necessary
% in order to properly detect floundering.
code_info__maybe_release_hp(MaybeHpSlot),
code_info__maybe_reset_prune_and_release_ticket(
MaybeTicketSlot, commit, PruneTicketCode),
trace__maybe_generate_negated_event_code(Goal, neg_failure,
FailTraceCode),
code_info__generate_failure(FailCode),
% We want liveness after not(G) to be the same as
% after G. Information about what variables are where
% will be set by code_info__generate_resume_point.
code_info__reset_to_position(AfterNegatedGoal)
),
% Generate the entry to the else branch
code_info__generate_resume_point(ResumePoint, ResumeCode),
code_info__set_forward_live_vars(LiveVars),
% Restore the heap pointer and solver state if necessary.
code_info__maybe_restore_and_release_hp(MaybeHpSlot, RestoreHpCode),
code_info__maybe_reset_discard_and_release_ticket(
MaybeTicketSlot, undo, RestoreTicketCode),
trace__maybe_generate_negated_event_code(Goal, neg_success,
SuccessTraceCode),
{ Code =
tree(FlushCode,
tree(PrepareHijackCode,
tree(EffectResumeCode,
tree(SaveHpCode,
tree(SaveTicketCode,
tree(EnterTraceCode,
tree(GoalCode,
tree(ThenNeckCode,
tree(PruneTicketCode,
tree(FailTraceCode,
tree(FailCode,
tree(ResumeCode,
tree(ElseNeckCode,
tree(RestoreTicketCode,
tree(RestoreHpCode,
SuccessTraceCode)))))))))))))))
}.
%---------------------------------------------------------------------------%