Files
mercury/compiler/rl_exprn.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

2036 lines
68 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_exprn.m
% Main author: stayl
%
% Generate RL "expressions" to evaluate join conditions.
%
% The code generated here is pretty awful. Each variable used in the
% expression is assigned its own register. All calls are generated inline -
% recursive calls and calls to imported predicates result in an abort.
% Unifications are generated eagerly.
%
% For complicated join conditions (for example anything containing
% calls to non-builtin predicates) we will probably generate Mercury bytecode,
% when the interpreter is done.
%
% Expressions are arranged into fragments. Each fragment consists of
% rl_PROC_expr_frag(N) followed by rl_EXP_* bytecodes to implement the
% fragment. Jumps addresses start at zero at the first instruction following
% the rl_PROC_expr_frag.
% 0 - initialisation: run once before anything else. Used to initialise
% the rule numbers (see below).
% 1 - group initialisation: has access to the first tuple in an aggregate group
% 2 - test - returns either zero/non-zero or -1/0/1 as for strcmp,
% depending on the operation.
% 3 - project - constructs an output tuple.
% 4 - cleanup - currently not used.
%
% Expressions have their own constant table separate from the procedure
% constant table. This is set up using rl_HEAD_const_* bytecodes before
% any fragments.
%
% Each expression has zero, one or two input tuples, a tuple to store
% local variables and zero, one or two output tuples.
%
% Expressions also need to set up rule numbers to identify data constructors.
% This is done with rl_EXP_define_var_rule(RuleNo, TypeIndex, NameIndex, Arity)
% (`var' refers to the schema of the tuple holding the local expression
% variables). TypeIndex and NameIndex are indices into the expression's
% constant table holding the type name and constructor name. `RuleNo' is
% used for bytecodes such as rl_EXP_test_functor and rl_EXP_construct_term
% to specify which constructor to use.
%
%-----------------------------------------------------------------------------%
:- module rl_exprn.
:- interface.
:- import_module hlds_module, hlds_pred, rl, rl_code, rl_file, prog_data.
:- import_module list.
% Generate an expression to compare tuples with the
% given schema on the given attributes.
:- pred rl_exprn__generate_compare_exprn(module_info::in, sort_spec::in,
list(type)::in, list(bytecode)::out) is det.
% Generate an expression to produce the upper and lower
% bounds for a B-tree access.
:- pred rl_exprn__generate_key_range(module_info::in, key_range::in,
list(bytecode)::out, int::out, list(type)::out, list(type)::out,
int::out) is det.
% Generate an expression for a join/project/subtract condition.
:- pred rl_exprn__generate(module_info::in, rl_goal::in, list(bytecode)::out,
int::out, exprn_mode::out, list(type)::out) is det.
% Given the closures used to create the initial accumulator for each
% group and update the accumulator for each tuple, create
% an expression to evaluate the aggregate.
:- pred rl_exprn__aggregate(module_info::in, pred_proc_id::in,
pred_proc_id::in, (type)::in, (type)::in, (type)::in,
list(bytecode)::out, list(type)::out) is det.
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module code_util, hlds_pred, hlds_data, inst_match.
:- import_module instmap, mode_util, tree, type_util, prog_out.
:- import_module rl_out, llds, inlining, hlds_goal.
:- import_module assoc_list, bool, char, int, map.
:- import_module require, set, std_util, string, term, varset.
% A compare expression tests each attribute in a list of attributes
% in turn.
rl_exprn__generate_compare_exprn(_ModuleInfo, Spec, Schema, Code) :-
(
Spec = attributes(Attrs),
list__foldl(rl_exprn__generate_compare_instrs(Schema),
Attrs, empty, CompareCode)
;
Spec = sort_var(_),
error("rl_exprn__generate_compare_exprn: unbound sort_var")
),
ExprnCode =
tree(node([rl_PROC_expr_frag(2)]),
tree(CompareCode,
node([
rl_EXP_int_immed(0), % return equal
rl_EXP_int_result,
rl_PROC_expr_end
])
)),
tree__flatten(ExprnCode, Instrs0),
list__condense(Instrs0, Code).
:- pred rl_exprn__generate_compare_instrs(list(type)::in,
pair(int, sort_dir)::in, byte_tree::in, byte_tree::out) is det.
rl_exprn__generate_compare_instrs(Types, Attr - Dir, Code0, Code) :-
list__index0_det(Types, Attr, Type),
rl_exprn__type_to_aditi_type(Type, AType),
rl_exprn__compare_bytecode(AType, CompareByteCode),
rl_exprn__get_input_field_code(one, AType, Attr, FieldCode1),
rl_exprn__get_input_field_code(two, AType, Attr, FieldCode2),
(
Dir = ascending,
CompareAttr = node([
FieldCode1,
FieldCode2,
CompareByteCode,
rl_EXP_return_if_nez
])
;
Dir = descending,
CompareAttr = node([
FieldCode2,
FieldCode1,
CompareByteCode,
rl_EXP_return_if_nez
])
),
Code = tree(Code0, CompareAttr).
%-----------------------------------------------------------------------------%
rl_exprn__generate_key_range(ModuleInfo,
key_range(LowerBound, UpperBound, MaybeArgTypes, KeyTypes),
Code, NumParams, Output1Schema, Output2Schema, MaxDepth) :-
( MaybeArgTypes = yes(_) ->
NumParams = 1
;
NumParams = 0
),
rl_exprn_info_init(ModuleInfo, Info0),
% Generate code to produce the lower bound term.
rl_exprn__generate_bound(ModuleInfo, MaybeArgTypes, KeyTypes,
one, LowerBound, LowerBoundCode, Output1Schema,
MaxDepth0, Info0, Info1),
% Generate code to produce the upper bound term.
rl_exprn__generate_bound(ModuleInfo, MaybeArgTypes, KeyTypes,
two, UpperBound, UpperBoundCode, Output2Schema,
MaxDepth1, Info1, Info2),
int__max(MaxDepth0, MaxDepth1, MaxDepth),
rl_exprn__generate_init_fragment(InitCode, Info2, Info),
rl_exprn_info_get_consts(Consts - _, Info, _),
map__to_assoc_list(Consts, ConstsAL),
assoc_list__reverse_members(ConstsAL, ConstsLA0),
list__sort(ConstsLA0, ConstsLA),
list__map(rl_exprn__generate_const_decl, ConstsLA, ConstCode),
CodeTree =
tree(node(ConstCode),
tree(InitCode,
tree(node([rl_PROC_expr_frag(3)]),
tree(LowerBoundCode,
UpperBoundCode
)))),
tree__flatten(CodeTree, Code0),
list__condense(Code0, Code).
:- pred rl_exprn__generate_bound(module_info::in, maybe(list(type))::in,
list(type)::in, tuple_num::in, bounding_tuple::in, byte_tree::out,
list(type)::out, int::out, rl_exprn_info::in,
rl_exprn_info::out) is det.
% An output schema of [] signals to the relational operation that
% that end of the key range has no bound (it doesn't make sense
% to have a key with no attributes).
rl_exprn__generate_bound(_, _, _, _, infinity, empty, [], 0) --> [].
rl_exprn__generate_bound(ModuleInfo, MaybeArgTypes, KeyTypes,
TupleNum, bound(Attrs), Code, KeyTypes, MaxDepth) -->
{ assoc_list__values(Attrs, AttrValues) },
rl_exprn__generate_bound_2(ModuleInfo, MaybeArgTypes,
TupleNum, no, AttrValues, empty, Code, 0, 1, MaxDepth).
:- pred rl_exprn__generate_bound_2(module_info::in, maybe(list(type))::in,
tuple_num::in, bool::in, list(key_attr)::in, byte_tree::in,
byte_tree::out, int::in, int::in, int::out, rl_exprn_info::in,
rl_exprn_info::out) is det.
rl_exprn__generate_bound_2(_, _, _, _, [], Code, Code,
_, MaxDepth, MaxDepth) --> [].
rl_exprn__generate_bound_2(ModuleInfo, MaybeArgTypes, TupleNum, IsSubTerm,
[Attr | Attrs], Code0, Code, Index0, MaxDepth0, MaxDepth) -->
rl_exprn__generate_bound_3(ModuleInfo, MaybeArgTypes, IsSubTerm,
Index0, TupleNum, Attr, AttrCode, Depth),
{ int__max(MaxDepth0, Depth, MaxDepth1) },
{ Index is Index0 + 1 },
rl_exprn__generate_bound_2(ModuleInfo, MaybeArgTypes, TupleNum,
IsSubTerm, Attrs, tree(Code0, AttrCode),
Code, Index, MaxDepth1, MaxDepth).
:- pred rl_exprn__generate_bound_3(module_info::in, maybe(list(type))::in,
bool::in, int::in, tuple_num::in, key_attr::in, byte_tree::out,
int::out, rl_exprn_info::in, rl_exprn_info::out) is det.
rl_exprn__generate_bound_3(_, _, _, _, _, infinity, _, _, _, _) :-
% Eventually the B-tree lookup code will be able to handle this case.
% For now we have to be careful not to generate it in rl_key.m.
error("rl_exprn__generate_bound_3: embedded infinities NYI").
rl_exprn__generate_bound_3(_ModuleInfo, MaybeArgTypes, IsSubTerm, FieldNum,
TupleNum, input_field(InputFieldNum), Code, 1, Info, Info) :-
rl_exprn__get_key_arg(MaybeArgTypes, InputFieldNum, FieldType0),
rl_exprn__type_to_aditi_type(FieldType0, FieldType),
rl_exprn__get_input_field_code(one, FieldType, InputFieldNum, GetCode),
(
IsSubTerm = yes,
rl_exprn__set_term_arg_code(FieldType, FieldNum, PutCode)
;
IsSubTerm = no,
rl_exprn__set_output_field_code(TupleNum, FieldType,
FieldNum, PutCode)
),
Code = node([GetCode, PutCode]).
rl_exprn__generate_bound_3(ModuleInfo, MaybeArgTypes, IsSubTerm, FieldNum,
TupleNum, functor(ConsId, Type, Attrs), Code, Depth) -->
rl_exprn__set_term_arg_cons_id_code(ConsId, Type, TupleNum,
FieldNum, IsSubTerm, CreateTerm, NeedPop),
rl_exprn__generate_bound_2(ModuleInfo, MaybeArgTypes, TupleNum, yes,
Attrs, node(CreateTerm), Code0, 0, 1, Depth0),
{ NeedPop = yes ->
Code = tree(Code0, node([rl_EXP_term_pop]))
;
Code = Code0
},
{ Depth is Depth0 + 1 }.
:- pred rl_exprn__get_key_arg(maybe(list(T))::in, int::in, T::out) is det.
rl_exprn__get_key_arg(yes(Args), Index, Arg) :-
list__index0_det(Args, Index, Arg).
rl_exprn__get_key_arg(no, _, _) :-
error("rl_exprn__get_key_arg").
:- pred rl_exprn__set_term_arg_cons_id_code(cons_id::in, (type)::in,
tuple_num::in, int::in, bool::in, list(bytecode)::out, bool::out,
rl_exprn_info::in, rl_exprn_info::out) is det.
rl_exprn__set_term_arg_cons_id_code(cons(SymName, Arity), Type, TupleNum,
FieldNum, IsSubTerm, Code, NeedPop) -->
( { rl_exprn__is_char_cons_id(cons(SymName, Arity), Type, Int) } ->
rl_exprn__set_term_arg_cons_id_code(int_const(Int), Type,
TupleNum, FieldNum, IsSubTerm, Code, NeedPop)
;
{
TupleNum = one,
ExprnTuple = output1
;
TupleNum = two,
ExprnTuple = output2
},
rl_exprn__cons_id_to_rule_number(cons(SymName, Arity), Type,
ExprnTuple, Rule),
{
IsSubTerm = no,
(
TupleNum = one,
Code = [rl_EXP_new_term_output1(FieldNum,
Rule)]
;
TupleNum = two,
Code = [rl_EXP_new_term_output2(FieldNum,
Rule)]
),
NeedPop = no
;
IsSubTerm = yes,
Code = [
rl_EXP_term_dup,
rl_EXP_set_term_arg(FieldNum, Rule)
],
NeedPop = yes
}
).
rl_exprn__set_term_arg_cons_id_code(int_const(Int), _, TupleNum, FieldNum,
IsSubTerm, Code, no) -->
rl_exprn_info_lookup_const(int(Int), Index),
{ rl_exprn__set_term_arg_cons_id_code_2(int, TupleNum,
FieldNum, IsSubTerm, SetArgCode) },
{ Code0 = [rl_EXP_int_push(Index), SetArgCode] },
{ IsSubTerm = yes ->
Code = [rl_EXP_term_dup | Code0]
;
Code = Code0
}.
rl_exprn__set_term_arg_cons_id_code(float_const(Float), _, TupleNum, FieldNum,
IsSubTerm, Code, no) -->
rl_exprn_info_lookup_const(float(Float), Index),
{ rl_exprn__set_term_arg_cons_id_code_2(float, TupleNum,
FieldNum, IsSubTerm, SetArgCode) },
{ Code0 = [rl_EXP_flt_push(Index), SetArgCode] },
{ IsSubTerm = yes ->
Code = [rl_EXP_term_dup | Code0]
;
Code = Code0
}.
rl_exprn__set_term_arg_cons_id_code(string_const(Str), _, TupleNum, FieldNum,
IsSubTerm, Code, no) -->
rl_exprn_info_lookup_const(string(Str), Index),
{ rl_exprn__set_term_arg_cons_id_code_2(string, TupleNum,
FieldNum, IsSubTerm, SetArgCode) },
{ Code0 = [rl_EXP_str_push(Index), SetArgCode] },
{ IsSubTerm = yes ->
Code = [rl_EXP_term_dup | Code0]
;
Code = Code0
}.
rl_exprn__set_term_arg_cons_id_code(pred_const(_, _), _, _, _, _, _, _) -->
{ error("rl_exprn__set_term_arg_cons_id_code") }.
rl_exprn__set_term_arg_cons_id_code(code_addr_const(_, _),
_, _, _, _, _, _) -->
{ error("rl_exprn__set_term_arg_cons_id_code") }.
rl_exprn__set_term_arg_cons_id_code(base_type_info_const(_, _, _),
_, _, _, _, _, _) -->
{ error("rl_exprn__set_term_arg_cons_id_code") }.
rl_exprn__set_term_arg_cons_id_code(base_typeclass_info_const(_, _, _, _),
_, _, _, _, _, _) -->
{ error("rl_exprn__set_term_arg_cons_id_code") }.
rl_exprn__set_term_arg_cons_id_code(tabling_pointer_const(_, _),
_, _, _, _, _, _) -->
{ error("rl_exprn__set_term_arg_cons_id_code") }.
:- pred rl_exprn__set_term_arg_cons_id_code_2(aditi_type::in, tuple_num::in,
int::in, bool::in, bytecode::out) is det.
rl_exprn__set_term_arg_cons_id_code_2(int, one, FieldNum,
no, rl_EXP_output1_int(FieldNum)).
rl_exprn__set_term_arg_cons_id_code_2(int, two, FieldNum,
no, rl_EXP_output2_int(FieldNum)).
rl_exprn__set_term_arg_cons_id_code_2(int, _, FieldNum,
yes, rl_EXP_set_int_arg(FieldNum)).
rl_exprn__set_term_arg_cons_id_code_2(float, one, FieldNum,
no, rl_EXP_output1_flt(FieldNum)).
rl_exprn__set_term_arg_cons_id_code_2(float, two, FieldNum,
no, rl_EXP_output2_flt(FieldNum)).
rl_exprn__set_term_arg_cons_id_code_2(float, _, FieldNum,
yes, rl_EXP_set_flt_arg(FieldNum)).
rl_exprn__set_term_arg_cons_id_code_2(string, one, FieldNum,
no, rl_EXP_output1_str(FieldNum)).
rl_exprn__set_term_arg_cons_id_code_2(string, two, FieldNum,
no, rl_EXP_output2_str(FieldNum)).
rl_exprn__set_term_arg_cons_id_code_2(string, _, FieldNum,
yes, rl_EXP_set_str_arg(FieldNum)).
rl_exprn__set_term_arg_cons_id_code_2(term(_), _, _, _, _) :-
error("rl_exprn__set_term_arg_cons_id_code_2").
%-----------------------------------------------------------------------------%
rl_exprn__generate(ModuleInfo, RLGoal, Code, NumParams, Mode, Decls) :-
RLGoal = rl_goal(_, VarSet, VarTypes, InstMap,
Inputs, MaybeOutputs, Goals, _),
rl_exprn_info_init(ModuleInfo, InstMap, VarTypes, VarSet, Info0),
rl_exprn__generate_2(Inputs, MaybeOutputs, Goals,
Code, NumParams, Mode, Decls, Info0, _).
:- pred rl_exprn__generate_2(rl_goal_inputs::in, rl_goal_outputs::in,
list(hlds_goal)::in, list(bytecode)::out, int::out, exprn_mode::out,
list(type)::out, rl_exprn_info::in, rl_exprn_info::out) is det.
rl_exprn__generate_2(Inputs, MaybeOutputs, GoalList,
Code, NumParams, Mode, Decls) -->
{ goal_list_determinism(GoalList, Detism) },
{ determinism_components(Detism, CanFail, _) },
{ goal_list_nonlocals(GoalList, NonLocals0) },
{ MaybeOutputs = yes(OutputNonLocals) ->
set__insert_list(NonLocals0, OutputNonLocals, NonLocals)
;
NonLocals = NonLocals0
},
(
{ Inputs = no_inputs },
{ NumParams = 0 },
{ InputCode = empty }
;
{ Inputs = one_input(InputVars) },
{ NumParams = 1 },
rl_exprn__deconstruct_input_tuple(one, 0, InputVars,
NonLocals, InputCode)
;
{ Inputs = two_inputs(InputVars1, InputVars2) },
{ NumParams = 2 },
rl_exprn__deconstruct_input_tuple(one, 0,
InputVars1, NonLocals, InputCode1),
rl_exprn__deconstruct_input_tuple(two, 0,
InputVars2, NonLocals, InputCode2),
{ InputCode = tree(InputCode1, InputCode2) }
),
{ CanFail = can_fail ->
Fail = node([rl_EXP_return_false])
;
% Should cause an error if it is encountered.
Fail = node([rl_EXP_last_bytecode])
},
rl_exprn__goals(GoalList, Fail, GoalCode),
( { MaybeOutputs = yes(OutputVars) } ->
rl_exprn__construct_output_tuple(GoalList,
OutputVars, OutputCode),
{ Mode = generate }
;
{ OutputCode = empty },
{ Mode = test }
),
{
CanFail = can_fail,
EvalCode0 =
tree(InputCode,
GoalCode
),
rl_exprn__resolve_addresses(EvalCode0, EvalCode1),
EvalCode = tree(node([rl_PROC_expr_frag(2)]), EvalCode1),
( OutputCode = empty ->
ProjectCode = empty
;
ProjectCode =
tree(node([rl_PROC_expr_frag(3)]),
OutputCode)
)
;
CanFail = cannot_fail,
% For projections, the eval fragment is not run.
EvalCode = empty,
ProjectCode0 =
tree(InputCode,
tree(GoalCode,
OutputCode
)),
rl_exprn__resolve_addresses(ProjectCode0, ProjectCode1),
ProjectCode = tree(node([rl_PROC_expr_frag(3)]), ProjectCode1)
},
% Need to do the init code last, since it also needs to define
% the rule constants for the other fragments.
rl_exprn__generate_init_fragment(InitCode),
rl_exprn_info_get_consts(Consts - _),
{ map__to_assoc_list(Consts, ConstsAL) },
{ assoc_list__reverse_members(ConstsAL, ConstsLA0) },
{ list__sort(ConstsLA0, ConstsLA) },
{ list__map(rl_exprn__generate_const_decl, ConstsLA, ConstCode) },
rl_exprn_info_get_decls(Decls),
{ CodeTree =
tree(node(ConstCode),
tree(InitCode,
tree(EvalCode,
tree(ProjectCode,
node([rl_PROC_expr_end])
)))) },
{ tree__flatten(CodeTree, CodeLists) },
{ list__condense(CodeLists, Code) }.
:- pred rl_exprn__generate_init_fragment(byte_tree::out,
rl_exprn_info::in, rl_exprn_info::out) is det.
rl_exprn__generate_init_fragment(Code) -->
rl_exprn_info_get_rules(Rules - _),
{ map__to_assoc_list(Rules, RulesAL) },
{ assoc_list__reverse_members(RulesAL, RulesLA0) },
{ list__sort(RulesLA0, RulesLA) },
list__map_foldl(rl_exprn__generate_rule, RulesLA, RuleCodes),
( { RuleCodes = [] } ->
{ Code = empty }
;
{ Code =
tree(node([rl_PROC_expr_frag(0)]),
node(RuleCodes)
) }
).
:- pred rl_exprn__generate_const_decl(pair(int, rl_const)::in,
bytecode::out) is det.
rl_exprn__generate_const_decl(Addr - Const, Code) :-
(
Const = int(Int),
Code = rl_HEAD_const_int(Addr, Int)
;
Const = float(Float),
Code = rl_HEAD_const_flt(Addr, Float)
;
Const = string(Str),
Code = rl_HEAD_const_str(Addr, Str)
).
:- pred rl_exprn__generate_rule(pair(int, pair(rl_rule, exprn_tuple))::in, bytecode::out, rl_exprn_info::in, rl_exprn_info::out) is det.
rl_exprn__generate_rule(RuleNo - (Rule - RuleTuple), Code) -->
{ Rule = rl_rule(Type, Name, Arity) },
rl_exprn_info_lookup_const(string(Type), TypeIndex),
rl_exprn_info_lookup_const(string(Name), NameIndex),
{
RuleTuple = input1,
Code = rl_EXP_define_input1_rule(RuleNo,
TypeIndex, NameIndex, Arity)
;
RuleTuple = input2,
Code = rl_EXP_define_input2_rule(RuleNo,
TypeIndex, NameIndex, Arity)
;
RuleTuple = variables,
Code = rl_EXP_define_var_rule(RuleNo,
TypeIndex, NameIndex, Arity)
;
RuleTuple = output1,
Code = rl_EXP_define_output1_rule(RuleNo,
TypeIndex, NameIndex, Arity)
;
RuleTuple = output2,
Code = rl_EXP_define_output2_rule(RuleNo,
TypeIndex, NameIndex, Arity)
}.
%-----------------------------------------------------------------------------%
% Shift the inputs to the expression out of the input tuple.
:- pred rl_exprn__deconstruct_input_tuple(tuple_num::in, int::in,
list(prog_var)::in, set(prog_var)::in, byte_tree::out,
rl_exprn_info::in, rl_exprn_info::out) is det.
rl_exprn__deconstruct_input_tuple(_, _, [], _, empty) --> [].
rl_exprn__deconstruct_input_tuple(TupleNo, FieldNo, [Var | Vars],
NonLocals, Code) -->
( { set__member(Var, NonLocals) } ->
rl_exprn_info_lookup_var(Var, VarReg),
rl_exprn_info_lookup_var_type(Var, Type),
rl_exprn__assign(reg(VarReg),
input_field(TupleNo, FieldNo), Type, Code0)
;
{ Code0 = empty }
),
{ NextField is FieldNo + 1 },
rl_exprn__deconstruct_input_tuple(TupleNo, NextField, Vars,
NonLocals, Code1),
{ Code = tree(Code0, Code1) }.
% Move the outputs of the expression into the output tuple.
:- pred rl_exprn__construct_output_tuple(list(hlds_goal)::in,
list(prog_var)::in, byte_tree::out,
rl_exprn_info::in, rl_exprn_info::out) is det.
rl_exprn__construct_output_tuple(Goals, Vars, Code) -->
{ goal_list_determinism(Goals, Detism) },
( { determinism_components(Detism, _, at_most_zero) } ->
% The condition never succeeds, so don't try to
% construct the output.
{ Code = empty }
;
{ FirstField = 0 },
rl_exprn__construct_output_tuple_2(FirstField, Vars, Code)
).
:- pred rl_exprn__construct_output_tuple_2(int::in, list(prog_var)::in,
byte_tree::out, rl_exprn_info::in, rl_exprn_info::out) is det.
rl_exprn__construct_output_tuple_2(_, [], empty) --> [].
rl_exprn__construct_output_tuple_2(FieldNo, [Var | Vars], Code) -->
rl_exprn_info_lookup_var(Var, VarReg),
rl_exprn_info_lookup_var_type(Var, Type),
rl_exprn__assign(output_field(FieldNo), reg(VarReg), Type, Code0),
{ NextField is FieldNo + 1 },
rl_exprn__construct_output_tuple_2(NextField, Vars, Code1),
{ Code = tree(Code0, Code1) }.
%-----------------------------------------------------------------------------%
:- pred rl_exprn__goals(list(hlds_goal)::in, byte_tree::in,
byte_tree::out, rl_exprn_info::in, rl_exprn_info::out) is det.
rl_exprn__goals([], _, empty) --> [].
rl_exprn__goals([Goal | Goals], Fail, Code) -->
rl_exprn__goal(Goal, Fail, Code0),
rl_exprn__goals(Goals, Fail, Code1),
{ Code = tree(Code0, Code1) }.
:- pred rl_exprn__goal(hlds_goal::in, byte_tree::in,
byte_tree::out, rl_exprn_info::in, rl_exprn_info::out) is det.
rl_exprn__goal(unify(_, _, _, Uni, _) - Info, Fail, Code) -->
rl_exprn__unify(Uni, Info, Fail, Code).
rl_exprn__goal(call(PredId, ProcId, Args, _, _, _) - Info, Fail, Code) -->
rl_exprn__call(PredId, ProcId, Args, Info, Fail, Code).
rl_exprn__goal(not(NegGoal) - _, Fail, Code) -->
rl_exprn_info_get_next_label_id(EndLabel),
{ NotFail = node([rl_EXP_jmp(EndLabel)]) },
rl_exprn__goal(NegGoal, NotFail, NegCode),
{ Code =
tree(NegCode,
tree(Fail,
node([rl_PROC_label(EndLabel)])
)) }.
rl_exprn__goal(if_then_else(_, Cond, Then, Else, _) - _, Fail, Code) -->
rl_exprn_info_get_next_label_id(StartElse),
rl_exprn_info_get_next_label_id(EndIte),
{ CondFail = node([rl_EXP_jmp(StartElse)]) },
rl_exprn__goal(Cond, CondFail, CondCode),
rl_exprn__goal(Then, Fail, ThenCode),
rl_exprn__goal(Else, Fail, ElseCode),
{ Code =
tree(CondCode,
tree(ThenCode,
tree(node([rl_EXP_jmp(EndIte), rl_PROC_label(StartElse)]),
tree(ElseCode,
node([rl_PROC_label(EndIte)])
)))) }.
rl_exprn__goal(conj(Goals) - _, Fail, Code) -->
rl_exprn__goals(Goals, Fail, Code).
rl_exprn__goal(par_conj(_, _) - _, _, _) -->
{ error("rl_exprn__goal: par_conj not yet implemented") }.
rl_exprn__goal(disj(Goals, _) - _Info, Fail, Code) -->
% Nondet disjunctions should have been transformed into
% separate Aditi predicates by dnf.m.
rl_exprn_info_get_next_label_id(EndDisj),
{ GotoEnd = node([rl_EXP_jmp(EndDisj)]) },
rl_exprn__disj(Goals, GotoEnd, Fail, DisjCode),
{ Code = tree(DisjCode, node([rl_PROC_label(EndDisj)])) }.
rl_exprn__goal(switch(Var, _, Cases, _) - _, Fail, Code) -->
rl_exprn_info_get_next_label_id(EndSwitch),
{ GotoEnd = node([rl_EXP_jmp(EndSwitch)]) },
rl_exprn__cases(Var, Cases, GotoEnd, Fail, SwitchCode),
{ Code = tree(SwitchCode, node([rl_PROC_label(EndSwitch)])) }.
rl_exprn__goal(higher_order_call(_, _, _, _, _, _) - _, _, _) -->
{ error("rl_exprn__goal: higher-order call not yet implemented") }.
rl_exprn__goal(class_method_call(_, _, _, _, _, _) - _, _, _) -->
{ error("rl_exprn__goal: class method calls not yet implemented") }.
rl_exprn__goal(pragma_c_code(_, _, _, _, _, _, _) - _, _, _) -->
{ error("rl_exprn__goal: pragma_c_code not yet implemented") }.
rl_exprn__goal(some(_, Goal) - _, Fail, Code) -->
rl_exprn__goal(Goal, Fail, Code).
:- pred rl_exprn__cases(prog_var::in, list(case)::in, byte_tree::in,
byte_tree::in, byte_tree::out,
rl_exprn_info::in, rl_exprn_info::out) is det.
rl_exprn__cases(_, [], _, Fail, Fail) --> [].
rl_exprn__cases(Var, [case(ConsId, Goal) | Cases], Succeed, Fail, Code) -->
rl_exprn_info_get_next_label_id(NextCase),
{ Jmp = rl_EXP_jmp(NextCase) },
rl_exprn__functor_test(Var, ConsId, node([Jmp]), TestCode),
rl_exprn__goal(Goal, Fail, GoalCode),
rl_exprn__cases(Var, Cases, Succeed, Fail, Code1),
{ Code =
tree(TestCode,
tree(GoalCode,
tree(Succeed,
tree(node([rl_PROC_label(NextCase)]),
Code1
)))) }.
:- pred rl_exprn__disj(list(hlds_goal)::in, byte_tree::in,
byte_tree::in, byte_tree::out,
rl_exprn_info::in, rl_exprn_info::out) is det.
rl_exprn__disj([], _, Fail, Fail) --> [].
rl_exprn__disj([Goal | Goals], Succeed, Fail, Code) -->
rl_exprn_info_get_next_label_id(NextDisj),
{ TryNext = node([rl_EXP_jmp(NextDisj)]) },
{ NextLabel = node([rl_PROC_label(NextDisj)]) },
rl_exprn__goal(Goal, TryNext, GoalCode),
rl_exprn__disj(Goals, Succeed, Fail, Code1),
{ Code =
tree(GoalCode,
tree(Succeed,
tree(NextLabel,
Code1
))) }.
%-----------------------------------------------------------------------------%
:- pred rl_exprn__call(pred_id::in, proc_id::in, list(prog_var)::in,
hlds_goal_info::in, byte_tree::in, byte_tree::out,
rl_exprn_info::in, rl_exprn_info::out) is det.
rl_exprn__call(PredId, ProcId, Vars, _GoalInfo, Fail, Code) -->
rl_exprn_info_get_module_info(ModuleInfo),
{ module_info_pred_proc_info(ModuleInfo, PredId, ProcId,
PredInfo, ProcInfo) },
{ proc_info_inferred_determinism(ProcInfo, Detism) },
( { determinism_components(Detism, _, at_most_many) } ->
{ error("Sorry, not yet implemented - nondeterministic Mercury calls in Aditi procedures") }
;
{ pred_info_is_imported(PredInfo) },
{ \+ code_util__predinfo_is_builtin(PredInfo) }
->
{ error("Sorry, not yet implemented - calls to imported Mercury procedures from Aditi") }
;
rl_exprn__call_body(PredId, ProcId, PredInfo, ProcInfo,
Fail, Vars, Code)
).
:- pred rl_exprn__call_body(pred_id::in, proc_id::in, pred_info::in,
proc_info::in, byte_tree::in, list(prog_var)::in, byte_tree::out,
rl_exprn_info::in, rl_exprn_info::out) is det.
rl_exprn__call_body(PredId, ProcId, PredInfo, ProcInfo, Fail, Args, Code) -->
{ pred_info_name(PredInfo, PredName) },
{ pred_info_arity(PredInfo, Arity) },
( { code_util__predinfo_is_builtin(PredInfo) } ->
rl_exprn__generate_builtin_call(PredId, ProcId, PredInfo,
Args, Fail, Code)
;
% Handle unify/2 specially, since it is possibly recursive,
% which will cause the code below to fall over. Also, magic.m
% doesn't add type_info arguments yet.
{ PredName = "__Unify__" },
{ Arity = 2 },
{ list__reverse(Args, [Arg1, Arg2 | _]) },
{ hlds_pred__in_in_unification_proc_id(ProcId) }
->
rl_exprn_info_lookup_var(Arg1, Arg1Loc),
rl_exprn_info_lookup_var(Arg2, Arg2Loc),
rl_exprn_info_lookup_var_type(Arg1, Type),
rl_exprn__test(reg(Arg1Loc), reg(Arg2Loc), Type, Fail, Code)
;
% Handle compare/3 specially for the same reason
% as unify/2 above.
{ PredName = "__Compare__" },
{ Arity = 3 },
{ list__reverse(Args, [Arg2, Arg1, Res | _]) }
->
rl_exprn_info_lookup_var(Arg1, Arg1Loc),
rl_exprn_info_lookup_var(Arg2, Arg2Loc),
rl_exprn_info_lookup_var(Res, ResultReg),
rl_exprn_info_lookup_var_type(Arg1, Type),
rl_exprn_info_lookup_var_type(Res, ResType),
rl_exprn__generate_push(reg(Arg1Loc), Type, PushCode1),
rl_exprn__generate_push(reg(Arg2Loc), Type, PushCode2),
{ rl_exprn__type_to_aditi_type(Type, AditiType) },
{ rl_exprn__compare_bytecode(AditiType, Compare) },
{ EQConsId = cons(qualified(unqualified("mercury_builtin"),
"="), 0) },
{ LTConsId = cons(qualified(unqualified("mercury_builtin"),
"<"), 0) },
{ GTConsId = cons(qualified(unqualified("mercury_builtin"),
">"), 0) },
rl_exprn__cons_id_to_rule_number(EQConsId, ResType, EQRuleNo),
rl_exprn__cons_id_to_rule_number(GTConsId, ResType, GTRuleNo),
rl_exprn__cons_id_to_rule_number(LTConsId, ResType, LTRuleNo),
rl_exprn_info_get_next_label_id(GTLabel),
rl_exprn_info_get_next_label_id(LTLabel),
rl_exprn_info_get_next_label_id(EndLabel),
{ Code =
tree(PushCode1,
tree(PushCode2,
node([
Compare,
rl_EXP_b3way(LTLabel, GTLabel),
rl_EXP_new_term_var(ResultReg, EQRuleNo),
rl_EXP_term_pop,
rl_EXP_jmp(EndLabel),
rl_PROC_label(LTLabel),
rl_EXP_new_term_var(ResultReg, LTRuleNo),
rl_EXP_term_pop,
rl_EXP_jmp(EndLabel),
rl_PROC_label(GTLabel),
rl_EXP_new_term_var(ResultReg, GTRuleNo),
rl_EXP_term_pop,
rl_PROC_label(EndLabel)
])
)) }
;
% XXX temporary hack until we allow Mercury calls from Aditi -
% generate the goal of the called procedure, not a call to
% the called procedure, checking first that the call is not
% recursive.
rl_exprn_info_get_parent_pred_proc_ids(Parents0),
( { set__member(proc(PredId, ProcId), Parents0) } ->
{ error("sorry, recursive Mercury calls in Aditi-RL code are not yet implemented") }
;
[]
),
{ set__insert(Parents0, proc(PredId, ProcId), Parents) },
rl_exprn_info_set_parent_pred_proc_ids(Parents),
rl_exprn__inline_call(PredId, ProcId,
PredInfo, ProcInfo, Args, Goal),
rl_exprn__goal(Goal, Fail, Code),
rl_exprn_info_set_parent_pred_proc_ids(Parents0)
).
:- pred rl_exprn__inline_call(pred_id::in, proc_id::in, pred_info::in,
proc_info::in, list(prog_var)::in, hlds_goal::out,
rl_exprn_info::in, rl_exprn_info::out) is det.
rl_exprn__inline_call(_PredId, _ProcId, CalledPredInfo,
CalledProcInfo, Args, Goal) -->
rl_exprn_info_get_varset(VarSet0),
rl_exprn_info_get_vartypes(VarTypes0),
{ varset__init(TVarSet0) },
{ map__init(TVarMap0) },
{ inlining__do_inline_call([], Args, CalledPredInfo, CalledProcInfo,
VarSet0, VarSet, VarTypes0, VarTypes, TVarSet0, _, TVarMap0, _,
Goal) },
rl_exprn_info_set_varset(VarSet),
rl_exprn_info_set_vartypes(VarTypes).
%-----------------------------------------------------------------------------%
:- pred rl_exprn__unify(unification::in, hlds_goal_info::in,
byte_tree::in, byte_tree::out,
rl_exprn_info::in, rl_exprn_info::out) is det.
rl_exprn__unify(construct(Var, ConsId, Args, UniModes),
GoalInfo, _Fail, Code) -->
rl_exprn_info_lookup_var_type(Var, Type),
rl_exprn_info_lookup_var(Var, VarReg),
(
{ ConsId = cons(SymName, _) },
(
{ SymName = qualified(unqualified("mercury_builtin"),
TypeInfo) },
( { TypeInfo = "type_info" }
; { TypeInfo = "base_type_info" }
)
->
% XXX for now we ignore these and hope it doesn't
% matter. They may be introduced for calls to the
% automatically generated unification and comparison
% procedures.
{ Code = empty }
;
{ rl_exprn__is_char_cons_id(ConsId, Type, Int) }
->
rl_exprn__assign(reg(VarReg), const(int(Int)),
Type, Code)
;
rl_exprn__cons_id_to_rule_number(ConsId, Type, RuleNo),
{ Create = rl_EXP_new_term_var(VarReg, RuleNo) },
{ goal_info_get_nonlocals(GoalInfo, NonLocals) },
rl_exprn__handle_functor_args(Args, UniModes,
NonLocals, 0, ConsId, ArgCodes),
{ Code =
tree(node([Create]),
tree(ArgCodes,
node([rl_EXP_term_pop])
)) }
)
;
{ ConsId = int_const(Int) },
rl_exprn__assign(reg(VarReg), const(int(Int)), Type, Code)
;
{ ConsId = string_const(String) },
rl_exprn__assign(reg(VarReg), const(string(String)),
Type, Code)
;
{ ConsId = float_const(Float) },
rl_exprn__assign(reg(VarReg), const(float(Float)), Type, Code)
;
{ ConsId = pred_const(_, _) },
{ error("rl_exprn__unify: unsupported cons_id - pred_const") }
;
{ ConsId = code_addr_const(_, _) },
{ error("rl_exprn__unify: unsupported cons_id - code_addr_const") }
;
{ ConsId = base_type_info_const(_, _, _) },
% XXX for now we ignore these and hope it doesn't matter.
% They may be introduced for calls to the automatically
% generated unification and comparison procedures.
{ Code = empty }
;
{ ConsId = base_typeclass_info_const(_, _, _, _) },
{ error("rl_exprn__unify: unsupported cons_id - base_typeclass_info_const") }
;
{ ConsId = tabling_pointer_const(_, _) },
{ error("rl_exprn__unify: unsupported cons_id - tabling_pointer_const") }
).
rl_exprn__unify(deconstruct(Var, ConsId, Args, UniModes, CanFail),
GoalInfo, Fail, Code) -->
rl_exprn_info_lookup_var(Var, VarLoc),
rl_exprn_info_lookup_var_type(Var, Type),
( { CanFail = can_fail } ->
rl_exprn__functor_test(Var, ConsId, Fail, TestCode)
;
{ TestCode = empty }
),
( { Args \= [] } ->
{ goal_info_get_nonlocals(GoalInfo, NonLocals) },
rl_exprn__generate_push(reg(VarLoc), Type, PushCode),
rl_exprn__handle_functor_args(Args, UniModes,
NonLocals, 0, ConsId, ArgCodes0),
{ ArgCodes =
tree(PushCode,
tree(ArgCodes0,
node([rl_EXP_term_pop])
)) }
;
{ ArgCodes = empty }
),
{ Code = tree(TestCode, ArgCodes) }.
rl_exprn__unify(complicated_unify(_, _), _, _, _) -->
{ error("rl_gen__unify: complicated_unify") }.
rl_exprn__unify(assign(Var1, Var2), _GoalInfo, _Fail, Code) -->
rl_exprn_info_lookup_var(Var1, Var1Loc),
rl_exprn_info_lookup_var(Var2, Var2Loc),
rl_exprn_info_lookup_var_type(Var1, Type),
rl_exprn__assign(reg(Var1Loc), reg(Var2Loc), Type, Code).
rl_exprn__unify(simple_test(Var1, Var2), _GoalInfo, Fail, Code) -->
% Note that the type here isn't necessarily one of the builtins -
% magic.m uses simple_test for all in-in unifications it introduces.
rl_exprn_info_lookup_var(Var1, Var1Loc),
rl_exprn_info_lookup_var(Var2, Var2Loc),
rl_exprn_info_lookup_var_type(Var1, Type),
rl_exprn__test(reg(Var1Loc), reg(Var2Loc), Type, Fail, Code).
:- pred rl_exprn__assign(rl_lval::in, rl_rval::in, (type)::in,
byte_tree::out, rl_exprn_info::in, rl_exprn_info::out) is det.
rl_exprn__assign(Lval, Rval, Type, Code) -->
rl_exprn__generate_push(Rval, Type, PushCode),
rl_exprn__generate_pop(Lval, Type, PopCode),
{ Code = tree(PushCode, PopCode) }.
:- pred rl_exprn__test(rl_rval::in, rl_rval::in, (type)::in, byte_tree::in,
byte_tree::out, rl_exprn_info::in, rl_exprn_info::out) is det.
rl_exprn__test(Var1Loc, Var2Loc, Type, Fail, Code) -->
rl_exprn__generate_push(Var1Loc, Type, PushCode1),
rl_exprn__generate_push(Var2Loc, Type, PushCode2),
rl_exprn_info_get_next_label_id(Label),
{ rl_exprn__type_to_aditi_type(Type, AditiType) },
{
AditiType = int,
EqInstr = rl_EXP_int_eq
;
AditiType = float,
EqInstr = rl_EXP_flt_eq
;
AditiType = string,
EqInstr = rl_EXP_str_eq
;
AditiType = term(_),
EqInstr = rl_EXP_term_eq
},
{ Code =
tree(PushCode1,
tree(PushCode2,
tree(node([EqInstr]),
tree(node([rl_EXP_bnez(Label)]),
tree(Fail,
node([rl_PROC_label(Label)])
))))) }.
:- pred rl_exprn__functor_test(prog_var::in, cons_id::in, byte_tree::in,
byte_tree::out, rl_exprn_info::in, rl_exprn_info::out) is det.
rl_exprn__functor_test(Var, ConsId, Fail, Code) -->
rl_exprn_info_lookup_var(Var, VarReg),
rl_exprn_info_lookup_var_type(Var, Type),
( { ConsId = int_const(Int) } ->
rl_exprn__test(reg(VarReg), const(int(Int)), Type, Fail, Code)
; { ConsId = string_const(String) } ->
rl_exprn__test(reg(VarReg), const(string(String)),
Type, Fail, Code)
; { ConsId = float_const(Float) } ->
rl_exprn__test(reg(VarReg), const(float(Float)),
Type, Fail, Code)
; { rl_exprn__is_char_cons_id(ConsId, Type, Int) } ->
rl_exprn__test(reg(VarReg), const(int(Int)), Type, Fail, Code)
; { ConsId = cons(_, _) } ->
rl_exprn_info_get_next_label_id(Label),
rl_exprn__cons_id_to_rule_number(ConsId, Type, RuleNo),
rl_exprn__generate_push(reg(VarReg), Type, PushCode),
{ Code =
tree(PushCode,
tree(node([
rl_EXP_test_functor(RuleNo),
rl_EXP_bnez(Label)
]),
tree(Fail,
node([rl_PROC_label(Label)])
))) }
;
{ error("rl_exprn__functor_test: unsupported cons_id") }
).
:- pred rl_exprn__is_char_cons_id(cons_id::in,
(type)::in, int::out) is semidet.
rl_exprn__is_char_cons_id(ConsId, Type, Int) :-
ConsId = cons(unqualified(CharStr), 0),
type_to_type_id(Type, unqualified("character") - 0, _),
% Convert characters to integers.
( string__to_char_list(CharStr, [Char]) ->
char__to_int(Char, Int)
;
error("rl_exprn__unify: invalid char")
).
:- pred rl_exprn__handle_functor_args(list(prog_var)::in, list(uni_mode)::in,
set(prog_var)::in, int::in, cons_id::in, byte_tree::out,
rl_exprn_info::in, rl_exprn_info::out) is det.
rl_exprn__handle_functor_args([], [_|_], _, _, _, _) -->
{ error("rl_exprn__handle_functor_args") }.
rl_exprn__handle_functor_args([_|_], [], _, _, _, _) -->
{ error("rl_exprn__handle_functor_args") }.
rl_exprn__handle_functor_args([], [], _, _, _, empty) --> [].
rl_exprn__handle_functor_args([Arg | Args], [Mode | Modes], NonLocals,
Index, ConsId, Code) -->
{ NextIndex is Index + 1 },
rl_exprn__handle_functor_args(Args, Modes, NonLocals,
NextIndex, ConsId, Code0),
( { set__member(Arg, NonLocals) } ->
rl_exprn_info_lookup_var_type(Arg, Type),
rl_exprn_info_get_module_info(ModuleInfo),
{ Mode = ((LI - RI) -> (LF - RF)) },
{ mode_to_arg_mode(ModuleInfo, (LI -> LF), Type, LeftMode) },
{ mode_to_arg_mode(ModuleInfo, (RI -> RF), Type, RightMode) },
(
{ LeftMode = top_in },
{ RightMode = top_in }
->
% Can't have test in arg unification.
{ error("test in arg of [de]construction") }
;
{ LeftMode = top_in },
{ RightMode = top_out }
->
rl_exprn_info_lookup_var(Arg, ArgReg),
{ rl_exprn__type_to_aditi_type(Type, AditiType) },
{ rl_exprn__get_term_arg_code(AditiType,
Index, TermArgCode) },
rl_exprn__generate_pop(reg(ArgReg), Type, PopCode),
{ Code1 =
tree(node([rl_EXP_term_dup]),
tree(node([TermArgCode]),
PopCode
)) }
;
{ LeftMode = top_out },
{ RightMode = top_in }
->
rl_exprn_info_lookup_var(Arg, ArgLoc),
{ rl_exprn__type_to_aditi_type(Type, AditiType) },
{ rl_exprn__set_term_arg_code(AditiType,
Index, TermArgCode) },
rl_exprn__generate_push(reg(ArgLoc), Type, PushCode),
{ Code1 =
tree(node([rl_EXP_term_dup]),
tree(PushCode,
node([TermArgCode])
)) }
;
{ LeftMode = top_unused },
{ RightMode = top_unused }
->
{ Code1 = empty }
;
{ error("rl_exprn__handle_functor_args: weird unification") }
),
{ Code = tree(Code1, Code0) }
;
{ Code = Code0 }
).
%-----------------------------------------------------------------------------%
:- pred rl_exprn__cons_id_to_rule_number(cons_id::in, (type)::in, int::out,
rl_exprn_info::in, rl_exprn_info::out) is det.
rl_exprn__cons_id_to_rule_number(ConsId, Type, RuleNo) -->
rl_exprn__cons_id_to_rule_number(ConsId, Type, variables, RuleNo).
:- pred rl_exprn__cons_id_to_rule_number(cons_id::in, (type)::in,
exprn_tuple::in, int::out,
rl_exprn_info::in, rl_exprn_info::out) is det.
rl_exprn__cons_id_to_rule_number(ConsId, Type, ExprnTuple, RuleNo) -->
(
{ ConsId = cons(ConsName, Arity) },
{ type_to_type_id(Type, TypeId, Args) }
->
% These names should not be quoted, since they are not
% being parsed, just compared against other strings.
{ rl_out__mangle_type_name(TypeId, Args, MangledTypeName) },
{ rl_out__mangle_ctor_name(ConsName, Arity, MangledConsName) },
{ Rule = rl_rule(MangledTypeName, MangledConsName, Arity) },
rl_exprn_info_lookup_rule(Rule - ExprnTuple, RuleNo)
;
{ error("rl_exprn__cons_id_to_rule_number") }
).
%-----------------------------------------------------------------------------%
% Put a value on top of the expression stack.
:- pred rl_exprn__generate_push(rl_rval::in, (type)::in, byte_tree::out,
rl_exprn_info::in, rl_exprn_info::out) is det.
rl_exprn__generate_push(reg(Reg), Type0, Code) -->
{ rl_exprn__type_to_aditi_type(Type0, Type) },
rl_exprn__do_generate_push_var(Reg, Type, Code).
rl_exprn__generate_push(const(Const), _Type, node([ByteCode])) -->
rl_exprn_info_lookup_const(Const, ConstNo),
{
Const = int(_),
ByteCode = rl_EXP_int_push(ConstNo)
;
Const = float(_),
ByteCode = rl_EXP_flt_push(ConstNo)
;
Const = string(_),
ByteCode = rl_EXP_str_push(ConstNo)
}.
rl_exprn__generate_push(input_field(TupleNo, FieldNo),
Type0, node([ByteCode])) -->
{ rl_exprn__type_to_aditi_type(Type0, Type) },
{ rl_exprn__get_input_field_code(TupleNo, Type, FieldNo, ByteCode) }.
rl_exprn__generate_push(term_arg(TermLoc, _ConsId, Field, TermType),
Type0, ByteCodes) -->
{ rl_exprn__type_to_aditi_type(Type0, AditiType) },
rl_exprn__generate_push(TermLoc, TermType, PushCodes),
{
AditiType = int,
ByteCode = rl_EXP_get_int_arg(Field)
;
AditiType = float,
ByteCode = rl_EXP_get_flt_arg(Field)
;
AditiType = string,
ByteCode = rl_EXP_get_str_arg(Field)
;
AditiType = term(_),
ByteCode = rl_EXP_get_term_arg(Field)
},
{ ByteCodes =
tree(PushCodes,
node([ByteCode])
) }.
:- pred rl_exprn__do_generate_push_var(int::in, aditi_type::in,
byte_tree::out, rl_exprn_info::in, rl_exprn_info::out) is det.
rl_exprn__do_generate_push_var(Index, Type, node([ByteCode])) -->
{
Type = int,
ByteCode = rl_EXP_int_push_var(Index)
;
Type = float,
ByteCode = rl_EXP_flt_push_var(Index)
;
Type = string,
ByteCode = rl_EXP_str_push_var(Index)
;
Type = term(_),
ByteCode = rl_EXP_term_push_var(Index)
}.
%-----------------------------------------------------------------------------%
% Get the value on top of the expression stack and put it in the
% specified rl_lval.
:- pred rl_exprn__generate_pop(rl_lval::in, (type)::in, byte_tree::out,
rl_exprn_info::in, rl_exprn_info::out) is det.
rl_exprn__generate_pop(reg(Reg), Type0, ByteCode) -->
{ rl_exprn__type_to_aditi_type(Type0, Type) },
rl_exprn__do_generate_pop_var(Reg, Type, ByteCode).
rl_exprn__generate_pop(output_field(FieldNo), Type0, node([ByteCode])) -->
{ rl_exprn__type_to_aditi_type(Type0, Type) },
{
Type = int,
ByteCode = rl_EXP_output_int(FieldNo)
;
Type = float,
ByteCode = rl_EXP_output_flt(FieldNo)
;
Type = string,
ByteCode = rl_EXP_output_str(FieldNo)
;
Type = term(_),
% This bytecode copies the argument term adjusting rule numbers
% if the schemas of the argument term and the output tuple
% do not match.
ByteCode = rl_EXP_put_term_output(FieldNo)
}.
rl_exprn__generate_pop(term_arg(Reg, _ConsId, Field, TermType), Type0, Code) -->
% There's no swap operation (and to do a swap, the expression
% evaluator would probably need to know the types of the top
% two elements of the stack, so rl_EXP_swap_int_int,
% rl_EXP_swap_int_flt, etc).
{ rl_exprn__type_to_aditi_type(Type0, Type) },
rl_exprn_info_get_free_reg(Type0, TmpIndex),
rl_exprn__generate_pop(reg(TmpIndex), Type0, PopCode1),
rl_exprn__generate_push(reg(Reg), TermType, PushCode1),
rl_exprn__generate_push(reg(TmpIndex), Type0, PushCode2),
(
{ Type = int },
{ SetArg = rl_EXP_set_int_arg(Field) }
;
{ Type = float },
{ SetArg = rl_EXP_set_flt_arg(Field) }
;
{ Type = string },
{ SetArg = rl_EXP_set_str_arg(Field) }
;
{ Type = term(_) },
{ SetArg = rl_EXP_put_term_arg(Field) }
),
{ Code =
tree(PopCode1,
tree(PushCode1,
tree(PushCode2,
node([SetArg])
))) }.
:- pred rl_exprn__do_generate_pop_var(int::in, aditi_type::in, byte_tree::out,
rl_exprn_info::in, rl_exprn_info::out) is det.
rl_exprn__do_generate_pop_var(Index, Type, node([ByteCode])) -->
{
Type = int,
ByteCode = rl_EXP_int_pop_var(Index)
;
Type = float,
ByteCode = rl_EXP_flt_pop_var(Index)
;
Type = string,
ByteCode = rl_EXP_str_pop_var(Index)
;
Type = term(_),
ByteCode = rl_EXP_put_term_var(Index)
}.
%-----------------------------------------------------------------------------%
:- pred rl_exprn__generate_builtin_call(pred_id::in, proc_id::in,
pred_info::in, list(prog_var)::in, byte_tree::in,
byte_tree::out, rl_exprn_info::in, rl_exprn_info::out) is det.
rl_exprn__generate_builtin_call(_PredId, ProcId,
PredInfo, Args, Fail, Code) -->
{ pred_info_module(PredInfo, PredModule0) },
{ pred_info_name(PredInfo, PredName) },
%
% Generate LLDS for the builtin, then convert that to Aditi bytecode.
%
(
{ code_util__translate_builtin(PredModule0, PredName,
ProcId, Args, MaybeTest, MaybeAsg) }
->
( { MaybeTest = yes(TestRval) } ->
( rl_exprn__llds_rval_to_rl_rval(TestRval, RvalCode) ->
rl_exprn_info_get_next_label_id(SuccLabel),
{ Code =
tree(RvalCode,
tree(node([rl_EXP_bnez(SuccLabel)]),
tree(Fail,
node([rl_PROC_label(SuccLabel)])
))) }
;
{ error("rl_exprn__generate_exprn_instr: invalid test") }
)
; { MaybeAsg = yes(OutputVar - AsgRval) } ->
rl_exprn_info_lookup_var(OutputVar, OutputLoc),
rl_exprn_info_lookup_var_type(OutputVar, Type),
{ rl_exprn__type_to_aditi_type(Type, AditiType) },
rl_exprn__maybe_llds_rval_to_rl_rval(yes(AsgRval),
AditiType, RvalCode),
rl_exprn__generate_pop(reg(OutputLoc), Type, PopCode),
{ Code = tree(RvalCode, PopCode) }
;
{ error("rl_exprn__builtin_call: invalid builtin result") }
)
;
{ prog_out__sym_name_to_string(PredModule0, PredModule) },
{ pred_info_arity(PredInfo, Arity) },
{ string__format("Sorry, not implemented in Aditi: %s:%s/%i",
[s(PredModule), s(PredName), i(Arity)], Msg) },
{ error(Msg) }
).
:- pred rl_exprn__maybe_llds_rval_to_rl_rval(maybe(rval)::in,
aditi_type::in, byte_tree::out,
rl_exprn_info::in, rl_exprn_info::out) is det.
rl_exprn__maybe_llds_rval_to_rl_rval(no, _, empty) --> [].
rl_exprn__maybe_llds_rval_to_rl_rval(yes(LLDSRval), _ResultType, Code) -->
( rl_exprn__llds_rval_to_rl_rval(LLDSRval, RvalCode) ->
{ Code = RvalCode }
;
{ error("rl_exprn__maybe_llds_rval_to_rl_rval: invalid llds rval") }
).
:- pred rl_exprn__llds_rval_to_rl_rval(rval::in, byte_tree::out,
rl_exprn_info::in, rl_exprn_info::out) is semidet.
rl_exprn__llds_rval_to_rl_rval(var(Var), Code) -->
rl_exprn_info_lookup_var(Var, VarLoc),
rl_exprn_info_lookup_var_type(Var, Type),
rl_exprn__generate_push(reg(VarLoc), Type, Code).
rl_exprn__llds_rval_to_rl_rval(const(RvalConst), PushCode) -->
{
RvalConst = true,
Const = int(1),
Type = int
;
RvalConst = false,
Const = int(0),
Type = int
;
RvalConst = int_const(Int),
Const = int(Int),
Type = int
;
RvalConst = float_const(Float),
Const = float(Float),
Type = float
;
RvalConst = string_const(String),
Const = string(String),
Type = string
},
{ rl_exprn__aditi_type_to_type(Type, Type1) },
rl_exprn__generate_push(const(Const), Type1, PushCode).
rl_exprn__llds_rval_to_rl_rval(binop(BinOp, Rval1, Rval2), Code) -->
rl_exprn__llds_rval_to_rl_rval(Rval1, Code1),
rl_exprn__llds_rval_to_rl_rval(Rval2, Code2),
{ rl_exprn__binop_bytecode(BinOp, Bytecode) },
{ Code =
tree(Code1,
tree(Code2,
node([Bytecode])
)) }.
:- pred rl_exprn__binop_bytecode(binary_op::in, bytecode::out) is semidet.
rl_exprn__binop_bytecode((+), rl_EXP_int_add).
rl_exprn__binop_bytecode((-), rl_EXP_int_sub).
rl_exprn__binop_bytecode((*), rl_EXP_int_mult).
rl_exprn__binop_bytecode((/), rl_EXP_int_div).
rl_exprn__binop_bytecode((mod), rl_EXP_int_mod).
rl_exprn__binop_bytecode(eq, rl_EXP_int_eq).
rl_exprn__binop_bytecode(ne, rl_EXP_int_ne).
rl_exprn__binop_bytecode(str_eq, rl_EXP_str_eq).
rl_exprn__binop_bytecode(str_ne, rl_EXP_str_ne).
rl_exprn__binop_bytecode(str_lt, rl_EXP_str_lt).
rl_exprn__binop_bytecode(str_gt, rl_EXP_str_gt).
rl_exprn__binop_bytecode(str_le, rl_EXP_str_le).
rl_exprn__binop_bytecode(str_ge, rl_EXP_str_ge).
rl_exprn__binop_bytecode((<), rl_EXP_int_lt).
rl_exprn__binop_bytecode((>), rl_EXP_int_gt).
rl_exprn__binop_bytecode((>=), rl_EXP_int_ge).
rl_exprn__binop_bytecode((<=), rl_EXP_int_le).
rl_exprn__binop_bytecode(float_plus, rl_EXP_flt_add).
rl_exprn__binop_bytecode(float_minus, rl_EXP_flt_sub).
rl_exprn__binop_bytecode(float_times, rl_EXP_flt_mult).
rl_exprn__binop_bytecode(float_divide, rl_EXP_flt_div).
rl_exprn__binop_bytecode(float_eq, rl_EXP_flt_eq).
rl_exprn__binop_bytecode(float_ne, rl_EXP_flt_ne).
rl_exprn__binop_bytecode(float_lt, rl_EXP_flt_lt).
rl_exprn__binop_bytecode(float_gt, rl_EXP_flt_gt).
rl_exprn__binop_bytecode(float_le, rl_EXP_flt_le).
rl_exprn__binop_bytecode(float_ge, rl_EXP_flt_ge).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
rl_exprn__aggregate(ModuleInfo, ComputeInitial, UpdateAcc, GrpByType,
NonGrpByType, AccType, AggCode, Decls) :-
map__init(VarTypes),
varset__init(VarSet),
instmap__init_reachable(InstMap),
rl_exprn_info_init(ModuleInfo, InstMap, VarTypes, VarSet, Info0),
rl_exprn__aggregate_2(ComputeInitial, UpdateAcc, GrpByType,
NonGrpByType, AccType, AggCode, Decls, Info0, _).
:- pred rl_exprn__aggregate_2(pred_proc_id::in, pred_proc_id::in,
(type)::in, (type)::in, (type)::in, list(bytecode)::out,
list(type)::out, rl_exprn_info::in, rl_exprn_info::out) is det.
rl_exprn__aggregate_2(ComputeInitial, UpdateAcc, GrpByType,
NonGrpByType, AccType, AggCode, Decls) -->
rl_exprn_info_get_free_reg(GrpByType, GrpByReg),
rl_exprn_info_get_free_reg(AccType, AccReg),
%
% Initialise the accumulator and group-by variables.
%
rl_exprn__aggregate_init(ComputeInitial, GrpByReg, GrpByType,
NonGrpByType, AccReg, AccType, InitCode),
%
% Generate a test to check whether the current tuple is
% in the current group.
%
rl_exprn__test(reg(GrpByReg), input_field(one, 0),
GrpByType, node([rl_EXP_return_false]), TestCode),
%
% Generate code to update the accumulator.
%
rl_exprn__aggregate_update(UpdateAcc, GrpByReg, GrpByType,
NonGrpByType, AccReg, AccType, UpdateCode),
{ EvalCode0 = tree(TestCode, UpdateCode) },
{ rl_exprn__resolve_addresses(EvalCode0, EvalCode1) },
{ EvalCode = tree(node([rl_PROC_expr_frag(2)]), EvalCode1) },
%
% Create the output tuple.
%
rl_exprn__assign(output_field(0), reg(GrpByReg),
GrpByType, GrpByOutputCode),
rl_exprn__assign(output_field(1), reg(AccReg),
AccType, AccOutputCode),
rl_exprn_info_get_decls(Decls),
{ AggCode0 =
tree(InitCode,
tree(EvalCode,
tree(node([rl_PROC_expr_frag(3)]),
tree(GrpByOutputCode,
AccOutputCode
)))) },
{ tree__flatten(AggCode0, AggCode1) },
{ list__condense(AggCode1, AggCode) }.
%-----------------------------------------------------------------------------%
% Generate code to initialise the accumulator for a group and
% put the group-by variable in a known place.
:- pred rl_exprn__aggregate_init(pred_proc_id::in, reg_id::in, (type)::in,
(type)::in, reg_id::in, (type)::in, byte_tree::out,
rl_exprn_info::in, rl_exprn_info::out) is det.
rl_exprn__aggregate_init(ComputeClosure, GrpByReg, GrpByType, NonGrpByType,
AccReg, AccType, InitCode) -->
% Put the group-by value for this group in its place.
rl_exprn__assign(reg(GrpByReg), input_field(one, 0),
GrpByType, GrpByAssign),
rl_exprn_info_get_free_reg(NonGrpByType, NonGrpByReg),
rl_exprn__assign(reg(NonGrpByReg), input_field(one, 1),
GrpByType, NonGrpByAssign),
rl_exprn_info_get_free_reg(AccType, InitialAccReg),
%
% Compute the initial accumulator given the first tuple in
% the group, and assign it to a register.
%
{ Args = [GrpByReg, NonGrpByReg, InitialAccReg] },
rl_exprn__closure(ComputeClosure, Args, IsConst, AccCode0),
% Restore the initial value of the accumulator at the start
% of a new group.
rl_exprn__assign(reg(AccReg), reg(InitialAccReg), AccType, AccAssign),
{ IsConst = yes ->
% If the initial accumulator is constant, it can be
% computed once in the init fragment, rather than
% once per group.
rl_exprn__resolve_addresses(AccCode0, AccCode),
InitCode =
tree(node([rl_PROC_expr_frag(0)]),
tree(AccCode,
tree(node([rl_PROC_expr_frag(1)]),
tree(GrpByAssign,
AccAssign
))))
;
InitCode0 =
tree(GrpByAssign,
tree(NonGrpByAssign,
tree(AccCode0,
AccAssign
))),
% If the initial accumulator is not constant, it must be
% computed in the group init fragment.
rl_exprn__resolve_addresses(InitCode0, InitCode1),
InitCode =
tree(node([rl_PROC_expr_frag(1)]),
InitCode1
)
}.
%-----------------------------------------------------------------------------%
% Generate code to compute the new accumulator given the
% next element in the group, then destructively update the
% old accumulator.
:- pred rl_exprn__aggregate_update(pred_proc_id::in, reg_id::in,
(type)::in, (type)::in, reg_id::in, (type)::in,
byte_tree::out, rl_exprn_info::in, rl_exprn_info::out) is det.
rl_exprn__aggregate_update(UpdateClosure, GrpByReg, GrpByType, NonGrpByType,
AccReg, AccType, Code) -->
rl_exprn_info_get_free_reg(NonGrpByType, NonGrpByReg),
rl_exprn__assign(reg(NonGrpByReg), input_field(one, 1),
NonGrpByType, NonGrpByCode),
% Allocate a location to collect the new accumulator.
rl_exprn_info_get_free_reg(GrpByType, OutputAccReg),
rl_exprn__assign(reg(AccReg), reg(OutputAccReg),
AccType, AccAssignCode),
{ Args = [GrpByReg, NonGrpByReg, AccReg, OutputAccReg] },
rl_exprn__closure(UpdateClosure, Args, _, UpdateCode),
{ Code =
tree(NonGrpByCode,
tree(UpdateCode,
AccAssignCode
)) }.
%-----------------------------------------------------------------------------%
% Evaluate a deterministic closure to compute the initial value
% or update the accumulator for an aggregate.
% Return whether the input arguments are actually used in
% constructing the outputs. If not, the closure is constant
% and can be evaluated once, instead of once per group.
:- pred rl_exprn__closure(pred_proc_id::in, list(reg_id)::in, bool::out,
byte_tree::out, rl_exprn_info::in, rl_exprn_info::out) is det.
rl_exprn__closure(proc(PredId, ProcId), ArgLocs, IsConst, Code) -->
rl_exprn_info_get_module_info(ModuleInfo),
{ module_info_pred_proc_info(ModuleInfo, PredId, ProcId,
PredInfo, ProcInfo) },
{ proc_info_vartypes(ProcInfo, VarTypes) },
{ proc_info_varset(ProcInfo, VarSet) },
rl_exprn_info_set_vartypes(VarTypes),
rl_exprn_info_set_varset(VarSet),
{ proc_info_headvars(ProcInfo, HeadVars) },
{ map__from_corresponding_lists(HeadVars, ArgLocs, VarLocs) },
{ list__length(HeadVars, NextVar) },
rl_exprn_info_set_vars(VarLocs - NextVar),
% Check if the closure depends on the input arguments.
{ proc_info_goal(ProcInfo, Goal) },
{ Goal = _ - GoalInfo },
{ goal_info_get_nonlocals(GoalInfo, NonLocals) },
{ proc_info_argmodes(ProcInfo, ArgModes) },
{ partition_args(ModuleInfo, ArgModes, HeadVars, InputArgs, _) },
{ set__list_to_set(InputArgs, InputArgSet) },
{ set__intersect(InputArgSet, NonLocals, UsedInputArgs) },
( { set__empty(UsedInputArgs) } ->
{ IsConst = yes }
;
{ IsConst = no }
),
{ Fail = node([rl_EXP_return_false]) },
rl_exprn__call_body(PredId, ProcId, PredInfo, ProcInfo,
Fail, HeadVars, Code).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
% Return the bytecode used to get a field from an input term.
:- pred rl_exprn__get_input_field_code(tuple_num::in, aditi_type::in,
int::in, bytecode::out) is det.
rl_exprn__get_input_field_code(one, int, Attr, rl_EXP_int_field1(Attr)).
rl_exprn__get_input_field_code(one, string, Attr, rl_EXP_str_field1(Attr)).
rl_exprn__get_input_field_code(one, float, Attr, rl_EXP_flt_field1(Attr)).
rl_exprn__get_input_field_code(one, term(_), Attr, rl_EXP_term_field1(Attr)).
rl_exprn__get_input_field_code(two, int, Attr, rl_EXP_int_field2(Attr)).
rl_exprn__get_input_field_code(two, string, Attr, rl_EXP_str_field2(Attr)).
rl_exprn__get_input_field_code(two, float, Attr, rl_EXP_flt_field2(Attr)).
rl_exprn__get_input_field_code(two, term(_), Attr, rl_EXP_term_field2(Attr)).
% Return the bytecode used to set a field in the output term.
:- pred rl_exprn__set_output_field_code(tuple_num::in,
aditi_type::in, int::in, bytecode::out) is det.
rl_exprn__set_output_field_code(one, int, Attr, rl_EXP_output1_int(Attr)).
rl_exprn__set_output_field_code(one, string, Attr, rl_EXP_output1_str(Attr)).
rl_exprn__set_output_field_code(one, float, Attr, rl_EXP_output1_flt(Attr)).
rl_exprn__set_output_field_code(one, term(_), Attr,
rl_EXP_put_term_output1(Attr)).
rl_exprn__set_output_field_code(two, int, Attr, rl_EXP_output2_int(Attr)).
rl_exprn__set_output_field_code(two, string, Attr, rl_EXP_output2_str(Attr)).
rl_exprn__set_output_field_code(two, float, Attr, rl_EXP_output2_flt(Attr)).
rl_exprn__set_output_field_code(two, term(_), Attr,
rl_EXP_put_term_output2(Attr)).
% Return the bytecode used to extract a field from a term.
:- pred rl_exprn__get_term_arg_code(aditi_type::in,
int::in, bytecode::out) is det.
rl_exprn__get_term_arg_code(int, Index, rl_EXP_get_int_arg(Index)).
rl_exprn__get_term_arg_code(float, Index, rl_EXP_get_flt_arg(Index)).
rl_exprn__get_term_arg_code(string, Index, rl_EXP_get_str_arg(Index)).
rl_exprn__get_term_arg_code(term(_), Index, rl_EXP_get_term_arg(Index)).
% Return the bytecode used to set a field in a term.
:- pred rl_exprn__set_term_arg_code(aditi_type::in,
int::in, bytecode::out) is det.
rl_exprn__set_term_arg_code(int, Index, rl_EXP_set_int_arg(Index)).
rl_exprn__set_term_arg_code(float, Index, rl_EXP_set_flt_arg(Index)).
rl_exprn__set_term_arg_code(string, Index, rl_EXP_set_str_arg(Index)).
% This bytecode copies the argument term adjusting rule numbers
% if the schemas of the argument term and the term having its
% argument set do not match.
rl_exprn__set_term_arg_code(term(_), Index, rl_EXP_put_term_arg(Index)).
:- pred rl_exprn__compare_bytecode(aditi_type::in, bytecode::out) is det.
rl_exprn__compare_bytecode(int, rl_EXP_int_cmp).
rl_exprn__compare_bytecode(float, rl_EXP_flt_cmp).
rl_exprn__compare_bytecode(string, rl_EXP_str_cmp).
rl_exprn__compare_bytecode(term(_), rl_EXP_term_cmp).
%-----------------------------------------------------------------------------%
:- type aditi_type
---> int
; string
; float
; term(type).
:- pred rl_exprn__type_to_aditi_type((type)::in, aditi_type::out) is det.
rl_exprn__type_to_aditi_type(Type, AditiType) :-
( type_to_type_id(Type, TypeId, _) ->
( TypeId = unqualified("int") - 0 ->
AditiType = int
; TypeId = unqualified("character") - 0 ->
AditiType = int
; TypeId = unqualified("string") - 0 ->
AditiType = string
; TypeId = unqualified("float") - 0 ->
AditiType = float
;
AditiType = term(Type)
)
;
% All types in Aditi relations must be bound. This case
% can happen if an argument of an aggregate init or update
% closure is not used. int is a bit of a lie, but since
% the argument is not used, it should be harmless.
AditiType = int
).
:- pred rl_exprn__aditi_type_to_type(aditi_type::in, (type)::out) is det.
rl_exprn__aditi_type_to_type(int, Int) :-
construct_type(unqualified("int") - 0, [], Int).
rl_exprn__aditi_type_to_type(float, Float) :-
construct_type(unqualified("float") - 0, [], Float).
rl_exprn__aditi_type_to_type(string, Str) :-
construct_type(unqualified("string") - 0, [], Str).
rl_exprn__aditi_type_to_type(term(Type), Type).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- pred rl_exprn__resolve_addresses(byte_tree::in, byte_tree::out) is det.
rl_exprn__resolve_addresses(ByteTree0, ByteTree) :-
map__init(Labels0),
rl_exprn__get_exprn_labels(0, _, Labels0, Labels,
ByteTree0, ByteTree1),
ResolveAddr =
lambda([Code0::in, Code::out] is det, (
% This is incomplete, but we don't generate any
% of the other jump instructions.
( Code0 = rl_EXP_jmp(Label0) ->
map__lookup(Labels, Label0, Label),
Code = rl_EXP_jmp(Label)
; Code0 = rl_EXP_beqz(Label0) ->
map__lookup(Labels, Label0, Label),
Code = rl_EXP_beqz(Label)
; Code0 = rl_EXP_bnez(Label0) ->
map__lookup(Labels, Label0, Label),
Code = rl_EXP_bnez(Label)
; Code0 = rl_EXP_bltz(Label0) ->
map__lookup(Labels, Label0, Label),
Code = rl_EXP_bltz(Label)
; Code0 = rl_EXP_blez(Label0) ->
map__lookup(Labels, Label0, Label),
Code = rl_EXP_blez(Label)
; Code0 = rl_EXP_bgez(Label0) ->
map__lookup(Labels, Label0, Label),
Code = rl_EXP_bgez(Label)
; Code0 = rl_EXP_bgtz(Label0) ->
map__lookup(Labels, Label0, Label),
Code = rl_EXP_bgtz(Label)
; Code0 = rl_EXP_bt(Label0) ->
map__lookup(Labels, Label0, Label),
Code = rl_EXP_bt(Label)
; Code0 = rl_EXP_bf(Label0) ->
map__lookup(Labels, Label0, Label),
Code = rl_EXP_bf(Label)
;
Code = Code0
)
)),
rl_out__resolve_addresses(ResolveAddr, ByteTree1, ByteTree).
:- pred rl_exprn__get_exprn_labels(int::in, int::out, map(label_id, int)::in,
map(label_id, int)::out, byte_tree::in, byte_tree::out) is det.
rl_exprn__get_exprn_labels(PC0, PC0, Labels, Labels, empty, empty).
rl_exprn__get_exprn_labels(PC0, PC, Labels0, Labels,
tree(CodeA0, CodeB0), tree(CodeA, CodeB)) :-
rl_exprn__get_exprn_labels(PC0, PC1, Labels0, Labels1, CodeA0, CodeA),
rl_exprn__get_exprn_labels(PC1, PC, Labels1, Labels, CodeB0, CodeB).
rl_exprn__get_exprn_labels(PC0, PC, Labels0, Labels,
node(Instrs0), node(Instrs)) :-
rl_exprn__get_exprn_labels_list(PC0, PC,
Labels0, Labels, Instrs0, Instrs).
:- pred rl_exprn__get_exprn_labels_list(int::in, int::out,
map(label_id, int)::in, map(label_id, int)::out,
list(bytecode)::in, list(bytecode)::out) is det.
rl_exprn__get_exprn_labels_list(PC, PC, Labels, Labels, [], []).
rl_exprn__get_exprn_labels_list(PC0, PC, Labels0, Labels,
[Instr | Instrs0], Instrs) :-
( Instr = rl_PROC_label(_) ->
PC1 = PC0
;
functor(Instr, _, Arity),
PC1 is PC0 + Arity + 1 % +1 for the opcode
),
rl_exprn__get_exprn_labels_list(PC1, PC, Labels0, Labels1,
Instrs0, Instrs1),
( Instr = rl_PROC_label(Label) ->
% Register the label and remove the instruction.
map__det_insert(Labels1, Label, PC0, Labels),
Instrs = Instrs1
;
Labels = Labels1,
Instrs = [Instr | Instrs1]
).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- type rl_lval
---> reg(reg_id)
% A field in the output tuple
; output_field(
int % field no
)
% A field of a term.
; term_arg(
reg_id,
cons_id,
int,
type % type of the term
).
:- type rl_rval
---> reg(reg_id)
; const(rl_const)
% A field in one of the input tuples
; input_field(
tuple_num,
int % field no
)
% An argument of a term in a register
; term_arg(
rl_rval, % register holding the term
cons_id,
int, % arg no
type % type of the term
).
:- type input_tuple
---> one
; two.
:- type reg_id == int.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- type rl_exprn_info.
:- pred rl_exprn_info_init(module_info, instmap, map(prog_var, type),
prog_varset, rl_exprn_info).
:- mode rl_exprn_info_init(in, in, in, in, out) is det.
:- pred rl_exprn_info_init(module_info, rl_exprn_info).
:- mode rl_exprn_info_init(in, out) is det.
:- pred rl_exprn_info_get_module_info(module_info,
rl_exprn_info, rl_exprn_info).
:- mode rl_exprn_info_get_module_info(out, in, out) is det.
:- pred rl_exprn_info_get_instmap(instmap, rl_exprn_info, rl_exprn_info).
:- mode rl_exprn_info_get_instmap(out, in, out) is det.
:- pred rl_exprn_info_set_instmap(instmap, rl_exprn_info, rl_exprn_info).
:- mode rl_exprn_info_set_instmap(in, in, out) is det.
:- pred rl_exprn_info_get_vartypes(map(prog_var, type),
rl_exprn_info, rl_exprn_info).
:- mode rl_exprn_info_get_vartypes(out, in, out) is det.
:- pred rl_exprn_info_set_vartypes(map(prog_var, type),
rl_exprn_info, rl_exprn_info).
:- mode rl_exprn_info_set_vartypes(in, in, out) is det.
:- pred rl_exprn_info_get_varset(prog_varset, rl_exprn_info, rl_exprn_info).
:- mode rl_exprn_info_get_varset(out, in, out) is det.
:- pred rl_exprn_info_set_varset(prog_varset, rl_exprn_info, rl_exprn_info).
:- mode rl_exprn_info_set_varset(in, in, out) is det.
:- pred rl_exprn_info_set_vars(id_map(prog_var), rl_exprn_info, rl_exprn_info).
:- mode rl_exprn_info_set_vars(in, in, out) is det.
:- pred rl_exprn_info_lookup_var(prog_var, int, rl_exprn_info, rl_exprn_info).
:- mode rl_exprn_info_lookup_var(in, out, in, out) is det.
:- pred rl_exprn_info_get_free_reg((type), reg_id,
rl_exprn_info, rl_exprn_info).
:- mode rl_exprn_info_get_free_reg(in, out, in, out) is det.
:- pred rl_exprn_info_get_next_label_id(label_id,
rl_exprn_info, rl_exprn_info).
:- mode rl_exprn_info_get_next_label_id(out, in, out) is det.
:- pred rl_exprn_info_lookup_const(rl_const, int,
rl_exprn_info, rl_exprn_info).
:- mode rl_exprn_info_lookup_const(in, out, in, out) is det.
:- pred rl_exprn_info_get_consts(id_map(rl_const),
rl_exprn_info, rl_exprn_info).
:- mode rl_exprn_info_get_consts(out, in, out) is det.
:- pred rl_exprn_info_lookup_rule(pair(rl_rule, exprn_tuple), int,
rl_exprn_info, rl_exprn_info).
:- mode rl_exprn_info_lookup_rule(in, out, in, out) is det.
:- pred rl_exprn_info_get_rules(id_map(pair(rl_rule, exprn_tuple)),
rl_exprn_info, rl_exprn_info).
:- mode rl_exprn_info_get_rules(out, in, out) is det.
:- pred rl_exprn_info_get_parent_pred_proc_ids(set(pred_proc_id),
rl_exprn_info, rl_exprn_info) is det.
:- mode rl_exprn_info_get_parent_pred_proc_ids(out, in, out) is det.
:- pred rl_exprn_info_set_parent_pred_proc_ids(set(pred_proc_id),
rl_exprn_info, rl_exprn_info) is det.
:- mode rl_exprn_info_set_parent_pred_proc_ids(in, in, out) is det.
:- pred rl_exprn_info_lookup_var_type(prog_var, type,
rl_exprn_info, rl_exprn_info).
:- mode rl_exprn_info_lookup_var_type(in, out, in, out) is det.
:- pred rl_exprn_info_get_decls(list(type), rl_exprn_info, rl_exprn_info).
:- mode rl_exprn_info_get_decls(out, in, out) is det.
:- type rl_exprn_info
---> rl_exprn_info(
module_info,
instmap, % not yet used.
map(prog_var, type),
prog_varset,
id_map(prog_var),
label_id, % next label.
id_map(rl_const),
id_map(pair(rl_rule, exprn_tuple)),
set(pred_proc_id), % parent pred_proc_ids, used
% to abort on recursion.
list(type) % variable declarations in reverse.
).
:- type rl_rule
---> rl_rule(
string, % mangled type name Module__Name
string, % mangled functor name Module__Name
int % arity
).
% Each expression has a number of tuples associated with it,
% each of which has its own rule table.
:- type exprn_tuple
---> input1
; input2
; variables
; output1
; output2
.
:- type id_map(T) == pair(map(T, int), int).
:- pred id_map_init(id_map(T)::out) is det.
id_map_init(Empty - 0) :-
map__init(Empty).
:- pred id_map_lookup(T::in, int::out, bool::out,
id_map(T)::in, id_map(T)::out) is det.
id_map_lookup(Id, IdIndex, Added, Map0 - Index0, Map - Index) :-
( map__search(Map0, Id, IdIndex0) ->
IdIndex = IdIndex0,
Map = Map0,
Index = Index0,
Added = no
;
IdIndex = Index0,
Index is Index0 + 1,
Added = yes,
map__det_insert(Map0, Id, Index0, Map)
).
:- pred id_map_lookup(T::in, int::out, id_map(T)::in, id_map(T)::out) is det.
id_map_lookup(Id, IdIndex, Map0, Map) :-
id_map_lookup(Id, IdIndex, _, Map0, Map).
rl_exprn_info_init(ModuleInfo, Info0) :-
map__init(VarTypes),
varset__init(VarSet),
instmap__init_reachable(InstMap),
rl_exprn_info_init(ModuleInfo, InstMap, VarTypes, VarSet, Info0).
rl_exprn_info_init(ModuleInfo, InstMap, VarTypes, VarSet, Info) :-
id_map_init(VarMap),
id_map_init(ConstMap),
id_map_init(RuleMap),
set__init(Parents),
Label = 0,
Info = rl_exprn_info(ModuleInfo, InstMap, VarTypes, VarSet,
VarMap, Label, ConstMap, RuleMap, Parents, []).
rl_exprn_info_get_module_info(A, Info, Info) :-
Info = rl_exprn_info(A,_,_,_,_,_,_,_,_,_).
rl_exprn_info_get_instmap(B, Info, Info) :-
Info = rl_exprn_info(_,B,_,_,_,_,_,_,_,_).
rl_exprn_info_get_vartypes(C, Info, Info) :-
Info = rl_exprn_info(_,_,C,_,_,_,_,_,_,_).
rl_exprn_info_get_varset(D, Info, Info) :-
Info = rl_exprn_info(_,_,_,D,_,_,_,_,_,_).
rl_exprn_info_get_consts(G, Info, Info) :-
Info = rl_exprn_info(_,_,_,_,_,_,G,_,_,_).
rl_exprn_info_get_rules(H, Info, Info) :-
Info = rl_exprn_info(_,_,_,_,_,_,_,H,_,_).
rl_exprn_info_get_parent_pred_proc_ids(I, Info, Info) :-
Info = rl_exprn_info(_,_,_,_,_,_,_,_,I,_).
rl_exprn_info_get_decls(J, Info, Info) :-
Info = rl_exprn_info(_,_,_,_,_,_,_,_,_,J0),
list__reverse(J0, J).
rl_exprn_info_set_instmap(B, Info0, Info) :-
Info0 = rl_exprn_info(A,_,C,D,E,F,G,H,I,J),
Info = rl_exprn_info(A,B,C,D,E,F,G,H,I,J).
rl_exprn_info_set_vartypes(C, Info0, Info) :-
Info0 = rl_exprn_info(A,B,_,D,E,F,G,H,I,J),
Info = rl_exprn_info(A,B,C,D,E,F,G,H,I,J).
rl_exprn_info_set_varset(D, Info0, Info) :-
Info0 = rl_exprn_info(A,B,C,_,E,F,G,H,I,J),
Info = rl_exprn_info(A,B,C,D,E,F,G,H,I,J).
rl_exprn_info_set_vars(E, Info0, Info) :-
Info0 = rl_exprn_info(A,B,C,D,_,F,G,H,I,J),
Info = rl_exprn_info(A,B,C,D,E,F,G,H,I,J).
rl_exprn_info_set_parent_pred_proc_ids(I, Info0, Info) :-
Info0 = rl_exprn_info(A,B,C,D,E,F,G,H,_,J),
Info = rl_exprn_info(A,B,C,D,E,F,G,H,I,J).
rl_exprn_info_get_free_reg(Type, Loc, Info0, Info) :-
Info0 = rl_exprn_info(A,B,C,D,VarMap0,F,G,H,I,RegTypes0),
VarMap0 = Map - Loc,
Loc1 is Loc + 1,
VarMap = Map - Loc1,
RegTypes = [Type | RegTypes0],
Info = rl_exprn_info(A,B,C,D,VarMap,F,G,H,I,RegTypes).
rl_exprn_info_lookup_var(Var, Loc, Info0, Info) :-
Info0 = rl_exprn_info(A,B,VarTypes,D,VarMap0,F,G,H,I,RegTypes0),
id_map_lookup(Var, Loc, Added, VarMap0, VarMap),
( Added = yes ->
map__lookup(VarTypes, Var, Type),
RegTypes = [Type | RegTypes0]
;
RegTypes = RegTypes0
),
Info = rl_exprn_info(A,B,VarTypes,D,VarMap,F,G,H,I,RegTypes).
rl_exprn_info_get_next_label_id(Label0, Info0, Info) :-
Info0 = rl_exprn_info(A,B,C,D,E,Label0,G,H,I,J),
Label is Label0 + 1,
Info = rl_exprn_info(A,B,C,D,E,Label,G,H,I,J).
rl_exprn_info_lookup_const(Const, Loc, Info0, Info) :-
Info0 = rl_exprn_info(A,B,C,D,E,F,Consts0,H,I,J),
id_map_lookup(Const, Loc, Consts0, Consts),
Info = rl_exprn_info(A,B,C,D,E,F,Consts,H,I,J).
rl_exprn_info_lookup_rule(Rule, Loc, Info0, Info) :-
Info0 = rl_exprn_info(A,B,C,D,E,F,G,Rules0,I,J),
id_map_lookup(Rule, Loc, Rules0, Rules),
Info = rl_exprn_info(A,B,C,D,E,F,G,Rules,I,J).
rl_exprn_info_lookup_var_type(Var, Type) -->
rl_exprn_info_get_vartypes(VarTypes),
{ map__lookup(VarTypes, Var, Type) }.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%