Files
mercury/compiler/rl.m
Simon Taylor 18430aaef1 Aditi compilation.
Estimated hours taken: 1200

Aditi compilation.

compiler/options.m:
	The documentation for these is commented out because the Aditi
	system is not currently useful to the general public.
	--aditi: enable Aditi compilation.
	--dump-rl: write the intermediate RL to `<module>.rl_dump'.
	--dump-rl-bytecode: write a text version of the bytecodes
		to `<module>.rla'
	--aditi-only: don't produce a `.c' file.
	--filenames-from-stdin: accept a list of filenames to compile
		from stdin. This is used by the query shell.
	--optimize-rl, --optimize-rl-cse, --optimize-rl-invariants,
	--optimize-rl-index, --detect-rl-streams:
		Options to control RL optimization passes.
	--aditi-user:
		Default owner of any Aditi procedures,
		defaults to $USER or "guest".
	--generate-schemas:
		write schemas for base relations to `<module>'.base_schema
		and schemas for derived relations to `<module>'.derived_schema.
		This is used by the query shell.

compiler/handle_options.m:
	Handle the default for --aditi-user.

compiler/hlds_pred.m:
compiler/prog_data.m:
compiler/prog_io_pragma.m:
compiler/make_hlds.m:
	Add some Aditi pragma declarations - `aditi', `supp_magic', `context',
	`naive', `psn' (predicate semi-naive), `aditi_memo', `aditi_no_memo',
	`base_relation', `owner' and `index'.
	Separate out code to parse a predicate name and arity.

compiler/hlds_pred.m:
	Add predicates to identify Aditi procedures.
	Added markers `generate_inline' and `aditi_interface', which
	are used internally for Aditi code generation.
	Add an `owner' field to pred_infos, which is used for database
	security checks.
	Add a field to pred_infos to hold the list of indexes for a base
	relation.

compiler/make_hlds.m:
	Some pragmas must be exported if the corresponding predicates
	are exported, check this.
	Make sure stratification of Aditi procedures is checked.
	Predicates with a mode declaration but no type declaration
	are no longer assumed to be local.
	Set the `do_aditi_compilation' field of the module_info if there
	are any local Aditi procedures or base relations.
	Check that `--aditi' is set if Aditi compilation is required.

compiler/post_typecheck.m:
	Check that every Aditi predicate has an `aditi__state' argument,
	which is used to ensure sequencing of updates and that Aditi
	procedures are only called within transactions.

compiler/dnf.m:
	Changed the definition of disjunctive normal form slightly
	so that a call followed by some atomic goals not including
	any database calls is considered atomic. magic.m can handle
	this kind of goal, and it results in more efficient RL code.

compiler/hlds_module.m:
compiler/dependency_graph.m:
	Added dependency_graph__get_scc_entry_points which finds
	the procedures in an SCC which could be called from outside.
	Added a new field to the dependency_info, the
	aditi_dependency_ordering. This contains all Aditi SCCs of
	the original program, with multiple SCCs merged where
	possible to improve the effectiveness of differential evaluation
	and the low level RL optimizations.

compiler/hlds_module.m:
	Add a field to record whether there are any local Aditi procedures
	in the current module.
	Added versions of module_info_pred_proc_info and
	module_info_set_pred_proc_info which take a pred_proc_id,
	not a separate pred_id and proc_id.

compiler/polymorphism.m:
compiler/lambda.m:
	Make sure that predicates created for closures in Aditi procedures
	have the correct markers.

compiler/goal_util.m:
	Added goal_util__switch_to_disjunction,
	goal_util__case_to_disjunct (factored out from simplify.m)
	and goal_util__if_then_else_to_disjunction. These are
	require because supplementary magic sets can't handle
	if-then-elses or switches.

compiler/type_util.m:
	Added type_is_aditi_state/1.

compiler/mode_util.m:
	Added partition_args/5 which partitions a list of arguments
	into inputs and others.

compiler/inlining.m:
	Don't inline memoed procedures.
	Don't inline Aditi procedures into non-Aditi procedures.

compiler/intermod.m:
	Handle Aditi markers.
	Clean up handling of markers which should not appear in `.opt' files.

compiler/simplify.m:
	Export a slightly different interface for use by magic.m.
	Remove explicit quantifications where possible.
	Merge multiple nested quantifications.
	Don't report infinite recursion warnings for Aditi procedures.

compiler/prog_out.m:
	Generalised the code to output a module list to write any list.

compiler/code_gen.m:
compiler/arg_info.m:
	Don't process Aditi procedures.

compiler/mercury_compile.m:
	Call magic.m and rl_gen.m.
	Don't perform the low-level annotation passes on Aditi procedures.
	Remove calls to constraint.m - sometime soon a rewritten version
	will be called directly from deforestation.

compiler/passes_aux.m:
	Add predicates to process only non-Aditi procedures.

compiler/llds.m:
compiler/llds_out.m:
	Added new `code_addr' enum members, do_{det,semidet,nondet}_aditi_call,
	which are defined in extras/aditi/aditi.m.

compiler/call_gen.m:
	Handle generation of do_*_aditi_call.

compiler/llds_out.m:
	Write the RL code for the module as a constant char array
	in the `.c' file.

compiler/term_errors.m:
compiler/error_util.m:
	Move code to describe predicates into error_util.m
	Allow the caller to explicitly add line breaks.
	Added error_util:list_to_pieces to format a list of
	strings.
	Reordered some arguments for currying.

compiler/hlds_out.m:
	Don't try to print clauses if there are none.

runtime/mercury_init.h:
util/mkinit.c:
scripts/c2init.in:
	Added a function `mercury__load_aditi_rl_code()' to the generated
	`<module>_init.c' file which throws all the RL code for the program
	at the database. This should be called at connection time by
	`aditi__connect'.
	Added an option `--aditi' which controls the output
	`mercury__load_aditi_rl_code()'.

compiler/notes/compiler_design.html:
	Document the new files.

Mmakefile:
bindist/Mmakefile:
	Don't distribute extras/aditi yet.

New files:

compiler/magic.m:
compiler/magic_util.m:
	Supplementary magic sets transformation. Report errors
	for constructs that Aditi can't handle.

compiler/context.m:
	Supplementary context transformation.

compiler/rl_gen.m:
compiler/rl_relops.m:
	Aditi code generation.

compiler/rl_info.m:
	Code generator state.

compiler/rl.m:
	Intermediate RL representation.

compiler/rl_util:
	Predicates to collect information about RL instructions.

compiler/rl_dump.m:
	Print out the representation in rl.m.

compiler/rl_opt.m:
	Control low-level RL optimizations.

compiler/rl_block.m:
	Break a procedure into basic blocks.

compiler/rl_analyse.m:
	Generic dataflow analysis for RL procedures.

compiler/rl_liveness.m:
	Make sure all relations are initialised before used, clear
	references to relations that are no longer required.

compiler/rl_loop.m:
	Loop invariant removal.

compiler/rl_block_opt.m:
	CSE and instruction merging on basic blocks.

compiler/rl_key.m:
	Detect upper/lower bounds for which a goal could succeed.

compiler/rl_sort.m:
	Use indexing for joins and projections.
	Optimize away unnecessary sorting and indexing.

compiler/rl_stream.m:
	Detect relations which don't need to be materialised.

compiler/rl_code.m:
	RL bytecode definitions. Automatically generated from the Aditi
	header files.

compiler/rl_out.m:
compiler/rl_file.m:
	Output the RL bytecodes in binary to <module>.rlo (for use by Aditi)
	and in text to <module>.rla (for use by the RL interpreter).
	Also output the schema information if --generate-schemas is set.

compiler/rl_exprn.m:
	Generate bytecodes for join conditions.

extras/aditi/Mmakefile:
extras/aditi/aditi.m:
	Definitions of some Aditi library predicates and the
	interfacing and transaction processing code.
1998-12-06 23:49:14 +00:00

643 lines
19 KiB
Mathematica

%-----------------------------------------------------------------------------%
% Copyright (C) 1998 University of Melbourne.
% This file may only be copied under the terms of the GNU General
% Public License - see the file COPYING in the Mercury distribution.
%-----------------------------------------------------------------------------%
% File: rl.m
% Main author: stayl
%
% Intermediate form used for optimization of Aditi-RL code.
%
% Generated by rl_gen.m.
% Human readable debugging output by rl_dump.m.
% Output to RL bytecodes by rl_out.m.
%
%-----------------------------------------------------------------------------%
:- module rl.
:- interface.
:- import_module hlds_data, hlds_goal, hlds_module, hlds_pred.
:- import_module instmap, prog_data.
:- import_module assoc_list, list, std_util, map, set.
%-----------------------------------------------------------------------------%
:- type rl_code == list(rl_proc).
:- type rl_proc
---> rl_proc(
rl_proc_name,
list(relation_id), % input argument relations
list(relation_id), % output argument relations
set(relation_id), % memoed relations
relation_info_map, % all relations used by the procedure
list(rl_instruction),
list(pred_proc_id) % list of Mercury procedures contained
% in this RL procedure
).
:- type rl_proc_name
---> rl_proc_name(
string, % user
string, % module
string, % name
int % arity
).
%-----------------------------------------------------------------------------%
:- type relation_id == int.
:- type relation_info_map == map(relation_id, relation_info).
:- type relation_info
---> relation_info(
relation_type,
list(type), % schema
list(index_spec),
% Only used for base relations - other relations
% may have different indexes at different times.
string % name
).
:- type relation_type
---> permanent(pred_proc_id)
; temporary(relation_state).
% It may be possible that we only want to materialise a relation
% along certain branches. That should be fairly simple to fix later
% if it is necessary.
:- type relation_state
---> materialised
; stream.
%-----------------------------------------------------------------------------%
:- type index_spec
---> index_spec(
index_type,
list(int) % which attributes are being indexed on.
).
% Hash indexes?
:- type index_type
---> unique_B_tree
; non_unique_B_tree.
% A key range gives an upper and lower bound for the part of the
% indexed relation to search. For example, a simple B-tree join
% algorithm takes a tuple from first relation and uses it to build
% a key-range for the (indexed) second relation. The join condition
% is then applied to the tuple from the first relation and every tuple
% in the second which falls within the key range.
:- type key_range
---> key_range(
bounding_tuple, % lower bound
bounding_tuple, % upper bound
maybe(list(type)), % schema of the tuple used to generate
% the key range - there isn't one
% for selects.
list(type) % schema of the tuple used to search
% the B-tree index
).
:- type bounding_tuple
---> infinity % -infinity for lower bound,
% +infinity for upper bound
; bound(
assoc_list(int, key_attr)
% attributes of the key tuple, the
% associated integer is the index
% in a full tuple for that index
% attribute.
)
.
:- type key_attr
---> functor(cons_id, (type), list(key_attr))
; infinity % -infinity for lower bound,
% +infinity for upper
% This is currently not supported,
% since there may not be a way to
% construct a term representing
% infinity.
; input_field(int)
.
%-----------------------------------------------------------------------------%
% instruction and a comment.
:- type rl_instruction == pair(rl_instr, string).
:- type rl_instr
--->
join(
output_rel, % output
relation_id, % input 1
relation_id, % input 2
join_type,
rl_goal % join condition
)
;
subtract( % output = input 1 - input 2
output_rel, % output
relation_id, % input 1
relation_id, % input 2
subtract_type,
rl_goal % subtraction condition
)
;
% A difference is just a special case of subtract.
% The inputs must be sorted and have the same schema.
difference( % output = input 1 - input 2
output_rel, % output
relation_id, % input 1
relation_id, % input 2
difference_type
)
;
% A projection may have any number of output relations to
% avoid multiple traversals over the input relation.
% This also does selection - the expressions are allowed to
% fail.
% All but one of the outputs must be materialised - at the
% moment we materialise them all because it is difficult
% to ensure correctness for streams with side-effects.
project(
output_rel, % output (may be a stream)
relation_id, % input
rl_goal, % projection expression for
% stream output
assoc_list(output_rel, rl_goal),
% other outputs (materialised)
project_type
)
;
union(
output_rel, % output
list(relation_id), % inputs
union_type
)
;
% Output = Input1 U Input2, Difference = Input1 - Input2
% Input1 must have a B-tree index, and is destructively
% updated to create Output.
union_diff(
relation_id, % output (uo) (same indexes as input 1)
relation_id, % input 1 (di)
relation_id, % input 2 (in)
output_rel, % difference (out)
index_spec,
maybe(output_rel)
% Used by rl_liveness.m to make sure that
% the di input has a single reference. The
% relation_id is used to hold a copy of the
% di relation if required. The indexes should
% be added to the copy if it is made.
)
;
% Insert a relation into another relation.
% The input relation is destructively updated.
insert(
relation_id, % output (uo) (same indexes as di input)
relation_id, % relation to be inserted into (di)
relation_id, % relation to insert (in)
insert_type,
maybe(output_rel)
% Used by rl_liveness.m to make sure that
% the di input has a single reference. The
% relation_id is used to hold a copy of the
% di relation if required. The indexes should
% be added to the copy if it is made.
)
;
sort(
output_rel, % output
relation_id, % input
sort_attrs % attributes to sort on
)
;
% Make the output variable refer to the same relation
% as the input without copying.
ref(
relation_id, % output
relation_id % input
)
;
% Make a copy of the input relation, making sure the
% output has the given set of indexes.
% This could be a bit slow, because the system can't just
% copy the files, but has to do a full
copy(
output_rel, % output
relation_id % input
)
;
% If there are multiple references to the input, copy the
% input to the output, otherwise make the output a reference
% to the input. To introduce this, the compiler must know that
% there are no later references in the code to the input
% relation.
% I'm not 100% sure that the reference counts maintained
% by Aditi can be used in this way - currently we always
% do the copy.
% Make sure the output has the given set of indexes, even
% if it isn't copied.
make_unique(
output_rel, % output
relation_id % input
)
;
% Create an empty relation.
init(output_rel)
;
% add a tuple to a relation.
insert_tuple(
output_rel, % output
relation_id, % input
rl_goal
)
;
% call an RL procedure
call(
rl_proc_name, % called procedure
list(relation_id), % input argument relations
list(output_rel), % output argument relations
set(relation_id) % subset of the inputs which
% must be saved across the call,
% filled in by rl_liveness.m.
)
;
aggregate(
output_rel, % output relation
relation_id, % input relation
pred_proc_id, % predicate to produce the
% initial accumulator for
% each group
pred_proc_id % predicate to update the
% accumulator for each tuple.
)
;
% Make sure the relation has the given index.
% We don't include a remove_index operation because it
% would be very expensive and probably not very useful.
add_index(output_rel)
;
% Empty a relation. This will be expensive for permanent
% relations due to logging.
clear(relation_id)
;
% Drop a pointer to a temporary relation. The relation
% is left unchanged, but may be garbage collected if
% there are no references to it.
unset(relation_id)
;
label(label_id)
;
conditional_goto(goto_cond, label_id)
;
goto(label_id)
;
comment
.
% An output relation first clears the initial contents of the
% relation, then initialises the relation with the given set
% of indexes.
:- type output_rel
---> output_rel(
relation_id,
list(index_spec)
).
:- type goto_cond
---> empty(relation_id)
; and(goto_cond, goto_cond)
; or(goto_cond, goto_cond)
; not(goto_cond).
:- type join_type
---> nested_loop
; sort_merge(sort_spec, sort_spec)
; index(index_spec, key_range)
% The second relation is indexed.
% Each tuple in the first relation
% is used to create a key range
% for accessing the second. The goal
% builds the lower and upper bounds
% on the key range from the input
% tuple from the first relation.
; cross
; semi % The output tuple is copied from the
% first input tuple. An output projection
% must be done as a separate operation.
.
:- type subtract_type
---> nested_loop
; sort_merge(sort_spec, sort_spec)
; index(index_spec, key_range)
.
:- type difference_type
---> sort_merge(sort_spec)
.
:- type project_type
---> filter
; index(index_spec, key_range)
.
:- type union_type
---> sort_merge(sort_spec)
.
:- type insert_type
---> append
; index(index_spec).
%-----------------------------------------------------------------------------%
:- type sort_spec
---> sort_var(int) % Some operations, such as union,
% expect their inputs to be sorted
% on all attributes, but don't care
% in which order or direction.
; attributes(sort_attrs)
% Sort on the given attributes.
.
:- type sort_attrs == assoc_list(int, sort_dir).
:- type sort_dir
---> ascending
; descending
.
%-----------------------------------------------------------------------------%
% We delay converting join conditions to the lower level representation
% for as long as possible because they are easier to deal with in
% hlds_goal form.
:- type rl_goal
---> rl_goal(
maybe(pred_proc_id),
% Predicate from which the expression was
% taken - used to avoid unnecessarily merging
% varsets. Should be `no' if the varset
% contains vars from multiple procs.
prog_varset,
map(prog_var, type),
instmap, % instmap before goal
rl_goal_inputs,
rl_goal_outputs,
list(hlds_goal),
list(rl_var_bounds)
).
:- type rl_goal_inputs
---> no_inputs
; one_input(list(prog_var))
; two_inputs(list(prog_var), list(prog_var))
.
:- type rl_goal_outputs == maybe(list(prog_var)).
% A key_term is an intermediate form of a key_attr which keeps
% aliasing information. This can be converted into a key_range
% later. The set of variables attached to each node is the
% set of all variables in the goal which were found by rl_key.m
% to have that value.
:- type key_term == pair(key_term_node, set(prog_var)).
:- type key_term_node
---> functor(cons_id, (type), list(key_term))
; var
.
:- type rl_var_bounds == map(prog_var, pair(key_term)).
%-----------------------------------------------------------------------------%
:- type label_id == int.
%-----------------------------------------------------------------------------%
:- pred rl__default_temporary_state(module_info::in,
relation_state::out) is det.
% rl__instr_relations(Instr, InputRels, OutputRels).
:- pred rl__instr_relations(rl_instruction::in,
list(relation_id)::out, list(relation_id)::out) is det.
% Return all relations referenced by a goto condition.
:- pred rl__goto_cond_relations(goto_cond::in,
list(relation_id)::out) is det.
% Is the instructions a label, goto or conditional goto.
:- pred rl__instr_ends_block(rl_instruction).
:- mode rl__instr_ends_block(in) is semidet.
% Strip off the index specification from an output relation.
:- pred rl__output_rel_relation(output_rel::in, relation_id::out) is det.
% Get a sort specification sorting ascending on all attributes.
:- pred rl__ascending_sort_spec(list(type)::in, sort_attrs::out) is det.
% Get a list of all attributes for a given schema.
:- pred rl__attr_list(list(T)::in, list(int)::out) is det.
% Succeed if the goal contain any of the variables corresponding
% to the attributes of the given input tuple.
:- pred rl__goal_is_independent_of_input(tuple_num::in,
rl_goal::in, rl_goal::out) is semidet.
% Swap the inputs of a goal such as a join condition which
% as two input relations.
:- pred rl__swap_goal_inputs(rl_goal::in, rl_goal::out) is det.
% Succeed if the goal produces an output tuple.
:- pred rl__goal_produces_tuple(rl_goal::in) is semidet.
:- type tuple_num
---> one
; two
.
%-----------------------------------------------------------------------------%
:- pred rl__proc_name_to_string(rl_proc_name::in, string::out) is det.
:- pred rl__label_id_to_string(label_id::in, string::out) is det.
:- pred rl__relation_id_to_string(relation_id::in, string::out) is det.
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module globals, options.
:- import_module bool, int, require, string.
rl__default_temporary_state(ModuleInfo, TmpState) :-
module_info_globals(ModuleInfo, Globals),
globals__lookup_bool_option(Globals, detect_rl_streams, Streams),
(
Streams = yes,
TmpState = stream
;
Streams = no,
% We have to assume that everything must be materialised.
TmpState = materialised
).
%-----------------------------------------------------------------------------%
rl__instr_relations(join(output_rel(Output, _), Input1, Input2, _, _) - _,
[Input1, Input2], [Output]).
rl__instr_relations(subtract(output_rel(Output, _),
Input1, Input2, _, _) - _, [Input1, Input2], [Output]).
rl__instr_relations(difference(output_rel(Output, _),
Input1, Input2, _) - _, [Input1, Input2], [Output]).
rl__instr_relations(project(OutputRel,
Input, _, OtherOutputRels, _) - _,
[Input], Outputs) :-
assoc_list__keys(OtherOutputRels, OutputRels),
list__map(rl__output_rel_relation,
[OutputRel | OutputRels], Outputs).
rl__instr_relations(union(OutputRel, Inputs, _) - _, Inputs, [Output]) :-
rl__output_rel_relation(OutputRel, Output).
rl__instr_relations(union_diff(UoOutput, DiInput, Input,
output_rel(Diff, _), _, _) - _,
[DiInput, Input], [UoOutput, Diff]).
rl__instr_relations(insert(DiOutput, DiInput, Input, _, _) - _,
[DiInput, Input], [DiOutput]).
rl__instr_relations(sort(output_rel(Output, _), Input, _) - _,
[Input], [Output]).
rl__instr_relations(init(output_rel(Rel, _)) - _, [], [Rel]).
rl__instr_relations(insert_tuple(output_rel(Output, _), Input, _) - _,
[Input], [Output]).
rl__instr_relations(add_index(output_rel(Rel, _)) - _, [], [Rel]).
rl__instr_relations(clear(Rel) - _, [], [Rel]).
rl__instr_relations(unset(Rel) - _, [], [Rel]).
rl__instr_relations(label(_) - _, [], []).
rl__instr_relations(goto(_) - _, [], []).
rl__instr_relations(comment - _, [], []).
rl__instr_relations(conditional_goto(Cond, _) - _, Inputs, []) :-
rl__goto_cond_relations(Cond, Inputs).
rl__instr_relations(ref(Output, Input) - _, [Input], [Output]).
rl__instr_relations(copy(output_rel(Output, _), Input) - _,
[Input], [Output]).
rl__instr_relations(make_unique(output_rel(Output, _), Input) - _,
[Input], [Output]).
rl__instr_relations(aggregate(output_rel(Output, _), Input, _, _) - _,
[Input], [Output]).
rl__instr_relations(call(_, Inputs, OutputRels, _) - _,
Inputs, Outputs) :-
list__map(rl__output_rel_relation, OutputRels, Outputs).
%-----------------------------------------------------------------------------%
rl__instr_ends_block(goto(_) - _).
rl__instr_ends_block(label(_) - _).
rl__instr_ends_block(conditional_goto(_, _) - _).
%-----------------------------------------------------------------------------%
rl__output_rel_relation(output_rel(Output, _), Output).
%-----------------------------------------------------------------------------%
rl__goto_cond_relations(empty(Rel), [Rel]).
rl__goto_cond_relations(and(Cond1, Cond2), Rels) :-
rl__goto_cond_relations(Cond1, Rels1),
rl__goto_cond_relations(Cond2, Rels2),
list__append(Rels1, Rels2, Rels).
rl__goto_cond_relations(or(Cond1, Cond2), Rels) :-
rl__goto_cond_relations(Cond1, Rels1),
rl__goto_cond_relations(Cond2, Rels2),
list__append(Rels1, Rels2, Rels).
rl__goto_cond_relations(not(Cond), Rels) :-
rl__goto_cond_relations(Cond, Rels).
%-----------------------------------------------------------------------------%
rl__ascending_sort_spec(Schema, Attrs) :-
GetAttr =
lambda([_::in, Attr::out, Index0::in, Index::out] is det, (
Attr = Index0 - ascending,
Index is Index0 + 1
)),
list__map_foldl(GetAttr, Schema, Attrs, 0, _).
rl__attr_list(Schema, Attrs) :-
rl__attr_list_2(0, Schema, Attrs).
:- pred rl__attr_list_2(int::in, list(T)::in,
list(int)::out) is det.
rl__attr_list_2(_, [], []).
rl__attr_list_2(Index, [_ | Types], [Index | Attrs]) :-
NextIndex is Index + 1,
rl__attr_list_2(NextIndex, Types, Attrs).
%-----------------------------------------------------------------------------%
rl__goal_is_independent_of_input(InputNo, RLGoal0, RLGoal) :-
RLGoal0 = rl_goal(A, B, C, D, Inputs0, MaybeOutputs, Goals, H),
rl__select_input_args(InputNo, Inputs0, Inputs, InputArgs),
set__list_to_set(InputArgs, InputArgSet),
\+ (
MaybeOutputs = yes(Outputs),
set__list_to_set(Outputs, OutputSet),
set__intersect(OutputSet, InputArgSet, OutputIntersection),
\+ set__empty(OutputIntersection)
),
\+ (
list__member(Goal, Goals),
Goal = _ - GoalInfo,
goal_info_get_nonlocals(GoalInfo, NonLocals),
set__intersect(NonLocals, InputArgSet, Intersection),
\+ set__empty(Intersection)
),
RLGoal = rl_goal(A, B, C, D, Inputs, MaybeOutputs, Goals, H).
:- pred rl__select_input_args(tuple_num::in, rl_goal_inputs::in,
rl_goal_inputs::out, list(prog_var)::out) is det.
rl__select_input_args(_, no_inputs, _, _) :-
error("rl__select_input_args").
rl__select_input_args(one, one_input(Args), no_inputs, Args).
rl__select_input_args(two, one_input(_), _, _) :-
error("rl__select_input_args").
rl__select_input_args(one, two_inputs(Args, Args2),
one_input(Args2), Args).
rl__select_input_args(two, two_inputs(Args1, Args),
one_input(Args1), Args).
rl__swap_goal_inputs(RLGoal0, RLGoal) :-
RLGoal0 = rl_goal(A, B, C, D, Inputs0, F, G, H),
( Inputs0 = two_inputs(Inputs1, Inputs2) ->
RLGoal = rl_goal(A, B, C, D, two_inputs(Inputs2, Inputs1),
F, G, H)
;
error("rl__swap_inputs: goal does not have two inputs to swap")
).
rl__goal_produces_tuple(RLGoal) :-
RLGoal = rl_goal(_, _, _, _, _, yes(_), _, _).
%-----------------------------------------------------------------------------%
rl__proc_name_to_string(rl_proc_name(User, Module, Pred, Arity), Str) :-
string__int_to_string(Arity, ArStr),
string__append_list([User, "/", Module, "/", Pred, "/", ArStr], Str).
rl__label_id_to_string(Label, Str) :-
string__int_to_string(Label, Str0),
string__append("label", Str0, Str).
rl__relation_id_to_string(RelationId, Str) :-
string__int_to_string(RelationId, Str0),
string__append("Rel", Str0, Str).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%