mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-18 07:15:19 +00:00
Estimated hours taken: 10 This change fixes two bugs, in quantification and liveness. I made the changes to the other files while trying to find them; they ought to be useful in trying to find similar bugs in the future. The compiler now bootstraps with agressive inlining enabled. quantification: Fix a bug that switched two different accumulators of the same type when processing pragma_c_codes. liveness: Fix a bug that could cause a variable to end up in both the post-death and post-birth set of the same goal. options: Reenable inlining. hlds_out, mercury_to_mercury: If -D v is given, include the number of each variable at the end of its name (e.g. Varname_20). The predicates involved (a few from mercury_to_mercury and many from hlds_out) now have an extra argument that says whether this should be done or not. (It is not done when printing clauses e.g. for .opt files.) The bug in quantification was causing the improper substitution of one variable for another, but both had the same name; such bugs would be very difficult to find without this change. constraint, det_report, intermod, make_hlds, mercury_to_c, mode_debug, mode_errors, module_qual, typecheck: Call predicates in hlds_out or mercury_to_mercury with the extra argument. saved_vars: Thread the io__state through this module to allow debugging. mercury_compile: Call saved_vars via its new interface. Fix an inadvertent use of an out-of-date ModuleInfo. goal_util, hlds_goal: Minor formatting cleanup. code_gen: Clean up the import sequence.
1319 lines
44 KiB
Mathematica
1319 lines
44 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 1995 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.
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Code generation - convert from HLDS to LLDS.
|
|
% Main author: conway.
|
|
%
|
|
% Notes:
|
|
%
|
|
% code_gen forwards most of the actual construction of intruction
|
|
% sequences to code_info, and other modules. The generation of
|
|
% calls is done by call_gen, switches by switch_gen, if-then-elses
|
|
% by ite_gen, unifications by unify_gen, and disjunctions by disj_gen.
|
|
%
|
|
% The general scheme for generating semideterministic code is
|
|
% to treat it as deterministic code, and have a fall-through
|
|
% point for failure. Semideterministic procedures leave a 'true'
|
|
% in register r(1) to indicate success, and 'false' to indicate
|
|
% failure.
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module code_gen.
|
|
|
|
:- interface.
|
|
|
|
:- import_module hlds_module, hlds_pred, hlds_goal, llds, code_info, shapes.
|
|
:- import_module list, assoc_list, io.
|
|
|
|
% Translate a HLDS structure into an LLDS
|
|
|
|
:- pred generate_code(module_info, module_info, list(c_procedure),
|
|
io__state, io__state).
|
|
:- mode generate_code(in, out, out, di, uo) is det.
|
|
|
|
:- pred generate_proc_code(proc_info, proc_id, pred_id, module_info,
|
|
shape_table, shape_table, c_procedure, io__state, io__state).
|
|
:- mode generate_proc_code(in, in, in, in, in, out, out, di, uo) is det.
|
|
% N.B. could use unique mode for `shape_table'
|
|
|
|
% This predicate generates code for a goal.
|
|
|
|
:- pred code_gen__generate_goal(code_model, hlds__goal, code_tree,
|
|
code_info, code_info).
|
|
:- mode code_gen__generate_goal(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.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module call_gen, unify_gen, ite_gen, switch_gen.
|
|
:- import_module disj_gen, globals, options, hlds_out.
|
|
:- import_module code_aux, middle_rec, passes_aux.
|
|
:- import_module code_util, type_util, mode_util.
|
|
:- import_module prog_data, instmap.
|
|
:- import_module bool, char, int, string, list, term.
|
|
:- import_module map, tree, std_util, require, set, varset.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% For a set of high level data structures and associated data, given in
|
|
% ModuleInfo, generate a list of c_procedure structures.
|
|
|
|
generate_code(ModuleInfo0, ModuleInfo, Procedures) -->
|
|
% get a list of all the predicate ids
|
|
% for which we are going to generate code.
|
|
{ module_info_predids(ModuleInfo0, PredIds) },
|
|
% now generate the code for each predicate
|
|
generate_pred_list_code(ModuleInfo0, ModuleInfo, PredIds, Procedures).
|
|
|
|
% Generate a list of c_procedure structures for each mode of each
|
|
% predicate given in ModuleInfo
|
|
|
|
:- pred generate_pred_list_code(module_info, module_info, list(pred_id),
|
|
list(c_procedure), io__state, io__state).
|
|
:- mode generate_pred_list_code(in, out, in, out, di, uo) is det.
|
|
|
|
generate_pred_list_code(ModuleInfo, ModuleInfo, [], []) --> [].
|
|
generate_pred_list_code(ModuleInfo0, ModuleInfo, [PredId | PredIds],
|
|
Predicates) -->
|
|
{ module_info_preds(ModuleInfo0, PredInfos) },
|
|
% get the pred_info structure for this predicate
|
|
{ map__lookup(PredInfos, PredId, PredInfo) },
|
|
% extract a list of all the procedure ids for this
|
|
% predicate and generate code for them
|
|
{ pred_info_non_imported_procids(PredInfo, ProcIds) },
|
|
( { ProcIds = [] } ->
|
|
{ Predicates0 = [] },
|
|
{ ModuleInfo1 = ModuleInfo0 }
|
|
;
|
|
generate_pred_code(ModuleInfo0, ModuleInfo1, PredId,
|
|
PredInfo, ProcIds, Predicates0)
|
|
),
|
|
{ list__append(Predicates0, Predicates1, Predicates) },
|
|
% and generate the code for the rest of the predicates
|
|
generate_pred_list_code(ModuleInfo1, ModuleInfo, PredIds, Predicates1).
|
|
|
|
% For the predicate identified by PredId, with the the associated
|
|
% data in ModuleInfo, generate a code_tree.
|
|
|
|
:- pred generate_pred_code(module_info, module_info, pred_id, pred_info,
|
|
list(proc_id), list(c_procedure), io__state, io__state).
|
|
:- mode generate_pred_code(in, out, in, in, in, out, di, uo) is det.
|
|
|
|
generate_pred_code(ModuleInfo0, ModuleInfo, PredId, PredInfo, ProcIds, Code) -->
|
|
globals__io_lookup_bool_option(very_verbose, VeryVerbose),
|
|
( { VeryVerbose = yes } ->
|
|
io__write_string("% Generating code for "),
|
|
hlds_out__write_pred_id(ModuleInfo0, PredId),
|
|
io__write_string("\n"),
|
|
globals__io_lookup_bool_option(statistics, Statistics),
|
|
maybe_report_stats(Statistics)
|
|
;
|
|
[]
|
|
),
|
|
% generate all the procedures for this predicate
|
|
{ module_info_get_shapes(ModuleInfo0, Shapes0) },
|
|
generate_proc_list_code(ProcIds, PredId, PredInfo, ModuleInfo0,
|
|
Shapes0, Shapes, [], Code),
|
|
{ module_info_set_shapes(ModuleInfo0, Shapes, ModuleInfo) }.
|
|
|
|
% For all the modes of predicate PredId, generate the appropriate
|
|
% code (deterministic, semideterministic, or nondeterministic).
|
|
|
|
:- pred generate_proc_list_code(list(proc_id), pred_id, pred_info, module_info,
|
|
shape_table, shape_table, list(c_procedure), list(c_procedure),
|
|
io__state, io__state).
|
|
% :- mode generate_proc_list_code(in, in, in, in, di, uo, di, uo, di, uo)
|
|
% is det.
|
|
:- mode generate_proc_list_code(in, in, in, in, in, out, in, out, di, uo)
|
|
is det.
|
|
|
|
generate_proc_list_code([], _PredId, _PredInfo, _ModuleInfo,
|
|
Shapes, Shapes, Procs, Procs) --> [].
|
|
generate_proc_list_code([ProcId | ProcIds], PredId, PredInfo, ModuleInfo0,
|
|
Shapes0, Shapes, Procs0, Procs) -->
|
|
{ pred_info_procedures(PredInfo, ProcInfos) },
|
|
% locate the proc_info structure for this mode of the predicate
|
|
{ map__lookup(ProcInfos, ProcId, ProcInfo) },
|
|
% find out if the proc is deterministic/etc
|
|
generate_proc_code(ProcInfo, ProcId, PredId, ModuleInfo0,
|
|
Shapes0, Shapes1, Proc),
|
|
{ Procs1 = [Proc | Procs0] },
|
|
generate_proc_list_code(ProcIds, PredId, PredInfo, ModuleInfo0,
|
|
Shapes1, Shapes, Procs1, Procs).
|
|
|
|
generate_proc_code(ProcInfo, ProcId, PredId, ModuleInfo,
|
|
Shapes0, Shapes, Proc) -->
|
|
% find out if the proc is deterministic/etc
|
|
{ proc_info_interface_code_model(ProcInfo, CodeModel) },
|
|
% get the goal for this procedure
|
|
{ proc_info_goal(ProcInfo, Goal) },
|
|
% get the information about this procedure that we need.
|
|
{ proc_info_variables(ProcInfo, VarInfo) },
|
|
{ proc_info_liveness_info(ProcInfo, Liveness) },
|
|
{ proc_info_stack_slots(ProcInfo, StackSlots) },
|
|
{ proc_info_get_initial_instmap(ProcInfo, ModuleInfo, InitialInst) },
|
|
{ Goal = _ - GoalInfo },
|
|
{ goal_info_get_follow_vars(GoalInfo, MaybeFollowVars) },
|
|
{
|
|
MaybeFollowVars = yes(FollowVars)
|
|
;
|
|
MaybeFollowVars = no,
|
|
map__init(FollowVars)
|
|
},
|
|
globals__io_get_gc_method(GC_Method),
|
|
{ GC_Method = accurate ->
|
|
SaveSuccip = yes
|
|
;
|
|
SaveSuccip = no
|
|
},
|
|
globals__io_get_globals(Globals),
|
|
% initialise the code_info structure
|
|
{ code_info__init(VarInfo, Liveness, StackSlots, SaveSuccip, Globals,
|
|
PredId, ProcId, ProcInfo, InitialInst, FollowVars,
|
|
ModuleInfo, Shapes0, CodeInfo0) },
|
|
% generate code for the procedure
|
|
{ generate_category_code(CodeModel, Goal, CodeTree, SUsed, CodeInfo0,
|
|
CodeInfo) },
|
|
% extract the new shape table
|
|
{ code_info__get_shapes(Shapes, CodeInfo, _CodeInfo1) },
|
|
|
|
% turn the code tree into a list
|
|
{ tree__flatten(CodeTree, FragmentList) },
|
|
% now the code is a list of code fragments (== list(instr)),
|
|
% so we need to do a level of unwinding to get a flat list.
|
|
{ list__condense(FragmentList, Instructions0) },
|
|
(
|
|
{ SUsed = yes(SlotNum) }
|
|
->
|
|
{ code_gen__add_saved_succip(Instructions0,
|
|
SlotNum, Instructions) }
|
|
;
|
|
{ Instructions = Instructions0 }
|
|
),
|
|
% get the name and arity of this predicate
|
|
{ predicate_name(ModuleInfo, PredId, Name) },
|
|
{ predicate_arity(ModuleInfo, PredId, Arity) },
|
|
% construct a c_procedure structure with all the information
|
|
{ Proc = c_procedure(Name, Arity, ProcId, Instructions) }.
|
|
|
|
:- pred generate_category_code(code_model, hlds__goal, code_tree, maybe(int),
|
|
code_info, code_info).
|
|
:- mode generate_category_code(in, in, out, out, in, out) is det.
|
|
|
|
generate_category_code(model_det, Goal, Instrs, Used) -->
|
|
% generate the code for the body of the clause
|
|
(
|
|
code_info__get_globals(Globals),
|
|
{ globals__lookup_bool_option(Globals, middle_rec, yes) },
|
|
middle_rec__match_and_generate(Goal, MiddleRecInstrs)
|
|
->
|
|
{ Instrs = MiddleRecInstrs },
|
|
{ Used = no }
|
|
;
|
|
% Make a new failure cont (not model_non)
|
|
% This continuation is never actually used,
|
|
% but is a place holder.
|
|
code_info__manufacture_failure_cont(no),
|
|
|
|
code_gen__generate_goal(model_det, Goal, Instr1),
|
|
code_info__get_instmap(Instmap),
|
|
|
|
% generate the prolog for the clause, which for deterministic
|
|
% procedures creates a label, increments the
|
|
% stack pointer to reserve space for local variables and
|
|
% the succip, and saves the succip.
|
|
|
|
code_gen__generate_det_prolog(Instr0, Used),
|
|
|
|
% generate a procedure epilog
|
|
% This needs information based on what variables are
|
|
% live at the end of the goal - that is, those that
|
|
% are output parameters which are known from goal_info,
|
|
% and decrement the stack pointer to free local variables,
|
|
% and restore the succip.
|
|
|
|
(
|
|
{ instmap__is_reachable(Instmap) }
|
|
->
|
|
code_gen__generate_det_epilog(Instr2)
|
|
;
|
|
{ Instr2 = empty }
|
|
),
|
|
|
|
% combine the prolog, body and epilog
|
|
{ Instrs = tree(Instr0, tree(Instr1, Instr2)) }
|
|
).
|
|
|
|
generate_category_code(model_semi, Goal, Instrs, Used) -->
|
|
% Make a new failure cont (not model_non)
|
|
code_info__manufacture_failure_cont(no),
|
|
|
|
% generate the code for the body of the clause
|
|
code_gen__generate_goal(model_semi, Goal, Instr1),
|
|
code_gen__generate_semi_prolog(Instr0, Used),
|
|
code_gen__generate_semi_epilog(Instr2),
|
|
|
|
% combine the prolog, body and epilog
|
|
{ Instrs = tree(Instr0, tree(Instr1, Instr2)) }.
|
|
|
|
generate_category_code(model_non, Goal, Instrs, Used) -->
|
|
% Make a failure continuation, we lie and
|
|
% say that it is nondet, and then unset it
|
|
% so that it points to do_fail
|
|
code_info__manufacture_failure_cont(yes),
|
|
|
|
% generate the code for the body of the clause
|
|
code_gen__generate_goal(model_non, Goal, Instr1),
|
|
code_gen__generate_non_prolog(Instr0, Used),
|
|
code_gen__generate_non_epilog(Instr2),
|
|
|
|
% combine the prolog, body and epilog
|
|
{ Instrs = tree(Instr0, tree(Instr1, Instr2)) }.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred code_gen__generate_det_prolog(code_tree, maybe(int),
|
|
code_info, code_info).
|
|
:- mode code_gen__generate_det_prolog(out, out, in, out) is det.
|
|
|
|
code_gen__generate_det_prolog(EntryCode, SUsed) -->
|
|
code_info__get_stack_slots(StackSlots),
|
|
code_info__get_varset(VarSet),
|
|
{ code_aux__explain_stack_slots(StackSlots, VarSet, SlotsComment) },
|
|
code_info__get_total_stackslot_count(NS0),
|
|
code_info__get_pred_id(PredId),
|
|
code_info__get_proc_id(ProcId),
|
|
code_info__get_succip_used(Used),
|
|
code_info__get_module_info(ModuleInfo),
|
|
{ code_util__make_local_entry_label(ModuleInfo, PredId, ProcId, no,
|
|
Entry) },
|
|
{ CodeA = node([
|
|
comment(SlotsComment) - "",
|
|
label(Entry) - "Procedure entry point"
|
|
]) },
|
|
(
|
|
{ Used = yes }
|
|
->
|
|
{ NS is NS0 + 1 },
|
|
{ CodeC = node([
|
|
assign(stackvar(NS), lval(succip)) -
|
|
"save the success ip"
|
|
]) },
|
|
{ SUsed = yes(NS) }
|
|
;
|
|
{ NS = NS0 },
|
|
{ CodeC = empty },
|
|
{ SUsed = no }
|
|
),
|
|
(
|
|
{ NS = 0 }
|
|
->
|
|
{ CodeB = CodeA }
|
|
;
|
|
{ predicate_name(ModuleInfo, PredId, PredName) },
|
|
{ CodeB = tree(
|
|
CodeA,
|
|
node([incr_sp(NS, PredName) - "Allocate stack frame"])
|
|
) }
|
|
),
|
|
{ PStart = node([comment("Start of procedure prologue") - ""]) },
|
|
{ PEnd = node([comment("End of procedure prologue") - ""]) },
|
|
{ EntryCode = tree(tree(PStart, CodeB), tree(CodeC, PEnd)) }.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred code_gen__generate_det_epilog(code_tree, code_info, code_info).
|
|
:- mode code_gen__generate_det_epilog(out, in, out) is det.
|
|
|
|
code_gen__generate_det_epilog(ExitCode) -->
|
|
code_info__get_instmap(Instmap),
|
|
code_info__get_arginfo(ArgModes),
|
|
code_info__get_headvars(HeadVars),
|
|
{ assoc_list__from_corresponding_lists(HeadVars, ArgModes, Args)},
|
|
(
|
|
{ instmap__is_unreachable(Instmap) }
|
|
->
|
|
{ CodeA = empty }
|
|
;
|
|
code_info__setup_call(Args, callee, CodeA)
|
|
),
|
|
code_info__get_succip_used(Used),
|
|
code_info__get_total_stackslot_count(NS0),
|
|
(
|
|
{ Used = yes }
|
|
->
|
|
{ NS is NS0 + 1 },
|
|
{ CodeC = node([
|
|
assign(succip, lval(stackvar(NS))) -
|
|
"restore the success ip"
|
|
]) }
|
|
;
|
|
{ NS = NS0 },
|
|
{ CodeC = empty }
|
|
),
|
|
{ CodeB1 = node([ goto(succip) - "Return from procedure call"]) },
|
|
(
|
|
{ NS = 0 }
|
|
->
|
|
{ CodeB0 = empty }
|
|
;
|
|
{ CodeB0 = node([
|
|
decr_sp(NS) - "Deallocate stack frame"
|
|
]) }
|
|
),
|
|
{ code_gen__output_args(Args, LiveArgs) },
|
|
{ LiveValCode = node([
|
|
livevals(LiveArgs) - ""
|
|
]) },
|
|
{ CodeB = tree(CodeB0, tree(LiveValCode, CodeB1)) },
|
|
{ EStart = node([comment("Start of procedure epilogue") - ""]) },
|
|
{ EEnd = node([comment("End of procedure epilogue") - ""]) },
|
|
{ ExitCode = tree(tree(EStart, CodeA),
|
|
tree(CodeC, tree(EEnd, CodeB))) }.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred code_gen__generate_semi_prolog(code_tree, maybe(int), code_info, code_info).
|
|
:- mode code_gen__generate_semi_prolog(out, out, in, out) is det.
|
|
|
|
code_gen__generate_semi_prolog(EntryCode, SUsed) -->
|
|
code_info__get_stack_slots(StackSlots),
|
|
code_info__get_varset(VarSet),
|
|
{ code_aux__explain_stack_slots(StackSlots, VarSet, SlotsComment) },
|
|
code_info__get_pred_id(PredId),
|
|
code_info__get_proc_id(ProcId),
|
|
code_info__get_succip_used(Used),
|
|
code_info__get_total_stackslot_count(NS0),
|
|
code_info__get_module_info(ModuleInfo),
|
|
{ code_util__make_local_entry_label(ModuleInfo, PredId, ProcId, no,
|
|
Entry) },
|
|
{ CodeA = node([
|
|
comment(SlotsComment) - "",
|
|
label(Entry) - "Procedure entry point"
|
|
]) },
|
|
(
|
|
{ Used = yes }
|
|
->
|
|
{ NS is NS0 + 1 },
|
|
{ CodeC = node([
|
|
assign(stackvar(NS), lval(succip)) -
|
|
"save the success ip"
|
|
]) },
|
|
{ SUsed = yes(NS) }
|
|
;
|
|
{ NS = NS0 },
|
|
{ CodeC = empty },
|
|
{ SUsed = no }
|
|
),
|
|
(
|
|
{ NS = 0 }
|
|
->
|
|
{ CodeB = CodeA }
|
|
;
|
|
{ predicate_name(ModuleInfo, PredId, PredName) },
|
|
{ CodeB = tree(
|
|
CodeA,
|
|
node([incr_sp(NS, PredName) - "Allocate stack frame"])
|
|
) }
|
|
),
|
|
{ PStart = node([comment("Start of procedure prologue") - ""]) },
|
|
{ PEnd = node([comment("End of procedure prologue") - ""]) },
|
|
{ EntryCode = tree(tree(PStart, CodeB), tree(CodeC, PEnd)) }.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred code_gen__generate_semi_epilog(code_tree, code_info, code_info).
|
|
:- mode code_gen__generate_semi_epilog(out, in, out) is det.
|
|
|
|
code_gen__generate_semi_epilog(Instr) -->
|
|
code_info__get_instmap(Instmap),
|
|
code_info__get_arginfo(ArgModes),
|
|
code_info__get_headvars(HeadVars),
|
|
{assoc_list__from_corresponding_lists(HeadVars, ArgModes, Args) },
|
|
(
|
|
{ instmap__is_unreachable(Instmap) }
|
|
->
|
|
{ CodeA = empty }
|
|
;
|
|
code_info__setup_call(Args, callee, CodeA)
|
|
),
|
|
code_info__restore_failure_cont(FailureCont),
|
|
code_info__get_succip_used(Used),
|
|
code_info__get_total_stackslot_count(NS0),
|
|
{ code_gen__output_args(Args, LiveArgs0) },
|
|
{ set__insert(LiveArgs0, reg(r(1)), LiveArgs) },
|
|
{ SLiveValCode = node([
|
|
livevals(LiveArgs) - ""
|
|
]) },
|
|
{ set__singleton_set(LiveArg, reg(r(1))) },
|
|
{ FLiveValCode = node([
|
|
livevals(LiveArg) - ""
|
|
]) },
|
|
(
|
|
{ Used = yes }
|
|
->
|
|
{ NS is NS0 + 1 },
|
|
{ CodeC = node([
|
|
assign(succip, lval(stackvar(NS))) -
|
|
"restore the success ip"
|
|
]) }
|
|
;
|
|
{ NS = NS0 },
|
|
{ CodeC = empty }
|
|
),
|
|
(
|
|
{ NS = 0 }
|
|
->
|
|
{ UnLink = CodeC }
|
|
;
|
|
{ UnLink = tree(
|
|
CodeC,
|
|
node([
|
|
decr_sp(NS) - "Deallocate stack frame"
|
|
])
|
|
) }
|
|
),
|
|
{ Success = tree(
|
|
UnLink,
|
|
node([ assign(reg(r(1)), const(true)) - "Succeed" ])
|
|
) },
|
|
{ Failure = tree(
|
|
UnLink,
|
|
node([ assign(reg(r(1)), const(false)) - "Fail" ])
|
|
) },
|
|
{ ExitCode = tree(
|
|
tree(
|
|
tree(Success, SLiveValCode),
|
|
node([ goto(succip) - "Return from procedure call" ])
|
|
),
|
|
tree(
|
|
FailureCont,
|
|
tree(
|
|
tree(Failure, FLiveValCode),
|
|
node([ goto(succip) -
|
|
"Return from procedure call" ])
|
|
)
|
|
)
|
|
) },
|
|
{ EStart = node([comment("Start of procedure epilogue") - ""]) },
|
|
{ EEnd = node([comment("End of procedure epilogue") - ""]) },
|
|
{ Instr = tree(tree(EStart, CodeA), tree(ExitCode, EEnd)) }.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred code_gen__generate_non_prolog(code_tree, maybe(int),
|
|
code_info, code_info).
|
|
:- mode code_gen__generate_non_prolog(out, out, in, out) is det.
|
|
|
|
code_gen__generate_non_prolog(EntryCode, no) -->
|
|
code_info__get_stack_slots(StackSlots),
|
|
code_info__get_varset(VarSet),
|
|
{ code_aux__explain_stack_slots(StackSlots, VarSet, SlotsComment) },
|
|
code_info__get_pred_id(PredId),
|
|
code_info__get_proc_id(ProcId),
|
|
code_info__get_total_stackslot_count(NS),
|
|
code_info__get_module_info(ModuleInfo),
|
|
{ code_util__make_local_entry_label(ModuleInfo, PredId, ProcId, no,
|
|
Entry) },
|
|
{ CodeA = node([
|
|
comment(SlotsComment) - "",
|
|
label(Entry) - "Procedure entry point"
|
|
]) },
|
|
% The `name' argument to mkframe() is just for
|
|
% debugging purposes. We construct it as "predname/arity".
|
|
{ predicate_name(ModuleInfo, PredId, PredName) },
|
|
{ predicate_arity(ModuleInfo, PredId, PredArity) },
|
|
{ string__int_to_string(PredArity, PredArityString) },
|
|
{ string__append(PredName, "/", Tmp) },
|
|
{ string__append(Tmp, PredArityString, Name) },
|
|
{ CodeB = node([
|
|
mkframe(Name, NS, do_fail) - "Nondet stackframe"
|
|
]) },
|
|
{ PStart = node([comment("Start of procedure prologue") - ""]) },
|
|
{ PEnd = node([comment("End of procedure prologue") - ""]) },
|
|
{ EntryCode = tree(tree(PStart, CodeA), tree(CodeB, PEnd)) }.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred code_gen__generate_non_epilog(code_tree, code_info, code_info).
|
|
:- mode code_gen__generate_non_epilog(out, in, out) is det.
|
|
|
|
code_gen__generate_non_epilog(Instr) -->
|
|
code_info__get_instmap(Instmap),
|
|
code_info__get_arginfo(ArgModes),
|
|
code_info__get_headvars(HeadVars),
|
|
{assoc_list__from_corresponding_lists(HeadVars, ArgModes, Args) },
|
|
(
|
|
{ instmap__is_unreachable(Instmap) }
|
|
->
|
|
{ CodeA = empty }
|
|
;
|
|
code_info__setup_call(Args, callee, CodeA)
|
|
),
|
|
{ code_gen__output_args(Args, LiveArgs) },
|
|
{ LiveValCode = node([
|
|
livevals(LiveArgs) - ""
|
|
]) },
|
|
{ ExitCode = tree(LiveValCode, node([
|
|
goto(do_succeed(no)) - "Succeed"
|
|
])) },
|
|
{ EStart = node([comment("Start of procedure epilogue") - ""]) },
|
|
{ EEnd = node([comment("End of procedure epilogue") - ""]) },
|
|
{ Instr = tree(tree(EStart, CodeA), tree(ExitCode, EEnd)) }.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% 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.
|
|
|
|
code_gen__generate_goal(ContextModel, Goal - GoalInfo, Code) -->
|
|
% Make any changes to liveness before Goal
|
|
{ goal_is_atomic(Goal) ->
|
|
IsAtomic = yes
|
|
;
|
|
IsAtomic = no
|
|
},
|
|
code_info__pre_goal_update(GoalInfo, IsAtomic),
|
|
code_info__get_instmap(Instmap),
|
|
(
|
|
{ instmap__is_reachable(Instmap) }
|
|
->
|
|
{ goal_info_get_code_model(GoalInfo, CodeModel) },
|
|
(
|
|
{ CodeModel = model_det },
|
|
code_gen__generate_det_goal_2(Goal, GoalInfo, Code0)
|
|
;
|
|
{ CodeModel = model_semi },
|
|
( { ContextModel \= model_det } ->
|
|
code_gen__generate_semi_goal_2(Goal, GoalInfo,
|
|
Code0)
|
|
;
|
|
{ error("semidet model in det context") }
|
|
)
|
|
;
|
|
{ CodeModel = model_non },
|
|
( { ContextModel = model_non } ->
|
|
code_gen__generate_non_goal_2(Goal, GoalInfo,
|
|
Code0)
|
|
;
|
|
{ error("nondet model in det/semidet context") }
|
|
)
|
|
),
|
|
% Make live any variables which subsequent goals
|
|
% will expect to be live, but were not generated
|
|
code_info__set_instmap(Instmap),
|
|
code_info__post_goal_update(GoalInfo),
|
|
code_info__get_globals(Options),
|
|
(
|
|
{ globals__lookup_bool_option(Options, lazy_code, yes) }
|
|
->
|
|
{ Code1 = empty }
|
|
;
|
|
{ error("Eager code unavailable") }
|
|
%%% code_info__generate_eager_flush(Code1)
|
|
),
|
|
{ Code = tree(Code0, Code1) }
|
|
;
|
|
{ Code = empty }
|
|
),
|
|
!.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Generate a conjoined series of goals.
|
|
% Note of course, that with a conjunction, state information
|
|
% flows directly from one to the next atom.
|
|
|
|
:- pred code_gen__generate_goals(hlds__goals, code_model, code_tree,
|
|
code_info, code_info).
|
|
:- mode code_gen__generate_goals(in, in, out, in, out) is det.
|
|
|
|
code_gen__generate_goals([], _, empty) --> [].
|
|
code_gen__generate_goals([Goal | Goals], CodeModel, Instr) -->
|
|
code_gen__generate_goal(CodeModel, Goal, Instr1),
|
|
code_info__get_instmap(Instmap),
|
|
(
|
|
{ instmap__is_unreachable(Instmap) }
|
|
->
|
|
{ Instr = Instr1 }
|
|
;
|
|
code_gen__generate_goals(Goals, CodeModel, Instr2),
|
|
{ Instr = tree(Instr1, Instr2) }
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred code_gen__generate_det_goal_2(hlds__goal_expr, hlds__goal_info,
|
|
code_tree, code_info, code_info).
|
|
:- mode code_gen__generate_det_goal_2(in, in, out, in, out) is det.
|
|
|
|
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) -->
|
|
{ Goal = _ - InnerGoalInfo },
|
|
{ goal_info_get_code_model(InnerGoalInfo, CodeModel) },
|
|
(
|
|
{ CodeModel = model_det },
|
|
code_gen__generate_goal(model_det, Goal, Instr)
|
|
;
|
|
{ CodeModel = model_semi },
|
|
{ error("semidet model in det context") }
|
|
;
|
|
{ CodeModel = model_non },
|
|
code_info__generate_det_pre_commit(PreCommit),
|
|
code_gen__generate_goal(model_non, Goal, GoalCode),
|
|
code_info__generate_det_commit(Commit),
|
|
{ Instr = tree(PreCommit, tree(GoalCode, Commit)) }
|
|
).
|
|
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(model_det, Goal, Instr).
|
|
code_gen__generate_det_goal_2(higher_order_call(PredVar, Args, Types,
|
|
Modes, Det),
|
|
_CodeInfo, Instr) -->
|
|
call_gen__generate_higher_order_call(model_det, PredVar, Args,
|
|
Types, Modes, Det, Instr).
|
|
code_gen__generate_det_goal_2(call(PredId, ProcId, Args, BuiltinState, _, _),
|
|
_GoalInfo, Instr) -->
|
|
(
|
|
{ BuiltinState = not_builtin }
|
|
->
|
|
code_info__succip_is_used,
|
|
call_gen__generate_det_call(PredId, ProcId, Args, Instr)
|
|
;
|
|
call_gen__generate_det_builtin(PredId, ProcId, Args, Instr)
|
|
).
|
|
code_gen__generate_det_goal_2(switch(Var, CanFail, CaseList, StoreMap),
|
|
GoalInfo, Instr) -->
|
|
switch_gen__generate_switch(model_det, Var, CanFail, CaseList,
|
|
StoreMap, GoalInfo, Instr).
|
|
code_gen__generate_det_goal_2(
|
|
if_then_else(_Vars, CondGoal, ThenGoal, ElseGoal, StoreMap),
|
|
_GoalInfo, Instr) -->
|
|
ite_gen__generate_det_ite(CondGoal, ThenGoal, ElseGoal, StoreMap,
|
|
Instr).
|
|
code_gen__generate_det_goal_2(unify(_L, _R, _U, Uni, _C), _GoalInfo, Instr) -->
|
|
(
|
|
{ Uni = assign(Left, Right) },
|
|
unify_gen__generate_assignment(Left, Right, Instr)
|
|
;
|
|
{ Uni = construct(Var, ConsId, Args, Modes) },
|
|
unify_gen__generate_construction(Var, ConsId, Args,
|
|
Modes, Instr)
|
|
;
|
|
{ Uni = deconstruct(Var, ConsId, Args, Modes, _Det) },
|
|
unify_gen__generate_det_deconstruction(Var, ConsId, Args,
|
|
Modes, Instr)
|
|
;
|
|
% These should have been transformed into calls by
|
|
% polymorphism.m.
|
|
{ Uni = complicated_unify(_UniMode, _CanFail) },
|
|
{ error("code_gen__generate_det_goal_2 - complicated unify") }
|
|
;
|
|
{ Uni = simple_test(_, _) },
|
|
{ error("generate_det_goal_2: cannot have det simple_test") }
|
|
).
|
|
|
|
code_gen__generate_det_goal_2(
|
|
pragma_c_code(C_Code, IsRecursive, PredId, ModeId, Args,
|
|
ArgNames), GoalInfo, Instr) -->
|
|
code_gen__generate_pragma_c_code(model_det, C_Code, IsRecursive,
|
|
PredId, ModeId, Args, ArgNames, GoalInfo, Instr).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type c_arg ---> c_arg(var, maybe(string)).
|
|
|
|
:- pred code_gen__generate_pragma_c_code(code_model, string, c_is_recursive,
|
|
pred_id, proc_id, list(var), list(maybe(string)),
|
|
hlds__goal_info, code_tree, code_info, code_info).
|
|
:- mode code_gen__generate_pragma_c_code(in, in, in, in, in, in, in, in, out,
|
|
in, out) is det.
|
|
|
|
code_gen__generate_pragma_c_code(CodeModel, C_Code, IsRecursive,
|
|
PredId, ModeId, Args, Names, _GoalInfo, Instr) -->
|
|
% First we need to get a list of input and output arguments
|
|
code_info__get_pred_proc_arginfo(PredId, ModeId, ArgInfo),
|
|
{ make_c_arg_list(Args, Names, ArgNames) },
|
|
{ assoc_list__from_corresponding_lists(ArgNames, ArgInfo, ArgModes) },
|
|
{ pragma_select_in_args(ArgModes, InArgs) },
|
|
{ pragma_select_out_args(ArgModes, OutArgs) },
|
|
|
|
% The code generated for pragma_c_code is of the following form:
|
|
%
|
|
% <save live variables onto the stack> /* see note (1) below */
|
|
% {
|
|
% <declaration of one local variable for each arg>
|
|
% <assignment of input values from registers to local variables>
|
|
% save_registers(); /* see notes (1) and (2) below */
|
|
% { <the c code itself> }
|
|
% #ifndef CONSERVATIVE_GC
|
|
% restore_registers(); /* see notes (1) and (3) below */
|
|
% #endif
|
|
% <assignment of the output values from local variables to registers>
|
|
% }
|
|
%
|
|
% In the case of a semidet pragma c_code, this is followed by
|
|
%
|
|
% if (r1) goto label;
|
|
% <code to fail>
|
|
% label:
|
|
%
|
|
% Notes:
|
|
%
|
|
% (1) These parts are only emitted if the C code may be recursive.
|
|
% If a pragma c_code(non_recursive, ...) declaration was used,
|
|
% they will not be emitted.
|
|
%
|
|
% (2) The call to save_registers() is needed so that if the
|
|
% C code calls Mercury code, we can call restore_registers()
|
|
% on entry to the Mercury code (see export.m) to get the
|
|
% right values of `sp', `hp', `curfr' and `maxfr' for the
|
|
% recursive invocation of Mercury.
|
|
%
|
|
% (3) The call to restore_registers() is needed in case the
|
|
% C code calls Mercury code which allocates some data
|
|
% on the heap, and this data is returned from Mercury
|
|
% through C back to Mercury. In that case, we need to
|
|
% keep the value of `hp' that was set by the recursive
|
|
% invocation of Mercury. The Mercury calling convention
|
|
% guarantees that the values of `sp', `curfr', and `maxfr'
|
|
% will be preserved, so if we're using conservative gc,
|
|
% there is nothing that needs restoring.
|
|
|
|
( { IsRecursive = non_recursive } ->
|
|
{ SaveVarsCode = empty }
|
|
;
|
|
% the C code might call back Mercury code
|
|
% which clobbers the succip
|
|
code_info__succip_is_used,
|
|
|
|
% the C code might call back Mercury code which clobbers the
|
|
% other registers, so we need to save any live variables
|
|
% (other than the output args) onto the stack
|
|
{ get_c_arg_list_vars(OutArgs, OutArgs1) },
|
|
{ set__list_to_set(OutArgs1, OutArgsSet) },
|
|
call_gen__save_variables(OutArgsSet, SaveVarsCode)
|
|
),
|
|
|
|
make_pragma_decls(ArgNames, Decls),
|
|
get_pragma_input_vars(InArgs, Inputs, InputVarsCode),
|
|
( { CodeModel = model_semi } ->
|
|
% We have to clear r1 for C code that gets inlined
|
|
% so that it is safe to assign to SUCCESS_INDICATOR.
|
|
code_info__clear_r1(ShuffleR1_Code),
|
|
|
|
% C code goes here
|
|
|
|
( { IsRecursive = non_recursive } ->
|
|
[]
|
|
;
|
|
% the C code may call Mercury code which clobbers
|
|
% the regs
|
|
code_info__clear_all_registers
|
|
),
|
|
|
|
code_info__get_next_label(SkipLab),
|
|
code_info__generate_failure(FailCode),
|
|
{ CheckFailureCode = tree(node([
|
|
if_val(lval(reg(r(1))), label(SkipLab)) -
|
|
"Test for success of pragma_c_code"
|
|
]), tree(FailCode, node([ label(SkipLab) - "" ])))
|
|
},
|
|
|
|
code_info__lock_reg(r(1)),
|
|
pragma_acquire_regs(OutArgs, Regs),
|
|
code_info__unlock_reg(r(1))
|
|
;
|
|
{ ShuffleR1_Code = empty },
|
|
|
|
% c code goes here
|
|
|
|
( { IsRecursive = non_recursive } ->
|
|
[]
|
|
;
|
|
% the C code may call Mercury code which clobbers
|
|
% the regs
|
|
code_info__clear_all_registers
|
|
),
|
|
|
|
{ CheckFailureCode = empty },
|
|
|
|
pragma_acquire_regs(OutArgs, Regs)
|
|
),
|
|
place_pragma_output_args_in_regs(OutArgs, Regs, Outputs),
|
|
|
|
( { IsRecursive = non_recursive } ->
|
|
{ Wrapped_C_Code = C_Code }
|
|
;
|
|
{ string__append_list([
|
|
"\tsave_registers();\n{\n",
|
|
C_Code, "\n}\n",
|
|
"#ifndef CONSERVATIVE_GC\n",
|
|
"\trestore_registers();\n",
|
|
"#endif\n"
|
|
], Wrapped_C_Code) }
|
|
),
|
|
{ PragmaCode = node([pragma_c(Decls, Inputs, Wrapped_C_Code, Outputs) -
|
|
"Pragma C inclusion"]) },
|
|
{ Instr = tree(tree(tree(SaveVarsCode, InputVarsCode), ShuffleR1_Code),
|
|
tree(PragmaCode, CheckFailureCode)) }.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred make_c_arg_list(list(var), list(maybe(string)), list(c_arg)).
|
|
:- mode make_c_arg_list(in, in, out) is det.
|
|
|
|
make_c_arg_list(Vars, Names, ArgNames) :-
|
|
make_c_arg_list_2(Vars, Names, [], ArgNames0),
|
|
list__reverse(ArgNames0, ArgNames).
|
|
|
|
:- pred make_c_arg_list_2(list(var), list(maybe(string)),
|
|
list(c_arg), list(c_arg)).
|
|
:- mode make_c_arg_list_2(in, in, in, out) is det.
|
|
|
|
make_c_arg_list_2([], [], ArgNames, ArgNames).
|
|
make_c_arg_list_2([Var | Vars], [Name | Names], ArgNames0, ArgNames) :-
|
|
make_c_arg_list_2(Vars, Names, [c_arg(Var, Name) | ArgNames0],
|
|
ArgNames).
|
|
make_c_arg_list_2([], [_ | _], _, _) :-
|
|
error("code_gen:make_c_arg_list_2 - length mismatch").
|
|
make_c_arg_list_2([_ | _], [], _, _) :-
|
|
error("code_gen:make_c_arg_list_2 - length mismatch").
|
|
|
|
:- pred get_c_arg_list_vars(list(c_arg)::in, list(var)::out) is det.
|
|
|
|
get_c_arg_list_vars([], []).
|
|
get_c_arg_list_vars([c_arg(Var, _) | Args], [Var | Vars1]) :-
|
|
get_c_arg_list_vars(Args, Vars1).
|
|
|
|
% pragma_select_out_args returns the list of variables which are outputs for
|
|
% a procedure
|
|
|
|
:- pred pragma_select_out_args(assoc_list(c_arg, arg_info), list(c_arg)).
|
|
:- mode pragma_select_out_args(in, out) is det.
|
|
|
|
pragma_select_out_args([], []).
|
|
pragma_select_out_args([V - arg_info(_Loc, Mode) | Rest], Out) :-
|
|
pragma_select_out_args(Rest, Out0),
|
|
(
|
|
Mode = top_out
|
|
->
|
|
Out = [V | Out0]
|
|
;
|
|
Out = Out0
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% pragma_select_in_args returns the list of variables which are inputs for
|
|
% a procedure
|
|
|
|
:- pred pragma_select_in_args(assoc_list(c_arg, arg_info), list(c_arg)).
|
|
:- mode pragma_select_in_args(in, out) is det.
|
|
|
|
pragma_select_in_args([], []).
|
|
pragma_select_in_args([V - arg_info(_Loc, Mode) | Rest], In) :-
|
|
pragma_select_in_args(Rest, In0),
|
|
(
|
|
Mode = top_in
|
|
->
|
|
In = [V | In0]
|
|
;
|
|
In = In0
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% make_pragma_decls fills returns the list of pragma_decls for the pragma_c
|
|
% data structure in the llds. It is essentially a list of pairs of type and
|
|
% variable name, so that declarations of the form "Type Name;" can be made.
|
|
|
|
:- pred make_pragma_decls(list(c_arg), list(pragma_c_decl),
|
|
code_info, code_info).
|
|
:- mode make_pragma_decls(in, out, in, out) is det.
|
|
|
|
make_pragma_decls([], []) --> [].
|
|
make_pragma_decls([c_arg(Arg, ArgName) | ArgNames], Decls) -->
|
|
( { ArgName = yes(Name) } ->
|
|
code_info__variable_type(Arg, Type),
|
|
{ Decl = pragma_c_decl(Type, Name) },
|
|
{ Decls = [Decl | Decls1] },
|
|
make_pragma_decls(ArgNames, Decls1)
|
|
;
|
|
% if the variable doesn't occur in the ArgNameMap,
|
|
% it can't be used, so we just ignore it
|
|
make_pragma_decls(ArgNames, Decls)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% get_pragma_input_vars returns a list of pragma_c_inputs for the pragma_c
|
|
% data structure in the llds. It is essentially a list of the input variables,
|
|
% and the corresponding rvals assigned to those (C) variables.
|
|
|
|
:- pred get_pragma_input_vars(list(c_arg), list(pragma_c_input),
|
|
code_tree, code_info, code_info).
|
|
:- mode get_pragma_input_vars(in, out, out, in, out) is det.
|
|
|
|
get_pragma_input_vars([], [], empty) --> [].
|
|
get_pragma_input_vars([c_arg(Arg, MaybeName) | Args], Inputs, Code) -->
|
|
( { MaybeName = yes(Name) } ->
|
|
code_info__variable_type(Arg, Type),
|
|
code_info__produce_variable(Arg, Code0, Rval),
|
|
{ Input = pragma_c_input(Name, Type, Rval) },
|
|
{ Inputs = [Input | Inputs1] },
|
|
{ Code = tree(Code0, Code1) },
|
|
get_pragma_input_vars(Args, Inputs1, Code1)
|
|
;
|
|
% if the variable doesn't occur in the ArgNameMap,
|
|
% it can't be used, so we just ignore it
|
|
get_pragma_input_vars(Args, Inputs, Code)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% pragma_acquire_regs acquires a list of registers in which to place each
|
|
% of the given variables.
|
|
|
|
:- pred pragma_acquire_regs(list(c_arg), list(reg), code_info, code_info).
|
|
:- mode pragma_acquire_regs(in, out, in, out) is det.
|
|
|
|
pragma_acquire_regs([], []) --> [].
|
|
pragma_acquire_regs([_V | Vars], [Reg | Regs]) -->
|
|
code_info__acquire_reg(Reg),
|
|
pragma_acquire_regs(Vars, Regs).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% place_pragma_output_args_in_regs returns a list of pragma_c_outputs, which
|
|
% are pairs of names of output registers and (C) variables which hold the
|
|
% output value.
|
|
|
|
:- pred place_pragma_output_args_in_regs(list(c_arg), list(reg),
|
|
list(pragma_c_output), code_info, code_info).
|
|
:- mode place_pragma_output_args_in_regs(in, in, out, in, out) is det.
|
|
|
|
place_pragma_output_args_in_regs([], [], []) --> [].
|
|
place_pragma_output_args_in_regs([_X | _Xs], [], []) -->
|
|
{ error("place_pragma_output_args_in_regs: list length mismatch") }.
|
|
place_pragma_output_args_in_regs([], [_X | _Xs], []) -->
|
|
{ error("place_pragma_output_args_in_regs: list length mismatch") }.
|
|
place_pragma_output_args_in_regs([Arg | Args], [Reg | Regs], [O | Outputs]) -->
|
|
( { Arg = c_arg(A, yes(Name)) } ->
|
|
code_info__variable_type(A, Type),
|
|
code_info__release_reg(Reg),
|
|
code_info__set_var_location(A, reg(Reg)),
|
|
{ O = pragma_c_output(reg(Reg), Type, Name) },
|
|
place_pragma_output_args_in_regs(Args, Regs, Outputs)
|
|
;
|
|
{ error("code_gen:place_pragma_output_args_in_regs") }
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred code_gen__generate_semi_goal_2(hlds__goal_expr, hlds__goal_info,
|
|
code_tree, code_info, code_info).
|
|
:- mode code_gen__generate_semi_goal_2(in, in, out, in, out) is det.
|
|
|
|
code_gen__generate_semi_goal_2(conj(Goals), _GoalInfo, Code) -->
|
|
code_gen__generate_goals(Goals, model_semi, Code).
|
|
code_gen__generate_semi_goal_2(some(_Vars, Goal), _GoalInfo, Code) -->
|
|
{ Goal = _ - InnerGoalInfo },
|
|
{ goal_info_get_code_model(InnerGoalInfo, CodeModel) },
|
|
(
|
|
{ CodeModel = model_det },
|
|
code_gen__generate_goal(model_det, Goal, Code)
|
|
;
|
|
{ CodeModel = model_semi },
|
|
code_gen__generate_goal(model_semi, Goal, Code)
|
|
;
|
|
{ CodeModel = model_non },
|
|
code_info__generate_semi_pre_commit(Label, PreCommit),
|
|
code_gen__generate_goal(model_non, Goal, GoalCode),
|
|
code_info__generate_semi_commit(Label, Commit),
|
|
{ Code = tree(PreCommit, tree(GoalCode, Commit)) }
|
|
).
|
|
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(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,
|
|
Types, Modes, Det, Code).
|
|
code_gen__generate_semi_goal_2(call(PredId, ProcId, Args, BuiltinState, _, _),
|
|
_GoalInfo, Code) -->
|
|
(
|
|
{ BuiltinState = not_builtin }
|
|
->
|
|
code_info__succip_is_used,
|
|
call_gen__generate_semidet_call(PredId, ProcId, Args, Code)
|
|
;
|
|
call_gen__generate_semidet_builtin(PredId, ProcId, Args, Code)
|
|
).
|
|
code_gen__generate_semi_goal_2(switch(Var, CanFail, CaseList, StoreMap),
|
|
GoalInfo, Instr) -->
|
|
switch_gen__generate_switch(model_semi, Var, CanFail,
|
|
CaseList, StoreMap, GoalInfo, Instr).
|
|
code_gen__generate_semi_goal_2(
|
|
if_then_else(_Vars, CondGoal, ThenGoal, ElseGoal, StoreMap),
|
|
_GoalInfo, Instr) -->
|
|
ite_gen__generate_semidet_ite(CondGoal, ThenGoal, ElseGoal, StoreMap,
|
|
Instr).
|
|
code_gen__generate_semi_goal_2(unify(_L, _R, _U, Uni, _C),
|
|
_GoalInfo, Code) -->
|
|
(
|
|
{ Uni = assign(Left, Right) },
|
|
unify_gen__generate_assignment(Left, Right, Code)
|
|
;
|
|
{ Uni = construct(Var, ConsId, Args, Modes) },
|
|
unify_gen__generate_construction(Var, ConsId, Args,
|
|
Modes, Code)
|
|
;
|
|
{ Uni = deconstruct(Var, ConsId, Args, Modes, _) },
|
|
unify_gen__generate_semi_deconstruction(Var, ConsId, Args,
|
|
Modes, Code)
|
|
;
|
|
{ Uni = simple_test(Var1, Var2) },
|
|
unify_gen__generate_test(Var1, Var2, Code)
|
|
;
|
|
{ Uni = complicated_unify(_UniMode, _CanFail) },
|
|
{ error("code_gen__generate_semi_goal_2 - complicated_unify") }
|
|
).
|
|
|
|
code_gen__generate_semi_goal_2(pragma_c_code(C_Code, IsRecursive,
|
|
PredId, ModeId, Args, ArgNameMap), GoalInfo, Instr) -->
|
|
code_gen__generate_pragma_c_code(model_semi, C_Code, IsRecursive,
|
|
PredId, ModeId, Args, ArgNameMap, GoalInfo, Instr).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- 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(CodeModel, Goal0, Code) -->
|
|
{ Goal0 = GoalExpr - GoalInfo0 },
|
|
{ goal_info_get_resume_point(GoalInfo0, Resume) },
|
|
(
|
|
{ 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__failure_is_direct_branch(CodeAddr),
|
|
code_info__get_globals(Globals),
|
|
{ globals__lookup_bool_option(Globals, simple_neg, yes) }
|
|
->
|
|
% Because we're generating a goal
|
|
% (special-cased, though it may be)
|
|
% we need to apply the pre- and post-
|
|
% updates.
|
|
code_info__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),
|
|
{ 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__post_goal_update(GoalInfo),
|
|
{ Code = tree(tree(CodeL, CodeR), TestCode) }
|
|
;
|
|
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,
|
|
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, 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),
|
|
{
|
|
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, constraints, Constraints) },
|
|
code_info__maybe_save_ticket(Constraints, SaveTicketCode,
|
|
MaybeTicketSlot),
|
|
|
|
% 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__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_and_discard_ticket(MaybeTicketSlot,
|
|
RestoreTicketCode),
|
|
code_info__maybe_restore_and_discard_hp(MaybeHpSlot, RestoreHpCode),
|
|
{ Code = tree(ModContCode,
|
|
tree(SaveHpCode,
|
|
tree(SaveTicketCode,
|
|
tree(GoalCode,
|
|
tree(FailCode,
|
|
tree(RestoreContCode,
|
|
tree(RestoreTicketCode,
|
|
RestoreHpCode))))))) }.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred code_gen__generate_non_goal_2(hlds__goal_expr, hlds__goal_info,
|
|
code_tree, code_info, code_info).
|
|
:- mode code_gen__generate_non_goal_2(in, in, out, in, out) is det.
|
|
|
|
code_gen__generate_non_goal_2(conj(Goals), _GoalInfo, Code) -->
|
|
code_gen__generate_goals(Goals, model_non, Code).
|
|
code_gen__generate_non_goal_2(some(_Vars, Goal), _GoalInfo, Code) -->
|
|
{ Goal = _ - InnerGoalInfo },
|
|
{ goal_info_get_code_model(InnerGoalInfo, CodeModel) },
|
|
code_gen__generate_goal(CodeModel, Goal, Code).
|
|
code_gen__generate_non_goal_2(disj(Goals, StoreMap), _GoalInfo, Code) -->
|
|
disj_gen__generate_non_disj(Goals, StoreMap, Code).
|
|
code_gen__generate_non_goal_2(not(_Goal), _GoalInfo, _Code) -->
|
|
{ error("Cannot have a nondet negation.") }.
|
|
code_gen__generate_non_goal_2(higher_order_call(PredVar, Args, Types, Modes,
|
|
Det),
|
|
_CodeInfo, Code) -->
|
|
call_gen__generate_higher_order_call(model_non, PredVar, Args, Types,
|
|
Modes, Det, Code).
|
|
code_gen__generate_non_goal_2(call(PredId, ProcId, Args, BuiltinState, _, _),
|
|
_GoalInfo, Code) -->
|
|
(
|
|
{ BuiltinState = not_builtin }
|
|
->
|
|
code_info__succip_is_used,
|
|
call_gen__generate_nondet_call(PredId, ProcId, Args, Code)
|
|
;
|
|
call_gen__generate_nondet_builtin(PredId, ProcId, Args, Code)
|
|
).
|
|
code_gen__generate_non_goal_2(switch(Var, CanFail, CaseList, StoreMap),
|
|
GoalInfo, Instr) -->
|
|
switch_gen__generate_switch(model_non, Var, CanFail,
|
|
CaseList, StoreMap, GoalInfo, Instr).
|
|
code_gen__generate_non_goal_2(
|
|
if_then_else(_Vars, CondGoal, ThenGoal, ElseGoal, StoreMap),
|
|
_GoalInfo, Instr) -->
|
|
ite_gen__generate_nondet_ite(CondGoal, ThenGoal, ElseGoal,
|
|
StoreMap, Instr).
|
|
code_gen__generate_non_goal_2(unify(_L, _R, _U, _Uni, _C),
|
|
_GoalInfo, _Code) -->
|
|
{ error("Cannot have a nondet unification.") }.
|
|
code_gen__generate_non_goal_2(pragma_c_code(A, B, C, D, E, F), G, H) -->
|
|
% it would make sense to abort, but we need to handle string__append,
|
|
% which is implemented using pragma c_code, and whose reverse mode
|
|
% is multidet (see library/string.m)
|
|
code_gen__generate_det_goal_2(pragma_c_code(A, B, C, D, E, F), G, H).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
code_gen__output_args([], LiveVals) :-
|
|
set__init(LiveVals).
|
|
code_gen__output_args([_V - arg_info(Loc, Mode) | Args], Vs) :-
|
|
code_gen__output_args(Args, Vs0),
|
|
(
|
|
Mode = top_out
|
|
->
|
|
code_util__arg_loc_to_register(Loc, Reg),
|
|
set__insert(Vs0, reg(Reg), Vs)
|
|
;
|
|
Vs = Vs0
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
% Add the succip to the livevals before and after calls.
|
|
% Traverses the list of instructions looking for livevals and calls,
|
|
% adding succip in the stackvar number given as an argument.
|
|
|
|
:- pred code_gen__add_saved_succip(list(instruction), int, list(instruction)).
|
|
:- mode code_gen__add_saved_succip(in, in, out) is det.
|
|
|
|
code_gen__add_saved_succip([], _StackLoc, []).
|
|
code_gen__add_saved_succip([Instrn0 - Comment | Instrns0 ], StackLoc,
|
|
[Instrn - Comment | Instrns]) :-
|
|
(
|
|
Instrn0 = livevals(LiveVals0),
|
|
Instrns0 \= [goto(succip) - _ | _]
|
|
% XXX we should also test for tailcalls
|
|
% once we start generating them directly
|
|
->
|
|
set__insert(LiveVals0, stackvar(StackLoc), LiveVals1),
|
|
Instrn = livevals(LiveVals1)
|
|
;
|
|
Instrn0 = call(Target, ReturnLabel, LiveVals0, CM)
|
|
->
|
|
Instrn = call(Target, ReturnLabel,
|
|
[live_lvalue(stackvar(StackLoc), succip, no) |
|
|
LiveVals0], CM)
|
|
;
|
|
Instrn = Instrn0
|
|
),
|
|
code_gen__add_saved_succip(Instrns0, StackLoc, Instrns).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|