Files
mercury/compiler/code_gen.m
Zoltan Somogyi d10af74168 This change introduces interface tracing, and makes it possible to successfully
Estimated hours taken: 50

This change introduces interface tracing, and makes it possible to successfully
bootstrap the compiler with tracing (either interface or full).

compiler/options.m:
	Change the bool options --generate-trace into a string option --trace
	with three valid values: minimal, interface and full. The last two mean
	what they say; the intention is that eventually minimal will mean
	no tracing in non-tracing grades and interface tracing in tracing
	grades.

compiler/globals.m:
	Add a new global for the trace level.

compiler/handle_options.m:
	Convert the argument of --trace to a trace level.

	Use only consistent 4-space indentation in the deeply nested
	if-then-else.

compiler/trace.m:
	Implement interface tracing.

	Rename trace__generate_depth_reset_code as trace__prepare_for_call,
	since it does more than reset the depth if this module is compiled
	with interface tracing.

	Do not check whether tracing is enabled before calling MR_trace;
	let MR_trace make the check. This trades increased non-tracing
	execution time for a substantial code size reduction (which may
	in turn benefit execution time).

compiler/call_gen.m:
	Call trace__generate_depth_reset_code by its new name.

compiler/code_info.m:
	Fix a bug in the handling of non/semi commits. When entering a commit,
	we used to push a clone of whatever the top failure continuation was.
	However, the resume setup for this continuation could have started
	with a label that assumed that the resume vars were in their original
	locations (which are often registers), whereas the method of
	backtracking to that point only guarantees the survival of stack slots,
	not registers.

	(This bug caused two lines of incorrect code to be generated among
	the approx 30 million lines of code in the stage 2 compiler when
	compiled with tracing.)

	Fix another bug (previously untriggered as far as I know) in the
	handling of multi/det commits. This one was breaking the invariant
	that the resume vars set of each entry on the failure continuation
	stack included the resume vars set of every other entry below it,
	which meant that the values of these resume vars were not guaranteed
	to be preserved.

compiler/stack_layout.m:
	Make layout structures local to their module. They are not (yet)
	referred to by name from other modules, and by declaring them
	to be global we caused their names to be included even in stripped
	executables, adding several megabytes to the size of the binary.
	(The names are not stripped because a dynamically linked library
	may want to refer to them.)

	Change the mercury_data__stack_layout__ prefix on the names of
	generated globals vars to just mercury_data__layout__. It is now
	merely too long instead of far too long.

	Include the label number in the label layout structure and the number
	of typeinfo variables in a var_info structure only with native gc.
	Their only use is in debugging native gc.

	Fix some documentation rot.

compiler/llds.m:
	Add a new field to the pragma_c instruction that says whether the
	compiler-generated C code fragments access any stack variables.

compiler/frameopt.m:
	Use the new field in pragma_c's to avoid a bug. Because frameopt was
	assuming that the pragma_c instruction that filled in the stack slots
	containing the call sequence number and depth did not access the stack,
	it moved the pragma_c before the incr_sp that allocates the frame
	(it was trying to get it out of the loop).

compiler/*.m:
	Minor changes to set or ignore the extra field in pragma_c, to refer
	to layout structures via the new prefix, or to handle the --trace
	option.

doc/user_guide.texi:
	Update the documentation for --trace.

runtime/mercury_types.h:
	Add the type Unsigned.

runtime/mercury_goto.h:
	Use the shorter layout prefix.

runtime/mercury_stack_layout.h:
	Use the shorter layout prefix, and include the label number only with
	native gc.

runtime/mercury_trace.[ch]:
runtime/mercury_trace_internal.[ch]:
runtime/mercury_trace_external.[ch]:
runtime/mercury_trace_util.[ch]:
	Divide the old mercury_trace.[ch] into several components, with one
	module for the internal debugger, one for the interface to the
	external debugger, one for utilities needed by both. Mercury_trace.c
	now has only the top-level stuff that steers between the two
	debuggers.

runtime/mercury_trace.[ch]:
	Add the new global variable MR_trace_from_full. Before each call,
	the calling procedure assigns TRUE to this variable if the caller
	is fully traced, and FALSE otherwise. Interface traced procedures
	generate trace events only if this variable is TRUE when they are
	called (fully traced callee procedures ignore the initial value of
	the variable).

	Make MR_trace return immediately without doing anything unless
	tracing is enabled and a new extra argument to MR_trace is TRUE.
	This extra argument is always TRUE for trace events in fully traced
	procedures, while for trace events from interface traced procedures,
	its value is set from the value of MR_trace_from_full at the time
	that the procedure was called (i.e. the event is ignored unless the
	interface traced procedure was called from a fully traced procedure).

runtime/mercury_trace.[ch]:
runtime/mercury_trace_internal.[ch]:
	For global variables that are stored in stack slots, make their type
	Word rather than int.

	Use a new function MR_trace_event_report instead of calling
	MR_trace_event with a NULL command structure pointer to indicate
	that the event is to be reported but there is to be no user
	interaction.

	Use %ld formats in printfs and casts to long for better portability.

runtime/mercury_trace_internal.c:
	Save trace-related globals across calls to Mercury library code
	in the debugger, since otherwise any trace events in this code
	could screw up e.g. the event number or the call number sequence.

	Create separate functions for printing port names and determinisms.

runtime/mercury_wrapper.h:
	Disable the tracing of the initialization and finalization code
	written in Mercury.

runtime/Mmakefile:
	Update for the new source and header files.

tests/debugger/{debugger_regs,interpreter,queens}_lib.{m,inp,exp}:
	One new copy of each existing test case. These ones are intended
	to be used when the stage 2 library is compiled with tracing, which
	affects the tests by adding events for the library procedures called
	from the test programs.

	The .m files are the same as before; one of the .inp files is a bit
	different; the .exp files reflect the correct output when the library
	is compiled with full tracing.

tests/debugger/Mmakefile:
	Provide separate targets for the new set of test cases.

	Use --trace full instead of --generate-trace.

tests/debugger/runtests:
	Try both the new set of test cases if the old set fails, and report
	failure only if both sets fail. This is simpler than trying to figure
	out which set should be really tested, and the probability of a false
	positive is negligible.
1998-05-16 07:31:33 +00:00

1223 lines
42 KiB
Mathematica

%---------------------------------------------------------------------------%
% Copyright (C) 1996-1998 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.
%---------------------------------------------------------------------------%
%
% 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, disjunctions by disj_gen,
% and pragma_c_codes by pragma_c_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.
:- import_module continuation_info, globals.
:- import_module set, list, assoc_list, term, 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, globals,
continuation_info, int, continuation_info, int, c_procedure).
:- mode generate_proc_code(in, in, in, in, in, in, in, out, out, out) is det.
% 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, disj_gen.
:- import_module pragma_c_gen, trace, options, hlds_out.
:- import_module code_aux, middle_rec, passes_aux, llds_out.
:- import_module code_util, type_util, mode_util.
:- import_module prog_data, prog_out, instmap.
:- import_module bool, char, int, string.
:- import_module map, tree, std_util, require, 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_continuation_info(ModuleInfo0, ContInfo0) },
{ module_info_get_cell_count(ModuleInfo0, CellCount0) },
globals__io_get_globals(Globals),
{ generate_proc_list_code(ProcIds, PredId, PredInfo, ModuleInfo0,
Globals, ContInfo0, ContInfo, CellCount0, CellCount,
[], Code) },
{ module_info_set_cell_count(ModuleInfo0, CellCount, ModuleInfo1) },
{ module_info_set_continuation_info(ModuleInfo1, ContInfo,
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,
globals, continuation_info, continuation_info, int, int,
list(c_procedure), list(c_procedure)).
% :- mode generate_proc_list_code(in, in, in, in, in, di, uo, di, uo)
% is det.
:- mode generate_proc_list_code(in, in, in, in, in, in, out, in, out, in, out)
is det.
generate_proc_list_code([], _PredId, _PredInfo, _ModuleInfo, _Globals,
ContInfo, ContInfo, CellCount, CellCount, Procs, Procs).
generate_proc_list_code([ProcId | ProcIds], PredId, PredInfo, ModuleInfo0,
Globals, ContInfo0, ContInfo, CellCount0, CellCount,
Procs0, Procs) :-
pred_info_procedures(PredInfo, ProcInfos),
% locate the proc_info structure for this mode of the predicate
map__lookup(ProcInfos, ProcId, ProcInfo),
generate_proc_code(ProcInfo, ProcId, PredId, ModuleInfo0, Globals,
ContInfo0, CellCount0, ContInfo1, CellCount1, Proc),
generate_proc_list_code(ProcIds, PredId, PredInfo, ModuleInfo0,
Globals, ContInfo1, ContInfo, CellCount1, CellCount,
[Proc | Procs0], Procs).
%---------------------------------------------------------------------------%
% Values of this type hold information about stack frames that is
% generated when generating prologs and is used in generating epilogs
% and when massaging the code generated for the procedure.
:- type frame_info ---> frame(
int, % Number of slots in frame.
maybe(int), % Slot number of succip
% if succip is present
% in a general slot.
bool % Is this the frame of a
% model_non proc defined
% via pragma C code?
).
%---------------------------------------------------------------------------%
generate_proc_code(ProcInfo, ProcId, PredId, ModuleInfo, Globals,
ContInfo0, CellCount0, ContInfo, CellCount, Proc) :-
% find out if the proc is deterministic/etc
proc_info_interface_determinism(ProcInfo, Detism),
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_varset(ProcInfo, VarSet),
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__lookup_bool_option(Globals, basic_stack_layout,
BasicStackLayout),
( BasicStackLayout = yes ->
SaveSuccip = yes
;
SaveSuccip = no
),
% initialise the code_info structure
code_info__init(VarSet, Liveness, StackSlots, SaveSuccip, Globals,
PredId, ProcId, ProcInfo, InitialInst, FollowVars,
ModuleInfo, CellCount0, CodeInfo0),
% generate code for the procedure
globals__get_trace_level(Globals, TraceLevel),
code_util__make_proc_label(ModuleInfo, PredId, ProcId, ProcLabel),
(
( TraceLevel = interface ; TraceLevel = full )
->
trace__setup(TraceLevel, CodeInfo0, CodeInfo1)
;
CodeInfo1 = CodeInfo0
),
generate_category_code(CodeModel, Goal, ProcInfo, CodeTree,
MaybeTraceCallLabel, FrameInfo, CodeInfo1, CodeInfo),
% extract the new continuation_info and cell count
code_info__get_cell_count(CellCount, 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),
FrameInfo = frame(TotalSlots, MaybeSuccipSlot, _),
(
MaybeSuccipSlot = yes(SuccipSlot)
->
code_gen__add_saved_succip(Instructions0,
SuccipSlot, Instructions)
;
Instructions = Instructions0
),
( BasicStackLayout = yes ->
code_info__get_layout_info(LayoutInfo, CodeInfo, _CodeInfo2),
continuation_info__add_proc_info(proc(PredId, ProcId),
ProcLabel, TotalSlots, Detism, MaybeSuccipSlot,
MaybeTraceCallLabel, LayoutInfo, ContInfo0, ContInfo)
;
ContInfo = ContInfo0
),
% 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, proc(PredId, ProcId), Instructions).
%---------------------------------------------------------------------------%
% Generate_category_code generates code for an entire procedure.
% Its algorithm has three or four main stages:
%
% - generate code for the body goal
% - generate code for the procedure entry
% - generate code for the procedure exit
% - generate code for the procedure fail (if needed)
%
% The first three tasks are forwarded to other procedures.
% The fourth task, if needed, is done by generate_category_code.
%
% The only caller of generate_category_code, generate_proc_code,
% has set up the code generator state to reflect what the machine
% state will be on entry to the procedure. Ensuring that the
% machine state at exit will conform to the expectation
% of the caller is the job of code_gen__generate_exit.
%
% The reason why we generate the entry code after the body is that
% information such as the total number of stack slots needed,
% which is needed in the procedure entry prologue, cannot be
% conveniently obtained before generating the body, since the
% code generator may allocate temporary variables to hold values
% such as saved heap and trail pointers.
%
% Code_gen__generate_entry cannot depend on the code generator
% state, since when it is invoked this state is not appropriate
% for the procedure entry. Nor can it change the code generator state,
% since that would confuse code_gen__generate_exit.
%
% Generating CALL trace events is done by generate_category_code,
% since only on entry to generate_category_code is the code generator
% state set up right. Generating EXIT trace events is done by
% code_gen__generate_exit. Generating FAIL trace events is done
% by generate_category_code, since this requires modifying how
% we generate code for the body of the procedure (failures must
% now branch to a different place). Since FAIL trace events are
% part of the failure continuation, generate_category_code takes
% care of the failure continuation as well. (Model_det procedures
% of course have no failure continuation. Model_non procedures have
% a failure continuation, but in the absence of tracing this
% continuation needs no code. Only model_semi procedures need code
% for the failure continuation at all times.)
:- pred generate_category_code(code_model, hlds_goal, proc_info, code_tree,
maybe(label), frame_info, code_info, code_info).
:- mode generate_category_code(in, in, in, out, out, out, in, out) is det.
generate_category_code(model_det, Goal, ProcInfo, Code,
MaybeTraceCallLabel, FrameInfo) -->
% 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, MiddleRecCode)
->
{ Code = MiddleRecCode },
{ MaybeTraceCallLabel = no },
{ FrameInfo = frame(0, no, 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_info__get_maybe_trace_info(MaybeTraceInfo),
( { MaybeTraceInfo = yes(TraceInfo) } ->
code_info__get_module_info(ModuleInfo),
{ trace__fail_vars(ModuleInfo, ProcInfo, ResumeVars) },
% Protect these vars from being forgotten,
% so they will be around for the exit trace.
code_info__push_resume_point_vars(ResumeVars),
trace__generate_external_event_code(call, TraceInfo,
TraceCallLabel, _TypeInfos, TraceCallCode),
{ MaybeTraceCallLabel = yes(TraceCallLabel) }
;
{ TraceCallCode = empty },
{ MaybeTraceCallLabel = no }
),
code_gen__generate_goal(model_det, Goal, BodyCode),
code_gen__generate_entry(model_det, Goal, FrameInfo,
EntryCode),
code_gen__generate_exit(model_det, FrameInfo, _, ExitCode),
{ Code =
tree(EntryCode,
tree(TraceCallCode,
tree(BodyCode,
ExitCode)))
}
).
generate_category_code(model_semi, Goal, ProcInfo, Code,
MaybeTraceCallLabel, FrameInfo) -->
% make a new failure cont (not model_non)
code_info__manufacture_failure_cont(no),
code_info__get_maybe_trace_info(MaybeTraceInfo),
{ set__singleton_set(FailureLiveRegs, reg(r, 1)) },
{ FailCode = node([
assign(reg(r, 1), const(false)) - "Fail",
livevals(FailureLiveRegs) - "",
goto(succip) - "Return from procedure call"
]) },
( { MaybeTraceInfo = yes(TraceInfo) } ->
code_info__get_module_info(ModuleInfo),
{ trace__fail_vars(ModuleInfo, ProcInfo, ResumeVars) },
code_info__make_known_failure_cont(ResumeVars, orig_and_stack,
no, SetupCode),
code_info__push_resume_point_vars(ResumeVars),
trace__generate_external_event_code(call, TraceInfo,
TraceCallLabel, _TypeInfos, TraceCallCode),
{ MaybeTraceCallLabel = yes(TraceCallLabel) },
code_gen__generate_goal(model_semi, Goal, BodyCode),
code_gen__generate_entry(model_semi, Goal, FrameInfo,
EntryCode),
code_gen__generate_exit(model_semi, FrameInfo,
RestoreDeallocCode, ExitCode),
code_info__pop_resume_point_vars,
code_info__restore_failure_cont(ResumeCode),
code_info__set_forward_live_vars(ResumeVars),
trace__generate_external_event_code(fail, TraceInfo, _, _,
TraceFailCode),
{ Code =
tree(EntryCode,
tree(SetupCode,
tree(TraceCallCode,
tree(BodyCode,
tree(ExitCode,
tree(ResumeCode,
tree(TraceFailCode,
tree(RestoreDeallocCode,
FailCode))))))))
}
;
{ MaybeTraceCallLabel = no },
code_gen__generate_goal(model_semi, Goal, BodyCode),
code_gen__generate_entry(model_semi, Goal, FrameInfo,
EntryCode),
code_gen__generate_exit(model_semi, FrameInfo,
RestoreDeallocCode, ExitCode),
code_info__restore_failure_cont(ResumeCode),
{ Code =
tree(EntryCode,
tree(BodyCode,
tree(ExitCode,
tree(ResumeCode,
tree(RestoreDeallocCode,
FailCode)))))
}
).
generate_category_code(model_non, Goal, ProcInfo, Code,
MaybeTraceCallLabel, FrameInfo) -->
% make a new failure cont (yes, it is model_non)
code_info__manufacture_failure_cont(yes),
% we must arrange the tracing of failure out of this proc
code_info__get_maybe_trace_info(MaybeTraceInfo),
( { MaybeTraceInfo = yes(TraceInfo) } ->
code_info__get_module_info(ModuleInfo),
{ trace__fail_vars(ModuleInfo, ProcInfo, ResumeVars) },
code_info__make_known_failure_cont(ResumeVars, orig_and_stack,
yes, SetupCode),
code_info__push_resume_point_vars(ResumeVars),
trace__generate_external_event_code(call, TraceInfo,
TraceCallLabel, _TypeInfos, TraceCallCode),
{ MaybeTraceCallLabel = yes(TraceCallLabel) },
code_gen__generate_goal(model_non, Goal, BodyCode),
code_gen__generate_entry(model_non, Goal, FrameInfo,
PrologCode),
code_gen__generate_exit(model_non, FrameInfo, _, EpilogCode),
code_info__pop_resume_point_vars,
code_info__restore_failure_cont(RestoreCode),
code_info__set_forward_live_vars(ResumeVars),
trace__generate_external_event_code(fail, TraceInfo, _, _,
TraceFailCode),
code_info__generate_failure(FailCode),
{ Code =
tree(PrologCode,
tree(SetupCode,
tree(TraceCallCode,
tree(BodyCode,
tree(EpilogCode,
tree(RestoreCode,
tree(TraceFailCode,
FailCode)))))))
}
;
{ MaybeTraceCallLabel = no },
code_gen__generate_goal(model_non, Goal, BodyCode),
code_gen__generate_entry(model_non, Goal, FrameInfo,
PrologCode),
code_gen__generate_exit(model_non, FrameInfo, _, EpilogCode),
{ Code =
tree(PrologCode,
tree(BodyCode,
EpilogCode))
}
).
%---------------------------------------------------------------------------%
% Generate the prologue for a procedure.
%
% The prologue will contain
%
% a comment to mark prologue start
% a comment explaining the stack layout
% the procedure entry label
% code to allocate a stack frame
% code to fill in some special slots in the stack frame
% a comment to mark prologue end
%
% At the moment the only special slots are the succip slot, and
% the slots holding the call number and call depth for tracing.
%
% Not all frames will have all these components. For example, the code
% to allocate a stack frame will be missing if the procedure doesn't
% need a stack frame, and if the procedure is nondet, then the code
% to fill in the succip slot is subsumed by the mkframe.
:- pred code_gen__generate_entry(code_model, hlds_goal, frame_info,
code_tree, code_info, code_info).
:- mode code_gen__generate_entry(in, in, out, out, in, out) is det.
code_gen__generate_entry(CodeModel, Goal, FrameInfo, PrologCode) -->
code_info__get_stack_slots(StackSlots),
code_info__get_varset(VarSet),
{ code_aux__explain_stack_slots(StackSlots, VarSet, SlotsComment) },
{ StartComment = node([
comment("Start of procedure prologue") - "",
comment(SlotsComment) - ""
]) },
code_info__get_total_stackslot_count(MainSlots),
code_info__get_pred_id(PredId),
code_info__get_proc_id(ProcId),
code_info__get_module_info(ModuleInfo),
{ code_util__make_local_entry_label(ModuleInfo, PredId, ProcId, no,
Entry) },
{ LabelCode = node([
label(Entry) - "Procedure entry point"
]) },
code_info__get_succip_used(Used),
(
% Do we need to save the succip across calls?
{ Used = yes },
% Do we need to use a general slot for storing succip?
{ CodeModel \= model_non }
->
{ SuccipSlot is MainSlots + 1 },
{ SaveSuccipCode = node([
assign(stackvar(SuccipSlot), lval(succip)) -
"Save the success ip"
]) },
{ TotalSlots = SuccipSlot },
{ MaybeSuccipSlot = yes(SuccipSlot) }
;
{ SaveSuccipCode = empty },
{ TotalSlots = MainSlots },
{ MaybeSuccipSlot = no }
),
code_info__get_maybe_trace_info(MaybeTraceInfo),
( { MaybeTraceInfo = yes(TraceInfo) } ->
{ trace__generate_slot_fill_code(TraceInfo, TraceFillCode) }
;
{ TraceFillCode = empty }
),
{ predicate_module(ModuleInfo, PredId, ModuleName) },
{ predicate_name(ModuleInfo, PredId, PredName) },
{ predicate_arity(ModuleInfo, PredId, Arity) },
{ prog_out__sym_name_to_string(ModuleName, ModuleNameString) },
{ string__int_to_string(Arity, ArityStr) },
{ string__append_list([ModuleNameString, ":", PredName, "/", ArityStr],
PushMsg) },
(
{ CodeModel = model_non }
->
(
{ Goal = pragma_c_code(_,_,_,_,_,_, PragmaCode) - _},
{ PragmaCode = nondet(Fields, FieldsContext,
_,_,_,_,_,_,_) }
->
{ pragma_c_gen__struct_name(ModuleName, PredName,
Arity, ProcId, StructName) },
{ Struct = pragma_c_struct(StructName,
Fields, FieldsContext) },
{ string__format("#define\tMR_ORDINARY_SLOTS\t%d\n",
[i(TotalSlots)], DefineStr) },
{ DefineComponents = [pragma_c_raw_code(DefineStr)] },
{ AllocCode = node([
mkframe(PushMsg, TotalSlots, yes(Struct),
do_fail)
- "Allocate stack frame",
pragma_c([], DefineComponents,
will_not_call_mercury, no, no)
- ""
]) },
{ NondetPragma = yes }
;
{ AllocCode = node([
mkframe(PushMsg, TotalSlots, no, do_fail) -
"Allocate stack frame"
]) },
{ NondetPragma = no }
)
;
{ TotalSlots > 0 }
->
{ AllocCode = node([
incr_sp(TotalSlots, PushMsg) -
"Allocate stack frame"
]) },
{ NondetPragma = no }
;
{ AllocCode = empty },
{ NondetPragma = no }
),
{ FrameInfo = frame(TotalSlots, MaybeSuccipSlot, NondetPragma) },
{ EndComment = node([
comment("End of procedure prologue") - ""
]) },
{ PrologCode =
tree(StartComment,
tree(LabelCode,
tree(AllocCode,
tree(SaveSuccipCode,
tree(TraceFillCode,
EndComment)))))
}.
%---------------------------------------------------------------------------%
% Generate the success epilogue for a procedure.
%
% The success epilogue will contain
%
% a comment to mark epilogue start
% code to place the output arguments where their caller expects
% code to restore registers from some special slots
% code to deallocate the stack frame
% code to set r1 to TRUE (for semidet procedures only)
% a jump back to the caller, including livevals information
% a comment to mark epilogue end
%
% The parts of this that restore registers and deallocate the stack
% frame are also part of the failure epilog, which is handled by
% our caller; this is why we return RestoreDeallocCode.
%
% At the moment the only special slots are the succip slot, and
% the slots holding the call number and call depth for tracing.
%
% Not all frames will have all these components. For example, for
% nondet procedures we don't deallocate the stack frame before
% success.
%
% Epilogues for procedures defined by nondet pragma C codes do not
% follow the rules above. For such procedures, the normal functions
% of the epilogue are handled when traversing the pragma C code goal;
% we need only #undef a macro defined by the procedure prologue.
:- pred code_gen__generate_exit(code_model, frame_info, code_tree, code_tree,
code_info, code_info).
:- mode code_gen__generate_exit(in, in, out, out, in, out) is det.
code_gen__generate_exit(CodeModel, FrameInfo, RestoreDeallocCode, EpilogCode)
-->
{ StartComment = node([
comment("Start of procedure epilogue") - ""
]) },
{ EndComment = node([
comment("End of procedure epilogue") - ""
]) },
{ FrameInfo = frame(TotalSlots, MaybeSuccipSlot, NondetPragma) },
( { NondetPragma = yes } ->
{ UndefStr = "#undef\tMR_ORDINARY_SLOTS\n" },
{ UndefComponents = [pragma_c_raw_code(UndefStr)] },
{ UndefCode = node([
pragma_c([], UndefComponents,
will_not_call_mercury, no, no)
- ""
]) },
{ RestoreDeallocCode = empty }, % always empty for nondet code
{ EpilogCode =
tree(StartComment,
tree(UndefCode,
EndComment))
}
;
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) }
->
{ FlushCode = empty }
;
code_info__setup_call(Args, callee, FlushCode)
),
(
{ MaybeSuccipSlot = yes(SuccipSlot) }
->
{ RestoreSuccipCode = node([
assign(succip, lval(stackvar(SuccipSlot))) -
"restore the success ip"
]) }
;
{ RestoreSuccipCode = empty }
),
(
{ TotalSlots = 0 ; CodeModel = model_non }
->
{ DeallocCode = empty }
;
{ DeallocCode = node([
decr_sp(TotalSlots) - "Deallocate stack frame"
]) }
),
{ RestoreDeallocCode = tree(RestoreSuccipCode, DeallocCode ) },
code_info__get_maybe_trace_info(MaybeTraceInfo),
( { MaybeTraceInfo = yes(TraceInfo) } ->
trace__generate_external_event_code(exit, TraceInfo,
_, TypeInfoDatas, TraceExitCode),
{ assoc_list__values(TypeInfoDatas, TypeInfoLvals) }
;
{ TraceExitCode = empty },
{ TypeInfoLvals = [] }
),
% Find out which locations should be mentioned
% in the success path livevals(...) annotation,
% so that value numbering doesn't optimize them away.
{ code_gen__select_args_with_mode(Args, top_out, _OutVars,
OutLvals) },
{ list__append(TypeInfoLvals, OutLvals, LiveArgLvals) },
{ set__list_to_set(LiveArgLvals, LiveArgs) },
(
{ CodeModel = model_det },
{ SuccessCode = node([
livevals(LiveArgs) - "",
goto(succip) - "Return from procedure call"
]) },
{ AllSuccessCode =
tree(TraceExitCode,
tree(RestoreDeallocCode,
SuccessCode))
}
;
{ CodeModel = model_semi },
{ set__insert(LiveArgs, reg(r, 1), SuccessLiveRegs) },
{ SuccessCode = node([
assign(reg(r, 1), const(true)) - "Succeed",
livevals(SuccessLiveRegs) - "",
goto(succip) - "Return from procedure call"
]) },
{ AllSuccessCode =
tree(TraceExitCode,
tree(RestoreDeallocCode,
SuccessCode))
}
;
{ CodeModel = model_non },
{ SuccessCode = node([
livevals(LiveArgs) - "",
goto(do_succeed(no))
- "Return from procedure call"
]) },
{ AllSuccessCode =
tree(TraceExitCode,
SuccessCode)
}
),
{ EpilogCode =
tree(StartComment,
tree(FlushCode,
tree(AllSuccessCode,
EndComment)))
}
).
%---------------------------------------------------------------------------%
% 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, Code)
;
{ CodeModel = model_semi },
( { ContextModel \= model_det } ->
code_gen__generate_semi_goal_2(Goal, GoalInfo,
Code)
;
{ error("semidet model in det context") }
)
;
{ CodeModel = model_non },
( { ContextModel = model_non } ->
code_gen__generate_non_goal_2(Goal, GoalInfo,
Code)
;
{ 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 = empty }
),
!.
%---------------------------------------------------------------------------%
% Generate a conjoined series of goals.
% Note of course, that with a conjunction, state information
% flows directly from one conjunct to the next.
:- 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(Slots, PreCommit),
code_gen__generate_goal(model_non, Goal, GoalCode),
code_info__generate_det_commit(Slots, 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, _PredOrFunc),
GoalInfo, Instr) -->
call_gen__generate_higher_order_call(model_det, PredVar, Args,
Types, Modes, Det, GoalInfo, Instr).
code_gen__generate_det_goal_2(class_method_call(TCVar, Num, Args, Types,
Modes, Det),
GoalInfo, Instr) -->
call_gen__generate_class_method_call(model_det, TCVar, Num, Args,
Types, Modes, Det, GoalInfo, 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_call(model_det, PredId, ProcId, Args,
GoalInfo, 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(MayCallMercury,
PredId, ModeId, Args, ArgNames, OrigArgTypes, PragmaCode),
GoalInfo, Instr) -->
pragma_c_gen__generate_pragma_c_code(model_det, MayCallMercury,
PredId, ModeId, Args, ArgNames, OrigArgTypes, GoalInfo,
PragmaCode, Instr).
%---------------------------------------------------------------------------%
:- 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, Slots, PreCommit),
code_gen__generate_goal(model_non, Goal, GoalCode),
code_info__generate_semi_commit(Label, Slots, 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, _PredOrFunc), GoalInfo, Code) -->
call_gen__generate_higher_order_call(model_semi, PredVar, Args,
Types, Modes, Det, GoalInfo, Code).
code_gen__generate_semi_goal_2(class_method_call(TCVar, Num, Args, Types, Modes,
Det), GoalInfo, Code) -->
call_gen__generate_class_method_call(model_semi, TCVar, Num, Args,
Types, Modes, Det, GoalInfo, 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_call(model_semi, PredId, ProcId, Args,
GoalInfo, 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(MayCallMercury,
PredId, ModeId, Args, ArgNames, OrigArgTypes, PragmaCode),
GoalInfo, Instr) -->
pragma_c_gen__generate_pragma_c_code(model_semi, MayCallMercury,
PredId, ModeId, Args, ArgNames, OrigArgTypes, GoalInfo,
PragmaCode, 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.
code_info__make_known_failure_cont(ResumeVars, ResumeLocs, 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, use_trail, UseTrail) },
code_info__maybe_save_ticket(UseTrail, 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 } ->
{ DiscardTicketCode = empty },
{ FailCode = empty }
;
code_info__grab_code_info(CodeInfo),
code_info__pop_failure_cont,
% The call to reset_ticket(..., commit) here is necessary
% in order to properly detect floundering.
code_info__maybe_reset_and_discard_ticket(MaybeTicketSlot,
commit, DiscardTicketCode),
code_info__generate_failure(FailCode),
code_info__slap_code_info(CodeInfo)
),
code_info__restore_failure_cont(RestoreContCode),
code_info__maybe_reset_and_discard_ticket(MaybeTicketSlot, undo,
RestoreTicketCode),
code_info__maybe_restore_and_discard_hp(MaybeHpSlot, RestoreHpCode),
{ Code = tree(ModContCode,
tree(SaveHpCode,
tree(SaveTicketCode,
tree(GoalCode,
tree(DiscardTicketCode, % is this necessary?
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, _PredOrFunc),
GoalInfo, Code) -->
call_gen__generate_higher_order_call(model_non, PredVar, Args, Types,
Modes, Det, GoalInfo, Code).
code_gen__generate_non_goal_2(class_method_call(TCVar, Num, Args, Types, Modes,
Det),
GoalInfo, Code) -->
call_gen__generate_class_method_call(model_non, TCVar, Num, Args, Types,
Modes, Det, GoalInfo, 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_call(model_non, PredId, ProcId, Args,
GoalInfo, 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(MayCallMercury,
PredId, ModeId, Args, ArgNames, OrigArgTypes, PragmaCode),
GoalInfo, Instr) -->
pragma_c_gen__generate_pragma_c_code(model_non, MayCallMercury,
PredId, ModeId, Args, ArgNames, OrigArgTypes, GoalInfo,
PragmaCode, Instr).
%---------------------------------------------------------------------------%
code_gen__output_args(Args, Vs) :-
code_gen__select_args_with_mode(Args, top_out, _, Lvals),
set__list_to_set(Lvals, Vs).
:- pred code_gen__select_args_with_mode(assoc_list(var, arg_info),
arg_mode, list(var), list(lval)).
:- mode code_gen__select_args_with_mode(in, in, out, out) is det.
code_gen__select_args_with_mode([], _, [], []).
code_gen__select_args_with_mode([Var - ArgInfo | Args], DesiredMode, Vs, Ls) :-
code_gen__select_args_with_mode(Args, DesiredMode, Vs0, Ls0),
ArgInfo = arg_info(Loc, Mode),
(
Mode = DesiredMode
->
code_util__arg_loc_to_register(Loc, Reg),
Vs = [Var | Vs0],
Ls = [Reg | Ls0]
;
Vs = Vs0,
Ls = Ls0
).
%---------------------------------------------------------------------------%
% 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
% if we ever 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, "", []) |
LiveVals0], CM)
;
Instrn = Instrn0
),
code_gen__add_saved_succip(Instrns0, StackLoc, Instrns).
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%