mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-22 21:03:53 +00:00
Estimated hours taken: 220
Aditi update syntax, type and mode checking.
Change the hlds_goal for constructions in preparation for
structure reuse to avoid making multiple conflicting changes.
compiler/hlds_goal.m:
Merge `higher_order_call' and `class_method_call' into a single
`generic_call' goal type. This also has alternatives for the
various Aditi builtins for which type declarations can't
be written.
Remove the argument types field from higher-order/class method calls.
It wasn't used often, and wasn't updated by optimizations
such as inlining. The types can be obtained from the vartypes
field of the proc_info.
Add a `lambda_eval_method' field to lambda_goals.
Add a field to constructions to identify which RL code fragment should
be used for an top-down Aditi closure.
Add fields to constructions to hold structure reuse information.
This is currently ignored -- the changes to implement structure
reuse will be committed to the alias branch.
This is included here to avoid lots of CVS conflicts caused by
changing the definition of `hlds_goal' twice.
Add a field to `some' goals to specify whether the quantification
can be removed. This is used to make it easier to ensure that
indexes are used for updates.
Add a field to lambda_goals to describe whether the modes were
guessed by the compiler and may need fixing up after typechecking
works out the argument types.
Add predicate `hlds_goal__generic_call_id' to work out a call_id
for a generic call for use in error messages.
compiler/purity.m:
compiler/post_typecheck.m:
Fill in the modes of Aditi builtin calls and closure constructions.
This needs to know which are the `aditi__state' arguments, so
it must be done after typechecking.
compiler/prog_data.m:
Added `:- type sym_name_and_arity ---> sym_name/arity'.
Add a type `lambda_eval_method', which describes how a closure
is to be executed. The alternatives are normal Mercury execution,
bottom-up execution by Aditi and top-down execution by Aditi.
compiler/prog_out.m:
Add predicate `prog_out__write_sym_name_and_arity', which
replaces duplicated inline code in a few places.
compiler/hlds_data.m:
Add a `lambda_eval_method' field to `pred_const' cons_ids and
`pred_closure_tag' cons_tags.
compiler/hlds_pred.m:
Remove type `pred_call_id', replace it with type `simple_call_id',
which combines a `pred_or_func' and a `sym_name_and_arity'.
Add a type `call_id' which describes all the different types of call,
including normal calls, higher-order and class-method calls
and Aditi builtins.
Add `aditi_top_down' to the type `marker'.
Remove `aditi_interface' from type `marker'. Interfacing to
Aditi predicates is now handled by `generic_call' hlds_goals.
Add a type `rl_exprn_id' which identifies a predicate to
be executed top-down by Aditi.
Add a `maybe(rl_exprn_id)' field to type `proc_info'.
Add predicate `adjust_func_arity' to convert between the arity
of a function to its arity as a predicate.
Add predicates `get_state_args' and `get_state_args_det' to
extract the DCG state arguments from an argument list.
Add predicate `pred_info_get_call_id' to get a `simple_call_id'
for a predicate for use in error messages.
compiler/hlds_out.m:
Write the new representation for call_ids.
Add a predicate `hlds_out__write_call_arg_id' which
replaces similar code in mode_errors.m and typecheck.m.
compiler/prog_io_goal.m:
Add support for `aditi_bottom_up' and `aditi_top_down' annotations
on pred expressions.
compiler/prog_io_util.m:
compiler/prog_io_pragma.m:
Add predicates
- `prog_io_util:parse_name_and_arity' to parse `SymName/Arity'
(moved from prog_io_pragma.m).
- `prog_io_util:parse_pred_or_func_name_and_arity to parse
`pred SymName/Arity' or `func SymName/Arity'.
- `prog_io_util:parse_pred_or_func_and_args' to parse terms resembling
a clause head (moved from prog_io_pragma.m).
compiler/type_util.m:
Add support for `aditi_bottom_up' and `aditi_top_down' annotations
on higher-order types.
Add predicates `construct_higher_order_type',
`construct_higher_order_pred_type' and
`construct_higher_order_func_type' to avoid some code duplication.
compiler/mode_util.m:
Add predicate `unused_mode/1', which returns `builtin:unused'.
Add functions `aditi_di_mode/0', `aditi_ui_mode/0' and
`aditi_uo_mode/0' which return `in', `in', and `out', but will
be changed to return `di', `ui' and `uo' when alias tracking
is implemented.
compiler/goal_util.m:
Add predicate `goal_util__generic_call_vars' which returns
any arguments to a generic_call which are not in the argument list,
for example the closure passed to a higher-order call or
the typeclass_info for a class method call.
compiler/llds.m:
compiler/exprn_aux.m:
compiler/dupelim.m:
compiler/llds_out.m:
compiler/opt_debug.m:
Add builtin labels for the Aditi update operations.
compiler/hlds_module.m:
Add predicate predicate_table_search_pf_sym, used for finding
possible matches for a call with the wrong number of arguments.
compiler/intermod.m:
Don't write predicates which build `aditi_top_down' goals,
because there is currently no way to tell importing modules
which RL code fragment to use.
compiler/simplify.m:
Obey the `cannot_remove' field of explicit quantification goals.
compiler/make_hlds.m:
Parse Aditi updates.
Don't typecheck clauses for which syntax errors in Aditi updates
are found - this avoids spurious "undefined predicate `aditi_insert/3'"
errors.
Factor out some common code to handle terms of the form `Head :- Body'.
Factor out common code in the handling of pred and func expressions.
compiler/typecheck.m:
Typecheck Aditi builtins.
Allow the argument types of matching predicates to be adjusted
when typechecking the higher-order arguments of Aditi builtins.
Change `typecheck__resolve_pred_overloading' to take a list of
argument types rather than a `map(var, type)' and a list of
arguments to allow a transformation to be performed on the
argument types before passing them.
compiler/error_util.m:
Move the part of `report_error_num_args' which writes
"wrong number of arguments (<x>; expected <y>)" from
typecheck.m for use by make_hlds.m when reporting errors
for Aditi builtins.
compiler/modes.m:
compiler/unique_modes.m:
compiler/modecheck_call.m:
Modecheck Aditi builtins.
compiler/lambda.m:
Handle the markers for predicates introduced for
`aditi_top_down' and `aditi_bottom_up' lambda expressions.
compiler/polymorphism.m:
Add extra type_infos to `aditi_insert' calls
describing the tuple to insert.
compiler/call_gen.m:
Generate code for Aditi builtins.
compiler/unify_gen.m:
compiler/bytecode_gen.m:
Abort on `aditi_top_down' and `aditi_bottom_up' lambda
expressions - code generation for them is not yet implemented.
compiler/magic.m:
Use the `aditi_call' generic_call rather than create
a new procedure for each Aditi predicate called from C.
compiler/rl_out.pp:
compiler/rl_gen.m:
compiler/rl.m:
Move some utility code used by magic.m and call_gen.m into rl.m.
Remove an XXX comment about reference counting being not yet
implemented - Evan has fixed that.
library/ops.m:
compiler/mercury_to_mercury.m:
doc/transition_guide.texi:
Add unary prefix operators `aditi_bottom_up' and `aditi_top_down',
used as qualifiers on lambda expressions.
Add infix operator `==>' to separate the tuples in an
`aditi_modify' call.
compiler/follow_vars.m:
Thread a `map(prog_var, type)' through, needed because
type information is no longer held in higher-order call goals.
compiler/table_gen.m:
Use the `make_*_construction' predicates in hlds_goal.m
to construct constants.
compiler/*.m:
Trivial changes to add extra fields to hlds_goal structures.
doc/reference_manual.texi:
Document Aditi updates.
Use @samp{pragma base_relation} instead of
@samp{:- pragma base_relation} throughout the Aditi documentation
to be consistent with other parts of the reference manual.
tests/valid/Mmakefile:
tests/valid/aditi_update.m:
tests/valid/aditi.m:
Test case.
tests/valid/Mmakefile:
Remove some hard-coded --intermodule-optimization rules which are
no longer needed because `mmake depend' is now run in this directory.
tests/invalid/*.err_exp:
Fix expected output for changes in reporting of call_ids
in typecheck.m.
tests/invalid/Mmakefile
tests/invalid/aditi_update_errors.{m,err_exp}:
tests/invalid/aditi_update_mode_errors.{m,err_exp}:
Test error messages for Aditi updates.
tests/valid/aditi.m:
tests/invalid/aditi.m:
Cut down version of extras/aditi/aditi.m to provide basic declarations
for Aditi compilation such as `aditi__state' and the modes
`aditi_di', `aditi_uo' and `aditi_ui'. Installing extras/aditi/aditi.m
somewhere would remove the need for these.
1982 lines
67 KiB
ObjectPascal
1982 lines
67 KiB
ObjectPascal
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 1998-1999 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_out.m
|
|
% Main author: stayl
|
|
%
|
|
% Generate RL bytecodes.
|
|
%
|
|
% See $ADITI_ROOT/src/rosi/rlo_spec.tex for a partial specification
|
|
% of the bytecodes. (copy in ~stayl/aditi/src/rosi/rlo_spec.tex)
|
|
%
|
|
% The conditional compilation in this module is done to avoid
|
|
% major efficiency problems when compiling the large disjunctions
|
|
% in rl_code.m using the alias branch mode checker.
|
|
%
|
|
%-----------------------------------------------------------------------------%
|
|
:- module rl_out.
|
|
|
|
:- interface.
|
|
|
|
:- import_module rl, rl_file, hlds_module.
|
|
#if INCLUDE_ADITI_OUTPUT % See ../Mmake.common.in.
|
|
:- import_module rl_code, tree.
|
|
#else
|
|
#endif
|
|
|
|
:- import_module list, io, std_util.
|
|
|
|
% Output schemas for locally defined base and derived relations to
|
|
% <module>.base_schema and <module>.derived_schema respectively.
|
|
:- pred rl_out__generate_schema_file(module_info::in,
|
|
io__state::di, io__state::uo) is det.
|
|
|
|
% Output bytecode to `<module>.rlo' if --aditi-only was set and a text
|
|
% representation to `<module>.rla' if --dump-rl-bytecode was specified.
|
|
% Output schema information for derived relations to
|
|
% `<module>.derived_schema' if --generate-schemas was set.
|
|
% If --aditi-only is not set, return the rl_file containing
|
|
% bytecodes to be output as constant data in the C file.
|
|
:- pred rl_out__generate_rl_bytecode(module_info::in, list(rl_proc)::in,
|
|
maybe(rl_file)::out, io__state::di, io__state::uo) is det.
|
|
|
|
#if INCLUDE_ADITI_OUTPUT % See ../Mmake.common.in.
|
|
% Given a predicate to update the labels in a bytecode, update
|
|
% all the labels in a tree of bytecodes.
|
|
:- pred rl_out__resolve_addresses(pred(bytecode, bytecode),
|
|
byte_tree, byte_tree).
|
|
:- mode rl_out__resolve_addresses(pred(in, out) is det, in, out) is det.
|
|
|
|
:- type byte_tree == tree(list(bytecode)).
|
|
#else
|
|
#endif
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
:- implementation.
|
|
|
|
:- import_module code_util, hlds_data, hlds_pred, prog_data, prog_out.
|
|
:- import_module llds, globals, options, rl_code, tree, type_util, passes_aux.
|
|
:- import_module rl_file, getopt, modules, prog_util, magic_util.
|
|
|
|
#if INCLUDE_ADITI_OUTPUT % See ../Mmake.common.in.
|
|
:- import_module rl_exprn.
|
|
#else
|
|
#endif
|
|
|
|
:- import_module assoc_list, bool, char, int, map, multi_map, require, set.
|
|
:- import_module string, term, tree, varset.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
rl_out__generate_schema_file(ModuleInfo) -->
|
|
{ module_info_name(ModuleInfo, ModuleName) },
|
|
module_name_to_file_name(ModuleName, ".base_schema", yes, FileName),
|
|
io__open_output(FileName, Res),
|
|
( { Res = ok(Stream) } ->
|
|
io__set_output_stream(Stream, OldStream),
|
|
{ module_info_predids(ModuleInfo, PredIds) },
|
|
list__foldl(rl_out__generate_schema_file_2(ModuleInfo),
|
|
PredIds),
|
|
io__set_output_stream(OldStream, _)
|
|
;
|
|
{ string__append_list(["Error: cannot open ", FileName,
|
|
" for output.\n"], Msg) },
|
|
{ error(Msg) }
|
|
).
|
|
|
|
:- pred rl_out__generate_schema_file_2(module_info::in, pred_id::in,
|
|
io__state::di, io__state::uo) is det.
|
|
|
|
rl_out__generate_schema_file_2(ModuleInfo, PredId) -->
|
|
{ module_info_pred_info(ModuleInfo, PredId, PredInfo) },
|
|
{ pred_info_get_markers(PredInfo, Markers) },
|
|
{ module_info_name(ModuleInfo, Module) },
|
|
{ pred_info_module(PredInfo, PredModule) },
|
|
(
|
|
{ Module = PredModule },
|
|
{ check_marker(Markers, base_relation) }
|
|
->
|
|
{ rl__get_permanent_relation_info(ModuleInfo, PredId,
|
|
Owner, ModuleName, PredName, PredArity0,
|
|
RelName, RelSchema) },
|
|
{ string__int_to_string(PredArity0, PredArity) },
|
|
io__write_strings([ModuleName, ":", PredName, "/", PredArity,
|
|
"\t", Owner, "/", ModuleName, "/", RelName,
|
|
"\t", RelSchema, "\n"])
|
|
;
|
|
[]
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% If the RL procedure is callable from the query shell or Mercury,
|
|
% i.e. it has one entry point, generate a description of the
|
|
% procedure to the `<module>.derived_schema' file.
|
|
:- pred rl_out__generate_derived_schema(module_info::in, rl_proc::in,
|
|
io__state::di, io__state::uo) is det.
|
|
|
|
rl_out__generate_derived_schema(ModuleInfo, Proc) -->
|
|
{ Proc = rl_proc(ProcName, Inputs, Outputs, _,
|
|
RelInfo, _, EntryPoints) },
|
|
(
|
|
{ EntryPoints = [proc(PredId, _)] },
|
|
{ Inputs = [InputRel] },
|
|
{ Outputs = [OutputRel] }
|
|
->
|
|
{ module_info_pred_info(ModuleInfo, PredId, PredInfo) },
|
|
{ pred_info_module(PredInfo, PredModule0) },
|
|
{ prog_out__sym_name_to_string(PredModule0, PredModule) },
|
|
{ pred_info_name(PredInfo, PredName) },
|
|
{ pred_info_arity(PredInfo, PredArity0) },
|
|
{ string__int_to_string(PredArity0, PredArity) },
|
|
{ rl_out__get_proc_schema(ModuleInfo, RelInfo,
|
|
[InputRel, OutputRel], SchemaString) },
|
|
{ rl__proc_name_to_string(ProcName, ProcNameStr) },
|
|
io__write_strings([PredModule, ":", PredName, "/", PredArity,
|
|
"\t", ProcNameStr, "\t", SchemaString, "\n"])
|
|
;
|
|
[]
|
|
).
|
|
|
|
:- pred rl_out__get_proc_schema(module_info::in, relation_info_map::in,
|
|
list(relation_id)::in, string::out) is det.
|
|
|
|
rl_out__get_proc_schema(ModuleInfo, Relations, Args, SchemaString) :-
|
|
list__map(
|
|
(pred(Arg::in, ArgSchema::out) is det :-
|
|
map__lookup(Relations, Arg, ArgInfo),
|
|
ArgInfo = relation_info(_, ArgSchema, _, _)
|
|
), Args, ArgSchemas),
|
|
rl__schemas_to_strings(ModuleInfo, ArgSchemas,
|
|
TypeDecls, ArgSchemaStrings),
|
|
list__map_foldl(
|
|
(pred(ArgSchemaString::in, ArgSchemaDecl::out,
|
|
Index::in, (Index + 1)::out) is det :-
|
|
ArgPrefix = "__arg_",
|
|
string__int_to_string(Index, ArgString),
|
|
string__append_list(
|
|
[":", ArgPrefix, ArgString, "=",
|
|
ArgPrefix, ArgString, "(",
|
|
ArgSchemaString, ") "],
|
|
ArgSchemaDecl)
|
|
), ArgSchemaStrings, ArgSchemaDeclList, 1, _),
|
|
rl_out__get_proc_schema_2(1, Args, "", SchemaString0),
|
|
list__condense([[TypeDecls | ArgSchemaDeclList], ["("],
|
|
[SchemaString0, ")"]], SchemaStrings),
|
|
string__append_list(SchemaStrings, SchemaString).
|
|
|
|
:- pred rl_out__get_proc_schema_2(int::in, list(T)::in,
|
|
string::in, string::out) is det.
|
|
|
|
rl_out__get_proc_schema_2(_, [], SchemaList, SchemaList).
|
|
rl_out__get_proc_schema_2(ArgNo, [_ | Args], SchemaList0, SchemaList) :-
|
|
ArgPrefix = "__arg_",
|
|
( Args = [] ->
|
|
Comma = ""
|
|
;
|
|
Comma = ","
|
|
),
|
|
string__int_to_string(ArgNo, ArgString),
|
|
string__append_list([SchemaList0, ":T", ArgPrefix, ArgString, Comma],
|
|
SchemaList1),
|
|
rl_out__get_proc_schema_2(ArgNo + 1, Args, SchemaList1, SchemaList).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
#if INCLUDE_ADITI_OUTPUT % See ../Mmake.common.in,
|
|
|
|
rl_out__generate_rl_bytecode(ModuleInfo, Procs, MaybeRLFile) -->
|
|
{ module_info_name(ModuleInfo, ModuleName0) },
|
|
module_name_to_file_name(ModuleName0, ".rlo", yes, RLOName),
|
|
module_name_to_file_name(ModuleName0, ".rla", yes, RLAName),
|
|
globals__io_lookup_bool_option(verbose, Verbose),
|
|
maybe_write_string(Verbose, "% Writing RL bytecode to `"),
|
|
maybe_write_string(Verbose, RLOName),
|
|
maybe_write_string(Verbose, "'..."),
|
|
maybe_flush_output(Verbose),
|
|
|
|
{ rl_out_info_init(ModuleInfo, RLInfo0) },
|
|
{ list__foldl(rl_out__generate_proc_bytecode, Procs,
|
|
RLInfo0, RLInfo1) },
|
|
|
|
globals__io_lookup_string_option(aditi_user, Owner),
|
|
{ rl_out_info_assign_const(string(Owner), OwnerIndex,
|
|
RLInfo1, RLInfo2) },
|
|
{ prog_out__sym_name_to_string(ModuleName0, ModuleName) },
|
|
module_name_to_file_name(ModuleName0, ".m", no, SourceFileName),
|
|
module_name_to_file_name(ModuleName0, ".int", no, IntFileName),
|
|
{ rl_out_info_assign_const(string(ModuleName), ModuleIndex,
|
|
RLInfo2, RLInfo3) },
|
|
{ rl_out_info_assign_const(string(IntFileName), IntIndex,
|
|
RLInfo3, RLInfo4) },
|
|
{ rl_out_info_assign_const(string(SourceFileName),
|
|
SourceIndex, RLInfo4, RLInfo5) },
|
|
{ rl_out_info_get_procs(RLProcs, RLInfo5, RLInfo6) },
|
|
{ rl_out_info_get_consts(Consts, RLInfo6, RLInfo7) },
|
|
{ rl_out_info_get_permanent_relations(PermRelsSet,
|
|
RLInfo7, RLInfo8) },
|
|
{ rl_out_info_get_relation_variables(RelVars, RLInfo8, _) },
|
|
|
|
{ map__to_assoc_list(Consts, ConstsAL) },
|
|
{ assoc_list__reverse_members(ConstsAL, ConstsLA0) },
|
|
{ list__sort(ConstsLA0, ConstsLA) },
|
|
{ list__length(ConstsLA, ConstTableSize0) },
|
|
{ ConstTableSize is ConstTableSize0 + 1 },
|
|
{ set__to_sorted_list(PermRelsSet, PermRels) },
|
|
{ list__length(PermRels, NumPermRels) },
|
|
{ list__length(RLProcs, NumProcs) },
|
|
|
|
{ list__length(RelVars, NumVars) },
|
|
|
|
{ rl_code__version(MinorVersion, MajorVersion) },
|
|
{ File = rl_file(
|
|
MinorVersion,
|
|
MajorVersion,
|
|
ConstTableSize,
|
|
ConstsLA,
|
|
NumPermRels,
|
|
PermRels,
|
|
NumVars,
|
|
RelVars,
|
|
NumProcs,
|
|
RLProcs,
|
|
OwnerIndex,
|
|
ModuleIndex,
|
|
SourceIndex,
|
|
IntIndex
|
|
) },
|
|
|
|
%
|
|
% Dump the binary representation to `<module>.rlo', if --aditi-only
|
|
% was specified, otherwise return the rl_file for output into the
|
|
% C file.
|
|
%
|
|
globals__io_lookup_bool_option(aditi_only, AditiOnly),
|
|
( { AditiOnly = yes } ->
|
|
io__open_binary_output(RLOName, RLOResult),
|
|
|
|
( { RLOResult = ok(RLOStream) } ->
|
|
io__set_binary_output_stream(RLOStream, OldBinStream),
|
|
rl_file__write_binary(io__write_byte, File, _),
|
|
io__close_binary_output(RLOStream),
|
|
io__set_binary_output_stream(OldBinStream, _)
|
|
;
|
|
{ string__append_list([
|
|
"cannot open `", RLOName, "' for output.\n"],
|
|
RLOError) },
|
|
report_error(RLOError)
|
|
),
|
|
{ MaybeRLFile = no }
|
|
;
|
|
{ MaybeRLFile = yes(File) }
|
|
),
|
|
|
|
%
|
|
% Dump the text representation to `<module>.rla'
|
|
%
|
|
globals__io_lookup_bool_option(dump_rl_bytecode, DumpToRLA),
|
|
( { DumpToRLA = yes } ->
|
|
io__open_output(RLAName, RLAResult),
|
|
( { RLAResult = ok(RLAStream) } ->
|
|
io__set_output_stream(RLAStream, OldStdOut),
|
|
rl_file__write_text(File),
|
|
io__close_output(RLAStream),
|
|
io__set_output_stream(OldStdOut, _)
|
|
;
|
|
{ string__append_list([
|
|
"cannot open `", RLAName, "' for output.\n"],
|
|
RLAError) },
|
|
report_error(RLAError)
|
|
)
|
|
;
|
|
[]
|
|
),
|
|
|
|
%
|
|
% Dump the schema information for derived relations to
|
|
% `<module>.derived_schema'.
|
|
%
|
|
globals__io_lookup_bool_option(generate_schemas, GenSchemas),
|
|
( { GenSchemas = yes } ->
|
|
module_name_to_file_name(ModuleName0, ".derived_schema",
|
|
no, SchemaFileName),
|
|
io__open_output(SchemaFileName, SchemaResult),
|
|
( { SchemaResult = ok(SchemaStream) } ->
|
|
io__set_output_stream(SchemaStream, OldStream),
|
|
list__foldl(
|
|
rl_out__generate_derived_schema(ModuleInfo),
|
|
Procs),
|
|
io__set_output_stream(OldStream, SchemaStream1),
|
|
io__close_output(SchemaStream1)
|
|
;
|
|
{ string__append_list([
|
|
"cannot open `", SchemaFileName,
|
|
"' for output.\n"], SchemaError) },
|
|
report_error(SchemaError)
|
|
)
|
|
;
|
|
[]
|
|
),
|
|
maybe_write_string(Verbose, "done\n").
|
|
#else
|
|
rl_out__generate_rl_bytecode(_, _, MaybeRLFile) -->
|
|
{ semidet_succeed ->
|
|
error("rl_out.pp: `--aditi' requires `INCLUDE_ADITI_OUTPUT'")
|
|
;
|
|
MaybeRLFile = no
|
|
}.
|
|
#endif
|
|
|
|
#if INCLUDE_ADITI_OUTPUT
|
|
:- pred rl_out__generate_proc_bytecode(rl_proc::in,
|
|
rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out__generate_proc_bytecode(Proc) -->
|
|
{ Proc = rl_proc(Name, Inputs, Outputs, MemoedRels,
|
|
Relations, RLInstrs, _) },
|
|
|
|
{ Name = rl_proc_name(Owner, Module, ProcName, _) },
|
|
|
|
{ list__append(Inputs, Outputs, Args) },
|
|
rl_out_info_init_proc(Relations, Args),
|
|
rl_out__generate_instr_list(RLInstrs, RLInstrCodeTree0),
|
|
|
|
{ set__to_sorted_list(MemoedRels, MemoedList) },
|
|
( { MemoedList = [] } ->
|
|
{ CollectCode = [] },
|
|
{ NameCode = [] },
|
|
{ GroupCode = empty }
|
|
;
|
|
% If one memoed relation is dropped, all must be
|
|
% dropped for correctness. We could possibly be a
|
|
% little smarter about this.
|
|
rl_out__collect_memoed_relations(Owner, Name, MemoedList, 0,
|
|
CollectCode, NameCode),
|
|
rl_out__get_rel_var_list(MemoedList, RelVarCodes),
|
|
{ GroupCode = tree(node([rl_PROC_grouprels]), RelVarCodes) }
|
|
),
|
|
|
|
rl_out_info_get_relation_addrs(Addrs),
|
|
{ map__to_assoc_list(Addrs, AddrsAL) },
|
|
rl_out__collect_permanent_relations(AddrsAL, [], PermRelCodes),
|
|
|
|
rl_out_info_get_proc_expressions(Exprns),
|
|
{ list__length(Exprns, NumExprns) },
|
|
|
|
rl_out__resolve_proc_addresses(RLInstrCodeTree0, RLInstrCodeTree1),
|
|
|
|
{ RLInstrCodeTree =
|
|
tree(node(PermRelCodes),
|
|
tree(node(CollectCode),
|
|
tree(RLInstrCodeTree1,
|
|
tree(node(NameCode),
|
|
tree(GroupCode,
|
|
node([rl_PROC_ret])
|
|
))))) },
|
|
{ tree__flatten(RLInstrCodeTree, CodeLists) },
|
|
{ list__condense(CodeLists, Codes) },
|
|
|
|
rl_out_info_assign_const(string(Owner), OwnerConst),
|
|
rl_out_info_assign_const(string(Module), ModuleConst),
|
|
rl_out_info_assign_const(string(ProcName), NameConst),
|
|
{ rl_out__instr_code_size(node(Codes), CodeLength) },
|
|
|
|
{ list__length(Args, NumArgs) },
|
|
list__map_foldl(rl_out_info_get_relation_addr, Args, ArgLocs),
|
|
rl_out__generate_proc_schema(Args, SchemaConst),
|
|
|
|
{ RLProc = procedure(OwnerConst, ModuleConst, NameConst, SchemaConst,
|
|
NumArgs, ArgLocs, NumExprns, Exprns,
|
|
CodeLength, Codes) },
|
|
rl_out_info_add_proc(RLProc).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Temporaries in Aditi are reference counted. If the count on a
|
|
% temporary goes to zero, it may be garbage collected. For relations
|
|
% which are memoed, we do not inhibit garbage collection by
|
|
% holding a reference to them. Instead we just give them a name
|
|
% by which we can retrieve the relation later. If the system does
|
|
% not need to garbage collect the relation between calls, it
|
|
% will be used, otherwise it will be reinitialised. If one
|
|
% memoed relation in a procedure is dropped, all must be dropped
|
|
% to maintain correctness. Aditi should prefer to drop unnamed
|
|
% temporaries to named ones, since unnamed temporaries cannot
|
|
% possibly be used later.
|
|
:- pred rl_out__collect_memoed_relations(string::in, rl_proc_name::in,
|
|
list(relation_id)::in, int::in, list(bytecode)::out,
|
|
list(bytecode)::out, rl_out_info::in,
|
|
rl_out_info::out) is det.
|
|
|
|
rl_out__collect_memoed_relations(_, _, [], _, [], []) --> [].
|
|
rl_out__collect_memoed_relations(Owner, ProcName, [Rel | Rels], Counter0,
|
|
[GetCode | GetCodes], [NameCode, DropCode | NameCodes]) -->
|
|
|
|
rl_out_info_get_relation_addr(Rel, Addr),
|
|
rl_out_info_get_relation_schema_offset(Rel, SchemaOffset),
|
|
|
|
{ rl__proc_name_to_string(ProcName, ProcNameStr) },
|
|
{ string__to_char_list(ProcNameStr, ProcNameList0) },
|
|
% Slashes are significant in relation names, so convert them to colons.
|
|
{ RemoveSlashes =
|
|
lambda([Char0::in, Char::out] is det, (
|
|
( Char0 = ('/') ->
|
|
Char = (':')
|
|
;
|
|
Char = Char0
|
|
)
|
|
)) },
|
|
{ list__map(RemoveSlashes, ProcNameList0, ProcNameList) },
|
|
{ string__from_char_list(ProcNameList, ProcNameStr1) },
|
|
rl_out_info_get_module_info(ModuleInfo),
|
|
{ module_info_name(ModuleInfo, ModuleName0) },
|
|
{ prog_out__sym_name_to_string(ModuleName0, ModuleName) },
|
|
{ string__format("%s/%s/Memoed__%s__%i",
|
|
[s(Owner), s(ModuleName), s(ProcNameStr1), i(Counter0)],
|
|
UniqueName) },
|
|
rl_out_info_assign_const(string(UniqueName), NameOffset),
|
|
|
|
% Get the memoed relation, if it exists, at the start of
|
|
% the procedure. If it does not exist it will be created.
|
|
{ GetCode = rl_PROC_settemprel(Addr, NameOffset, SchemaOffset) },
|
|
|
|
% Make sure the relation variable has the correct name
|
|
% at the end of the procedure so that the settemprel can
|
|
% find it at the start of the next call.
|
|
{ NameCode = rl_PROC_nametemprel(Addr, NameOffset) },
|
|
|
|
% Drop the pointer - this should already have been done
|
|
% for non-memoed relations, but we need to name memoed
|
|
% relations before dropping the pointers to them.
|
|
{ DropCode = rl_PROC_unsetrel(Addr) },
|
|
|
|
{ Counter is Counter0 + 1 },
|
|
rl_out__collect_memoed_relations(Owner, ProcName, Rels, Counter,
|
|
GetCodes, NameCodes).
|
|
|
|
% Put pointers to all the permanent relations
|
|
% used by the procedure into variables.
|
|
:- pred rl_out__collect_permanent_relations(assoc_list(relation_id, int)::in,
|
|
list(bytecode)::in, list(bytecode)::out,
|
|
rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out__collect_permanent_relations([], Codes, Codes) --> [].
|
|
rl_out__collect_permanent_relations([RelationId - Addr | Rels],
|
|
Codes0, Codes) -->
|
|
rl_out_info_get_relations(Relations),
|
|
{ map__lookup(Relations, RelationId, RelInfo) },
|
|
{ RelInfo = relation_info(RelType, _Schema, _Index, _) },
|
|
(
|
|
{ RelType = permanent(proc(PredId, _)) }
|
|
->
|
|
rl_out_info_get_module_info(ModuleInfo),
|
|
|
|
{ rl__get_permanent_relation_info(ModuleInfo, PredId,
|
|
Owner, PredModule, _, _, RelName, SchemaString) },
|
|
|
|
rl_out_info_assign_const(string(Owner), OwnerConst),
|
|
rl_out_info_assign_const(string(PredModule), PredModuleConst),
|
|
rl_out_info_assign_const(string(SchemaString), SchemaOffset),
|
|
rl_out_info_assign_const(string(RelName), RelNameConst),
|
|
|
|
rl_out_info_get_permanent_relations(PermRels0),
|
|
{ set__insert(PermRels0,
|
|
relation(OwnerConst, PredModuleConst,
|
|
RelNameConst, SchemaOffset),
|
|
PermRels) },
|
|
rl_out_info_set_permanent_relations(PermRels),
|
|
|
|
{ string__format("%s/%s/%s",
|
|
[s(Owner), s(PredModule), s(RelName)], Name) },
|
|
rl_out_info_assign_const(string(Name), RelNameOffset),
|
|
{ SetCode = rl_PROC_openpermrel(Addr, RelNameOffset,
|
|
SchemaOffset) },
|
|
{ Codes1 = [SetCode | Codes0] }
|
|
;
|
|
{ Codes1 = Codes0 }
|
|
),
|
|
rl_out__collect_permanent_relations(Rels, Codes1, Codes).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred rl_out__get_rel_var_list(list(relation_id)::in, byte_tree::out,
|
|
rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out__get_rel_var_list(Rels, Code) -->
|
|
list__map_foldl(rl_out_info_get_relation_addr, Rels, Addrs),
|
|
{ ConsElems = lambda([Addr::in, Cons::out] is det, (
|
|
LockSpec = 0,
|
|
Cons = rl_PROC_var_list_cons(Addr, LockSpec)
|
|
)) },
|
|
{ list__map(ConsElems, Addrs, Code1) },
|
|
{ Code = tree(node(Code1), node([rl_PROC_var_list_nil])) }.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Generate the schema string for a procedure.
|
|
:- pred rl_out__generate_proc_schema(list(relation_id)::in, int::out,
|
|
rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out__generate_proc_schema(Args, SchemaOffset) -->
|
|
rl_out_info_get_module_info(ModuleInfo),
|
|
rl_out_info_get_relations(Relations),
|
|
{ rl_out__get_proc_schema(ModuleInfo, Relations, Args, SchemaString) },
|
|
rl_out_info_assign_const(string(SchemaString), SchemaOffset).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Convert a schema for use in the bytecode. A schema string is
|
|
% a list of type definitions followed by a bracketed list
|
|
% of types.
|
|
:- pred rl_out__schema_to_string(list(type)::in, int::out,
|
|
rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out__schema_to_string(Types, SchemaOffset) -->
|
|
rl_out_info_get_module_info(ModuleInfo),
|
|
{ rl__schema_to_string(ModuleInfo, Types, SchemaString) },
|
|
rl_out_info_assign_const(string(SchemaString), SchemaOffset).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred rl_out__generate_instr_list(list(rl_instruction)::in, byte_tree::out,
|
|
rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out__generate_instr_list([], empty) --> [].
|
|
rl_out__generate_instr_list([RLInstr | RLInstrs], Code) -->
|
|
rl_out__generate_instr(RLInstr, Code1),
|
|
{ rl_out__instr_code_size(Code1, Size) },
|
|
rl_out_info_incr_pc(Size),
|
|
rl_out__generate_instr_list(RLInstrs, Code2),
|
|
{ Code = tree(Code1, Code2) }.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred rl_out__generate_instr(rl_instruction::in, byte_tree::out,
|
|
rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out__generate_instr(join(Output, Input1, Input2, Type, Cond) - _, Code) -->
|
|
(
|
|
{ Type = nested_loop },
|
|
rl_out__generate_join(rl_PROC_join_nl, Output,
|
|
Input1, Input2, Cond, Code)
|
|
;
|
|
{ Type = sort_merge(_, _) },
|
|
rl_out__generate_join(rl_PROC_join_sm, Output,
|
|
Input1, Input2, Cond, Code)
|
|
;
|
|
{ Type = index(IndexSpec, Range) },
|
|
{ rl_out__index_spec_to_string(IndexSpec, IndexStr) },
|
|
rl_out_info_assign_const(string(IndexStr), IndexConst),
|
|
rl_out__generate_stream(Input1, Stream1Code),
|
|
rl_out_info_get_relation_addr(Input2, Input2Addr),
|
|
rl_out__generate_key_range(Range, RangeExprn),
|
|
rl_out_info_get_output_relation_schema_offset(Output,
|
|
OutputSchemaOffset),
|
|
rl_out__generate_exprn(Cond, OutputSchemaOffset, CondExprn),
|
|
{ Code =
|
|
tree(node([rl_PROC_join_index_simple]),
|
|
tree(Stream1Code,
|
|
node([
|
|
rl_PROC_indexed_var(Input2Addr, 0, IndexConst),
|
|
rl_PROC_expr(RangeExprn),
|
|
rl_PROC_expr(CondExprn)
|
|
])
|
|
)) }
|
|
;
|
|
{ Type = cross },
|
|
rl_out__generate_join(rl_PROC_join_cross, Output,
|
|
Input1, Input2, Cond, Code)
|
|
;
|
|
{ Type = semi },
|
|
%
|
|
% Optimize a common case here - if the output does not depend
|
|
% on the second relation, we generate this as:
|
|
% if (empty(rel2)) {
|
|
% init(output);
|
|
% } else {
|
|
% output = rel1;
|
|
% }
|
|
%
|
|
% This happens for joins with zero-arity input relations.
|
|
(
|
|
{ rl__goal_is_independent_of_input(two,
|
|
Cond, _Cond1) }
|
|
->
|
|
rl_out__generate_stream(Input1, Stream1Code),
|
|
rl_out__generate_stream(Input2, Stream2Code),
|
|
{ CondCode =
|
|
tree(node([rl_PROC_empty]),
|
|
Stream2Code) },
|
|
rl_out__generate_instr(init(Output) - "", ThenCode),
|
|
rl_out__generate_stream_instruction(Output,
|
|
Stream1Code, ElseCode),
|
|
rl_out__generate_ite(CondCode, ThenCode, ElseCode,
|
|
Code)
|
|
;
|
|
rl_out__generate_join(rl_PROC_join_sm, Output,
|
|
Input1, Input2, Cond, Code)
|
|
)
|
|
).
|
|
|
|
rl_out__generate_instr(subtract(Output, Input1, Input2, Type, Cond) - _,
|
|
Code) -->
|
|
rl_out__generate_stream(Input1, Stream1Code),
|
|
rl_out__generate_stream(Input2, Stream2Code),
|
|
rl_out_info_get_output_relation_schema_offset(Output,
|
|
OutputSchemaOffset),
|
|
rl_out__generate_exprn(Cond, OutputSchemaOffset, CondExprn),
|
|
(
|
|
{ Type = nested_loop },
|
|
{ SubtractCode = rl_PROC_subtract_nl }
|
|
;
|
|
{ Type = sort_merge(_, _) },
|
|
{ SubtractCode = rl_PROC_subtract_sm }
|
|
;
|
|
{ Type = index(_IndexSpec, _) },
|
|
{ error(
|
|
"rl_out__generate_instr: subtract_index not yet implemented") }
|
|
),
|
|
{ InstrCode =
|
|
tree(node([SubtractCode]),
|
|
tree(Stream1Code,
|
|
tree(Stream2Code,
|
|
node([rl_PROC_expr(CondExprn)])
|
|
))) },
|
|
rl_out__generate_stream_instruction(Output, InstrCode, Code).
|
|
rl_out__generate_instr(difference(Output, Input1, Input2, Type) - _,
|
|
Code) -->
|
|
rl_out__generate_stream(Input1, Stream1Code),
|
|
rl_out__generate_stream(Input2, Stream2Code),
|
|
{ Type = sort_merge(Spec) },
|
|
rl_out_info_get_output_relation_schema(Output, OutputSchema),
|
|
rl_out__generate_compare_exprn(Spec, OutputSchema, CompareExprn),
|
|
{ InstrCode =
|
|
tree(node([rl_PROC_difference]),
|
|
tree(Stream1Code,
|
|
tree(Stream2Code,
|
|
node([rl_PROC_expr(CompareExprn)])
|
|
))) },
|
|
rl_out__generate_stream_instruction(Output, InstrCode, Code).
|
|
rl_out__generate_instr(project(Output, Input, Cond0,
|
|
OtherOutputs, ProjectType) - _, Code) -->
|
|
rl_out_info_get_output_relation_schema_offset(Output,
|
|
OutputSchemaOffset),
|
|
|
|
% If the produced tuple is independent of the input tuple,
|
|
% generate:
|
|
% if (empty(Input)) {
|
|
% init(Output);
|
|
% } else
|
|
% init(Output);
|
|
% insert_tuple(Output, Tuple);
|
|
% }
|
|
%
|
|
% This can happen for tables of facts.
|
|
%
|
|
% Projections of this type are never combined with
|
|
% other projections of the same input relation in the
|
|
% one instruction by rl_block_opt.m.
|
|
(
|
|
{ OtherOutputs = [] },
|
|
{ rl__goal_is_independent_of_input(one, Cond0, Cond) }
|
|
->
|
|
rl_out__generate_exprn(Cond, OutputSchemaOffset, CondExprn),
|
|
rl_out__generate_stream(Input, StreamCode),
|
|
{ CondCode = tree(node([rl_PROC_empty]), StreamCode) },
|
|
rl_out__generate_instr(init(Output) - "", ThenCode),
|
|
{ TupleCode = node([
|
|
rl_PROC_insert_tuple_stream,
|
|
rl_PROC_stream,
|
|
rl_PROC_empty_stream(OutputSchemaOffset),
|
|
rl_PROC_stream_end,
|
|
rl_PROC_expr(CondExprn)
|
|
]) },
|
|
rl_out__generate_stream_instruction(Output, TupleCode,
|
|
ElseCode),
|
|
rl_out__generate_ite(CondCode, ThenCode, ElseCode, Code)
|
|
;
|
|
(
|
|
{ ProjectType = filter },
|
|
rl_out__generate_stream(Input, StreamCode)
|
|
;
|
|
% For an indexed project/select we do a btree_scan
|
|
% to select out the range of tuples we're interested
|
|
% in, then proceed as normal.
|
|
{ ProjectType = index(IndexSpec, Range) },
|
|
{ rl_out__index_spec_to_string(IndexSpec, IndexStr) },
|
|
rl_out_info_get_relation_addr(Input, InputAddr),
|
|
rl_out_info_assign_const(string(IndexStr), IndexConst),
|
|
rl_out__generate_key_range(Range, RangeExprn),
|
|
{ StreamCode = node([
|
|
rl_PROC_stream,
|
|
rl_PROC_btree_scan,
|
|
rl_PROC_indexed_var(InputAddr, 0, IndexConst),
|
|
rl_PROC_expr(RangeExprn),
|
|
rl_PROC_stream_end
|
|
]) }
|
|
),
|
|
|
|
rl_out__generate_exprn(Cond0, OutputSchemaOffset, CondExprn),
|
|
|
|
%
|
|
% Initialise the other output relations.
|
|
%
|
|
{ assoc_list__keys(OtherOutputs, OtherOutputRels) },
|
|
list__map_foldl(
|
|
(pred(TheOutput::in, RelInitCode::out, in, out) is det -->
|
|
rl_out__generate_instr(init(TheOutput) - "",
|
|
RelInitCode)
|
|
),
|
|
OtherOutputRels, OtherOutputInitCodeList),
|
|
{ list__foldl(
|
|
(pred(InitCode::in, Tree0::in, Tree::out) is det :-
|
|
Tree = tree(Tree0, InitCode)
|
|
),
|
|
OtherOutputInitCodeList, empty, OtherOutputInitCode) },
|
|
|
|
{ list__map(rl__output_rel_relation,
|
|
OtherOutputRels, OtherOutputRelations) },
|
|
rl_out__get_rel_var_list(OtherOutputRelations, VarListCode),
|
|
list__foldl2(rl_out__generate_project_exprn, OtherOutputs,
|
|
empty, ExprnListCode),
|
|
{ InstrCode =
|
|
tree(node([rl_PROC_project_tee]),
|
|
tree(StreamCode,
|
|
tree(node([rl_PROC_expr(CondExprn)]),
|
|
tree(VarListCode,
|
|
tree(ExprnListCode,
|
|
node([rl_PROC_expr_list_nil])
|
|
))))) },
|
|
rl_out__generate_stream_instruction(Output, InstrCode, Code0),
|
|
{ Code = tree(OtherOutputInitCode, Code0) }
|
|
).
|
|
rl_out__generate_instr(union(Output, Inputs, Type) - _, Code) -->
|
|
{ UnionCode = rl_PROC_union_sm },
|
|
{ Type = sort_merge(Spec) },
|
|
rl_out_info_get_output_relation_schema(Output, OutputSchema),
|
|
rl_out__generate_compare_exprn(Spec, OutputSchema, CompareExprn),
|
|
rl_out__generate_union(UnionCode, CompareExprn, Inputs, InstrCode),
|
|
rl_out__generate_stream_instruction(Output, InstrCode, Code).
|
|
rl_out__generate_instr(insert(_, _, _, _, _) - _, _) -->
|
|
{ error("rl_out__generate_instr: insert not yet implemented") }.
|
|
rl_out__generate_instr(
|
|
union_diff(UoOutput, DiInput, Input, Diff, Index, CopyInfo) - _,
|
|
Code) -->
|
|
{ CopyInfo = yes(_) ->
|
|
% This should be removed by rl_liveness.m.
|
|
error("rl_out__generate_instr: copy info on union_diff")
|
|
;
|
|
true
|
|
},
|
|
{ rl_out__index_spec_to_string(Index, IndexStr) },
|
|
rl_out_info_assign_const(string(IndexStr), IndexConst),
|
|
rl_out__generate_stream(Input, StreamCode),
|
|
rl_out_info_get_relation_addr(DiInput, DiInputAddr),
|
|
rl_out_info_get_relation_addr(UoOutput, UoOutputAddr),
|
|
{ InstrCode =
|
|
tree(node([
|
|
rl_PROC_uniondiff_btree,
|
|
rl_PROC_indexed_var(DiInputAddr, 0, IndexConst)
|
|
]),
|
|
StreamCode
|
|
) },
|
|
rl_out__generate_stream_instruction(Diff, InstrCode, Code0),
|
|
{ Code =
|
|
tree(Code0,
|
|
node([rl_PROC_setrel(UoOutputAddr, DiInputAddr)])
|
|
) }.
|
|
rl_out__generate_instr(sort(Output, Input, Attrs) - _, Code) -->
|
|
rl_out__generate_stream(Input, StreamCode),
|
|
rl_out_info_get_output_relation_schema(Output, OutputSchema),
|
|
rl_out__generate_compare_exprn(attributes(Attrs),
|
|
OutputSchema, CompareExprn),
|
|
|
|
% If we are sorting on all attributes we do duplicate removal
|
|
% as well. We shouldn't do this when sorting on a subset of the
|
|
% attributes because the duplicate removal only takes the compared
|
|
% attributes into account.
|
|
{ list__sort_and_remove_dups(Attrs, SortedAttrs) },
|
|
{ list__length(SortedAttrs, NumAttrs) },
|
|
{ list__length(OutputSchema, NumAttrs) ->
|
|
Filter = 1
|
|
;
|
|
Filter = 0
|
|
},
|
|
{ InstrCode =
|
|
tree(node([rl_PROC_sort(Filter)]),
|
|
tree(StreamCode,
|
|
node([rl_PROC_expr(CompareExprn)])
|
|
)) },
|
|
rl_out__generate_stream_instruction(Output, InstrCode, Code).
|
|
rl_out__generate_instr(add_index(output_rel(Rel, Indexes)) - _, Code) -->
|
|
rl_out__add_indexes_to_rel(may_have_index, Rel, Indexes, Code).
|
|
rl_out__generate_instr(clear(Rel) - _, Code) -->
|
|
rl_out_info_get_relation_addr(Rel, Addr),
|
|
{ Code = node([rl_PROC_clear(Addr)]) }.
|
|
rl_out__generate_instr(init(output_rel(Rel, Indexes)) - _, Code) -->
|
|
rl_out_info_get_relation_addr(Rel, Addr),
|
|
rl_out_info_get_relation_schema_offset(Rel, SchemaOffset),
|
|
rl_out__add_indexes_to_rel(does_not_have_index,
|
|
Rel, Indexes, IndexCodes),
|
|
{ Code =
|
|
tree(node([
|
|
rl_PROC_unsetrel(Addr),
|
|
rl_PROC_createtemprel(Addr, SchemaOffset)
|
|
]),
|
|
IndexCodes
|
|
) }.
|
|
rl_out__generate_instr(insert_tuple(Output, Input, Exprn) - _, Code) -->
|
|
rl_out__generate_stream(Input, InputStream),
|
|
rl_out_info_get_output_relation_schema_offset(Output,
|
|
OutputSchemaOffset),
|
|
rl_out__generate_exprn(Exprn, OutputSchemaOffset, ExprnNo),
|
|
{ InstrCode =
|
|
tree(node([rl_PROC_insert_tuple_stream]),
|
|
tree(InputStream,
|
|
node([rl_PROC_expr(ExprnNo)])
|
|
)) },
|
|
rl_out__generate_stream_instruction(Output, InstrCode, Code).
|
|
rl_out__generate_instr(unset(Rel) - _, Code) -->
|
|
rl_out_info_get_relation_addr(Rel, Addr),
|
|
{ Code = node([rl_PROC_unsetrel(Addr)]) }.
|
|
rl_out__generate_instr(conditional_goto(Cond, Label) - _, Code) -->
|
|
rl_out__generate_goto_cond(Cond, CondCode),
|
|
{ Code = tree(node([rl_PROC_conditional_goto_label(Label)]),
|
|
CondCode) }.
|
|
rl_out__generate_instr(goto(Label) - _, node([rl_PROC_goto_label(Label)])) -->
|
|
[].
|
|
rl_out__generate_instr(label(Label) - _, node([rl_PROC_label(LabelNo)])) -->
|
|
rl_out_info_add_label(Label, LabelNo).
|
|
rl_out__generate_instr(ref(OutputRel, InputRel) - _, Code) -->
|
|
rl_out_info_get_relation_type(InputRel, InputType),
|
|
rl_out_info_get_relation_type(OutputRel, OutputType),
|
|
(
|
|
{ InputType = temporary(stream) },
|
|
{ OutputType = temporary(materialised) }
|
|
->
|
|
rl_out__generate_instr(
|
|
copy(output_rel(OutputRel, []), InputRel) - "",
|
|
Code)
|
|
;
|
|
rl_out_info_get_relation_addr(InputRel, InputAddr),
|
|
rl_out_info_get_relation_addr(OutputRel, OutputAddr),
|
|
{ Code = node([rl_PROC_setrel(OutputAddr, InputAddr)]) }
|
|
).
|
|
rl_out__generate_instr(copy(OutputRel, InputRel) - _, Code) -->
|
|
% Unfortunately there are internal Aditi reasons why copy
|
|
% must be done as a materialise of each tuple into the new
|
|
% relation rather than just as a copy of the files.
|
|
rl_out_info_get_relation_addr(InputRel, InputAddr),
|
|
{ OutputRel = output_rel(Output, _) },
|
|
rl_out_info_get_relation_addr(Output, OutputAddr),
|
|
|
|
% The code for the `init' instruction
|
|
% will also add any necessary indexes.
|
|
rl_out__generate_instr(init(OutputRel) - "", InitCode),
|
|
|
|
rl_out_info_get_next_materialise_id(Id),
|
|
{ Code =
|
|
tree(InitCode,
|
|
node([
|
|
rl_PROC_materialise(Id),
|
|
rl_PROC_stream,
|
|
rl_PROC_var(InputAddr, 0),
|
|
rl_PROC_stream_end,
|
|
rl_PROC_var_list_cons(OutputAddr, 0),
|
|
rl_PROC_var_list_nil
|
|
])
|
|
) }.
|
|
rl_out__generate_instr(make_unique(OutputRel, Input) - Comment, Code) -->
|
|
% if (one_reference(InputRel)) {
|
|
% OutputRel = ref(InputRel)
|
|
% } else {
|
|
% OutputRel = copy(InputRel)
|
|
% }
|
|
rl_out_info_get_relation_addr(Input, InputAddr),
|
|
{ CondCode = node([rl_PROC_one_reference(InputAddr)]) },
|
|
|
|
{ OutputRel = output_rel(Output, _) },
|
|
rl_out__generate_instr(ref(Output, Input) - Comment, ThenCode0),
|
|
% We may not need to generate this instruction - rl_sort.m
|
|
% has enough information to work out whether this is actually needed.
|
|
rl_out__generate_instr(add_index(OutputRel) - Comment, ThenCode1),
|
|
{ ThenCode = tree(ThenCode0, ThenCode1) },
|
|
|
|
rl_out__generate_instr(copy(OutputRel, Input) - Comment, ElseCode),
|
|
|
|
rl_out__generate_ite(CondCode, ThenCode, ElseCode, Code).
|
|
rl_out__generate_instr(call(ProcName, Inputs, OutputRels, SaveRels) - _,
|
|
Code) -->
|
|
|
|
rl_out__save_input_args(Inputs, NewInputs, SaveRels,
|
|
SaveTmpVars, SaveCode),
|
|
{ list__map(rl__output_rel_relation, OutputRels, Outputs) },
|
|
rl_out__handle_overlapping_args(Outputs, NewOutputs, Inputs,
|
|
OverlapTmpVars, OverlapCode),
|
|
|
|
{ list__append(NewInputs, NewOutputs, CallArgs) },
|
|
{ list__map(lambda([Arg::in, ArgCode::out] is det, (
|
|
ArgCode = rl_PROC_var_list_cons(Arg, 0)
|
|
)), CallArgs, CallArgCodes) },
|
|
{ rl__proc_name_to_string(ProcName, ProcNameStr) },
|
|
rl_out_info_assign_const(string(ProcNameStr), ProcNameConst),
|
|
rl_out_info_return_tmp_vars(SaveTmpVars, SaveClearCode),
|
|
rl_out_info_return_tmp_vars(OverlapTmpVars, OverlapClearCode),
|
|
rl_out__add_indexes_to_rels(does_not_have_index,
|
|
OutputRels, IndexCode),
|
|
{ Code =
|
|
tree(SaveCode,
|
|
tree(node([rl_PROC_call(ProcNameConst)]),
|
|
tree(node(CallArgCodes),
|
|
tree(node([rl_PROC_var_list_nil]),
|
|
tree(OverlapCode,
|
|
tree(OverlapClearCode,
|
|
tree(SaveClearCode,
|
|
IndexCode
|
|
))))))) }.
|
|
rl_out__generate_instr(aggregate(Output, Input,
|
|
ComputeInitial, UpdateAcc) - _, Code) -->
|
|
rl_out__generate_stream(Input, InputCode),
|
|
rl_out__generate_aggregate_exprn(ComputeInitial, UpdateAcc,
|
|
Input, Output, AggExprn),
|
|
{ InstrCode =
|
|
tree(node([rl_PROC_aggregate]),
|
|
tree(InputCode,
|
|
node([rl_PROC_expr(AggExprn)])
|
|
)) },
|
|
rl_out__generate_stream_instruction(Output, InstrCode, Code).
|
|
rl_out__generate_instr(comment - _, empty) --> [].
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred rl_out__generate_join(bytecode::in, output_rel::in,
|
|
relation_id::in, relation_id::in, rl_goal::in,
|
|
byte_tree::out, rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out__generate_join(JoinCode, Output, Input1, Input2, Cond, Code) -->
|
|
rl_out_info_get_output_relation_schema_offset(Output,
|
|
OutputSchemaOffset),
|
|
rl_out__generate_stream(Input1, Stream1Code),
|
|
rl_out__generate_stream(Input2, Stream2Code),
|
|
rl_out__generate_exprn(Cond, OutputSchemaOffset, CondExprn),
|
|
{ InstrCode =
|
|
tree(node([JoinCode]),
|
|
tree(Stream1Code,
|
|
tree(Stream2Code,
|
|
node([rl_PROC_expr(CondExprn)])
|
|
))) },
|
|
rl_out__generate_stream_instruction(Output, InstrCode, Code).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Copy any arguments which are needed again later to a temporary
|
|
% location. The called procedure can then do what it likes to
|
|
% the new variable (except changing the contents of the relation
|
|
% it points to on entry).
|
|
:- pred rl_out__save_input_args(list(relation_id)::in, list(int)::out,
|
|
set(relation_id)::in, assoc_list(int, int)::out, byte_tree::out,
|
|
rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out__save_input_args([], [], _, [], empty) --> [].
|
|
rl_out__save_input_args([Input | Inputs], [InputAddr | InputAddrs],
|
|
SavedInputs, TmpVars, SaveCode) -->
|
|
rl_out__save_input_args(Inputs, InputAddrs, SavedInputs,
|
|
TmpVars1, SaveCode1),
|
|
rl_out_info_get_relation_schema_offset(Input, InputSchemaOffset),
|
|
rl_out_info_get_relation_addr(Input, OldInputAddr),
|
|
( { set__member(Input, SavedInputs) } ->
|
|
rl_out_info_get_tmp_var(InputSchemaOffset, InputAddr),
|
|
{ SaveCode =
|
|
tree(node([rl_PROC_setrel(InputAddr, OldInputAddr)]),
|
|
SaveCode1
|
|
) },
|
|
{ TmpVars = [InputSchemaOffset - InputAddr | TmpVars1] }
|
|
;
|
|
{ InputAddr = OldInputAddr },
|
|
{ SaveCode = SaveCode1 },
|
|
{ TmpVars = TmpVars1 }
|
|
).
|
|
|
|
% Where input and output relations overlap, put the overlapping
|
|
% outputs in new temporaries, then copy them over the inputs
|
|
% after the call. This should be very rarely needed, if at all.
|
|
:- pred rl_out__handle_overlapping_args(list(relation_id)::in, list(int)::out,
|
|
list(relation_id)::in, assoc_list(int, int)::out,
|
|
byte_tree::out, rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out__handle_overlapping_args([], [], _, [], empty) --> [].
|
|
rl_out__handle_overlapping_args([Output | Outputs], [NewOutput | NewOutputs],
|
|
Inputs, TmpVars, OverlapCode) -->
|
|
rl_out__handle_overlapping_args(Outputs, NewOutputs, Inputs,
|
|
TmpVars0, OverlapCode0),
|
|
rl_out_info_get_relation_addr(Output, OutputAddr),
|
|
( { list__member(Output, Inputs) } ->
|
|
rl_out_info_get_relation_schema_offset(Output,
|
|
OutputSchemaOffset),
|
|
rl_out_info_add_relation_variable(OutputSchemaOffset,
|
|
NewOutput),
|
|
{ OverlapCode1 =
|
|
node([
|
|
rl_PROC_setrel(OutputAddr, NewOutput),
|
|
rl_PROC_unsetrel(NewOutput)
|
|
]) },
|
|
{ OverlapCode = tree(OverlapCode0, OverlapCode1) },
|
|
{ TmpVars = [OutputSchemaOffset - NewOutput | TmpVars0] }
|
|
;
|
|
{ NewOutput = OutputAddr },
|
|
{ OverlapCode = OverlapCode0 },
|
|
{ TmpVars = TmpVars0 }
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred rl_out__generate_project_exprn(pair(output_rel, rl_goal)::in,
|
|
byte_tree::in, byte_tree::out,
|
|
rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out__generate_project_exprn(Output - Exprn,
|
|
ExprnListCode0, ExprnListCode) -->
|
|
rl_out_info_get_output_relation_schema_offset(Output,
|
|
OutputSchemaOffset),
|
|
rl_out__generate_exprn(Exprn, OutputSchemaOffset, ExprnNum),
|
|
{ ExprnListCode = tree(ExprnListCode0,
|
|
node([rl_PROC_expr_list_cons(ExprnNum)])) }.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred rl_out__generate_goto_cond(goto_cond::in, byte_tree::out,
|
|
rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out__generate_goto_cond(empty(RelationId), Code) -->
|
|
rl_out_info_get_relation_addr(RelationId, RelationAddr),
|
|
{ LockSpec = 0 }, % get default lock spec.
|
|
{ Code = node([
|
|
rl_PROC_empty,
|
|
rl_PROC_stream,
|
|
rl_PROC_var(RelationAddr, LockSpec),
|
|
rl_PROC_stream_end
|
|
]) }.
|
|
rl_out__generate_goto_cond(and(Cond1, Cond2), Code) -->
|
|
rl_out__generate_goto_cond(Cond1, Code1),
|
|
rl_out__generate_goto_cond(Cond2, Code2),
|
|
{ Code =
|
|
tree(node([rl_PROC_and]),
|
|
tree(Code1,
|
|
Code2)
|
|
) }.
|
|
rl_out__generate_goto_cond(or(Cond1, Cond2), Code) -->
|
|
rl_out__generate_goto_cond(Cond1, Code1),
|
|
rl_out__generate_goto_cond(Cond2, Code2),
|
|
{ Code =
|
|
tree(node([rl_PROC_or]),
|
|
tree(Code1,
|
|
Code2
|
|
)) }.
|
|
rl_out__generate_goto_cond(not(Cond), Code) -->
|
|
rl_out__generate_goto_cond(Cond, Code1),
|
|
{ Code =
|
|
tree(node([rl_PROC_not]),
|
|
Code1
|
|
) }.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Generate an if-then-else in the bytecode. This is used to handle
|
|
% some trivial cases where we didn't want to introduce the extra
|
|
% branching in the code earlier to avoid inhibiting other
|
|
% optimizations.
|
|
:- pred rl_out__generate_ite(byte_tree::in, byte_tree::in, byte_tree::in,
|
|
byte_tree::out, rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out__generate_ite(CondCode, ThenCode, ElseCode, IteCode) -->
|
|
rl_out_info_add_label(GotoLabel),
|
|
rl_out_info_add_label(EndLabel),
|
|
{ IteCode =
|
|
tree(node([
|
|
rl_PROC_conditional_goto(GotoLabel)
|
|
]),
|
|
tree(CondCode,
|
|
tree(ElseCode,
|
|
tree(node([
|
|
rl_PROC_goto(EndLabel),
|
|
rl_PROC_label(GotoLabel)
|
|
]),
|
|
tree(ThenCode,
|
|
node([rl_PROC_label(EndLabel)])
|
|
))))) }.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred rl_out__index_spec_to_string(index_spec::in, string::out) is det.
|
|
|
|
rl_out__index_spec_to_string(index_spec(Type, Attrs), IndexString) :-
|
|
(
|
|
Type = unique_B_tree,
|
|
TypeStr = "bu"
|
|
;
|
|
Type = non_unique_B_tree,
|
|
TypeStr = "bn"
|
|
),
|
|
( Attrs = [FirstAttr | OtherAttrs] ->
|
|
rl_out__index_attrs_to_string(FirstAttr,
|
|
OtherAttrs, "", AttrString)
|
|
;
|
|
error("rl_out__index_spec_to_string: no indexed attributes")
|
|
),
|
|
string__append_list([TypeStr, "(", AttrString, ")"], IndexString).
|
|
|
|
:- pred rl_out__index_attrs_to_string(int::in, list(int)::in,
|
|
string::in, string::out) is det.
|
|
|
|
rl_out__index_attrs_to_string(Attr, [], Str0, Str) :-
|
|
rl_out__index_attr_to_string(Attr, Str1),
|
|
string__append(Str0, Str1, Str).
|
|
rl_out__index_attrs_to_string(Attr1, [Attr2 | Attrs], Str0, Str) :-
|
|
rl_out__index_attr_to_string(Attr1, Str1),
|
|
string__append_list([Str0, Str1, ", "], Str2),
|
|
rl_out__index_attrs_to_string(Attr2, Attrs, Str2, Str).
|
|
|
|
:- pred rl_out__index_attr_to_string(int::in, string::out) is det.
|
|
|
|
rl_out__index_attr_to_string(Attr, Str) :-
|
|
string__int_to_string(Attr, AttrStr),
|
|
string__append("#:", AttrStr, Str).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- type check_index
|
|
---> may_have_index
|
|
; does_not_have_index
|
|
.
|
|
|
|
:- pred rl_out__add_indexes_to_rels(check_index::in,
|
|
list(output_rel)::in, byte_tree::out,
|
|
rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out__add_indexes_to_rels(_, [], empty) --> [].
|
|
rl_out__add_indexes_to_rels(CheckIndex,
|
|
[output_rel(Output, Indexes) | Outputs], IndexCode) -->
|
|
rl_out__add_indexes_to_rel(CheckIndex, Output, Indexes, IndexCode0),
|
|
rl_out__add_indexes_to_rels(CheckIndex, Outputs, IndexCode1),
|
|
{ IndexCode = tree(IndexCode0, IndexCode1) }.
|
|
|
|
:- pred rl_out__add_indexes_to_rel(check_index::in, relation_id::in,
|
|
list(index_spec)::in, byte_tree::out,
|
|
rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out__add_indexes_to_rel(_, _, [], empty) --> [].
|
|
rl_out__add_indexes_to_rel(CheckIndex, Output,
|
|
[Index | Indexes], IndexCode) -->
|
|
rl_out_info_get_relation_addr(Output, OutputAddr),
|
|
{ rl_out__index_spec_to_string(Index, IndexStr) },
|
|
rl_out_info_assign_const(string(IndexStr), IndexConst),
|
|
|
|
(
|
|
{ CheckIndex = may_have_index },
|
|
% Generate code to test whether the index already exists
|
|
% before adding it.
|
|
{ CondCode = node([
|
|
rl_PROC_has_index(OutputAddr, IndexConst)
|
|
]) },
|
|
{ ThenCode = empty },
|
|
{ ElseCode = node([
|
|
rl_PROC_addindextorel(OutputAddr, IndexConst)
|
|
]) },
|
|
rl_out__generate_ite(CondCode, ThenCode, ElseCode, IndexCode0)
|
|
;
|
|
{ CheckIndex = does_not_have_index },
|
|
{ IndexCode0 = node([
|
|
rl_PROC_addindextorel(OutputAddr, IndexConst)
|
|
]) }
|
|
),
|
|
rl_out__add_indexes_to_rel(CheckIndex,
|
|
OutputAddr, Indexes, IndexCode1),
|
|
{ IndexCode = tree(IndexCode0, IndexCode1) }.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Generate code to handle an instruction that could return a
|
|
% stream, either by binding the stream to a variable to be
|
|
% evaulated later or materialising it into a relation variable.
|
|
:- pred rl_out__generate_stream_instruction(output_rel::in, byte_tree::in,
|
|
byte_tree::out, rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out__generate_stream_instruction(output_rel(Output, Indexes),
|
|
InstrCode, Code) -->
|
|
rl_out_info_get_relation_addr(Output, OutputAddr),
|
|
rl_out_info_get_relation_type(Output, RelType),
|
|
{ Stream =
|
|
tree(node([rl_PROC_stream]),
|
|
tree(InstrCode,
|
|
node([rl_PROC_stream_end])
|
|
)) },
|
|
|
|
( { RelType = temporary(stream) } ->
|
|
{ Code =
|
|
tree(node([
|
|
rl_PROC_unsetrel(OutputAddr),
|
|
rl_PROC_bind_handle(OutputAddr)
|
|
]),
|
|
Stream
|
|
) }
|
|
;
|
|
rl_out_info_get_relation_schema_offset(Output, SchemaOffset),
|
|
rl_out_info_get_tmp_var(SchemaOffset, TmpVar),
|
|
rl_out_info_return_tmp_var(SchemaOffset,
|
|
TmpVar, TmpClearCode),
|
|
|
|
{ LockSpec = 0 }, % default lock spec
|
|
rl_out__add_indexes_to_rel(does_not_have_index,
|
|
Output, Indexes, IndexInstrs),
|
|
rl_out_info_get_next_materialise_id(Id),
|
|
{ Code =
|
|
tree(node([
|
|
rl_PROC_createtemprel(TmpVar, SchemaOffset)
|
|
]),
|
|
tree(IndexInstrs,
|
|
tree(node([
|
|
rl_PROC_materialise(Id)
|
|
]),
|
|
tree(Stream,
|
|
tree(node([
|
|
rl_PROC_var_list_cons(TmpVar, LockSpec),
|
|
rl_PROC_var_list_nil,
|
|
|
|
% This unsetrel must come after the code
|
|
% to materialise the stream, since the stream
|
|
% may depend on the variable.
|
|
rl_PROC_unsetrel(OutputAddr),
|
|
rl_PROC_setrel(OutputAddr, TmpVar)
|
|
]),
|
|
TmpClearCode
|
|
))))) }
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred rl_out__generate_stream_list(list(relation_id)::in, byte_tree::out,
|
|
rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out__generate_stream_list(Rels, Code) -->
|
|
rl_out__generate_stream_list_2(Rels, Code1),
|
|
{ Code =
|
|
tree(node([rl_PROC_stream_list_cons]),
|
|
tree(Code1,
|
|
node([rl_PROC_stream_list_nil])
|
|
)) }.
|
|
|
|
:- pred rl_out__generate_stream_list_2(list(relation_id)::in, byte_tree::out,
|
|
rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out__generate_stream_list_2([], empty) --> [].
|
|
rl_out__generate_stream_list_2([Rel | Rels], Code) -->
|
|
rl_out__generate_stream(Rel, Code1),
|
|
rl_out__generate_stream_list_2(Rels, Code2),
|
|
{ Code = tree(Code1, Code2) }.
|
|
|
|
:- pred rl_out__generate_stream(relation_id::in, byte_tree::out,
|
|
rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out__generate_stream(Rel, StreamCode) -->
|
|
rl_out_info_get_relation_addr(Rel, Addr),
|
|
{ LockSpec = 0 }, % get default lock spec
|
|
{ StreamCode = node([
|
|
rl_PROC_stream,
|
|
rl_PROC_var(Addr, LockSpec),
|
|
rl_PROC_stream_end
|
|
]) }.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Generate a binary tree of unions.
|
|
:- pred rl_out__generate_union(bytecode::in, int::in,
|
|
list(relation_id)::in, byte_tree::out,
|
|
rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out__generate_union(UnionCode, Exprn, Inputs, InstrCode) -->
|
|
( { Inputs = [] } ->
|
|
{ error("rl_out__generate_union: no inputs") }
|
|
; { Inputs = [Input] } ->
|
|
rl_out_info_get_relation_addr(Input, Addr),
|
|
{ LockSpec = 0 }, % get default lock spec
|
|
{ InstrCode = node([rl_PROC_var(Addr, LockSpec)]) }
|
|
;
|
|
{ list__length(Inputs, NumInputs) },
|
|
{ SplitPoint is NumInputs // 2 },
|
|
( { list__split_list(SplitPoint, Inputs, Inputs1, Inputs2) } ->
|
|
rl_out__generate_union(UnionCode, Exprn,
|
|
Inputs1, StreamCode1),
|
|
rl_out__generate_union(UnionCode, Exprn,
|
|
Inputs2, StreamCode2)
|
|
;
|
|
{ error("rl_out__generate_union: list__split_list failed") }
|
|
),
|
|
{ InstrCode =
|
|
tree(node([UnionCode, rl_PROC_stream]),
|
|
tree(StreamCode1,
|
|
tree(node([rl_PROC_stream_end, rl_PROC_stream]),
|
|
tree(StreamCode2,
|
|
node([rl_PROC_stream_end, rl_PROC_expr(Exprn)])
|
|
)))) }
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred rl_out__generate_arg_list(list(int)::in, byte_tree::out) is det.
|
|
|
|
rl_out__generate_arg_list(List, Code) :-
|
|
ConsElem = lambda([Elem::in, ArgCode::out] is det, (
|
|
LockSpec = 0, % default lock spec.
|
|
ArgCode = rl_PROC_var(Elem, LockSpec)
|
|
)),
|
|
list__map(ConsElem, List, Codes),
|
|
Code = node(Codes).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred rl_out__generate_int_list(list(int)::in, byte_tree::out) is det.
|
|
|
|
rl_out__generate_int_list(List, Code) :-
|
|
ConsElem = lambda([Elem::in, Cons::out] is det, (
|
|
Cons = rl_PROC_int_list_cons(Elem)
|
|
)),
|
|
list__map(ConsElem, List, Codes0),
|
|
list__append(Codes0, [rl_PROC_int_list_nil], Codes),
|
|
Code = node(Codes).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred rl_out__resolve_proc_addresses(byte_tree::in, byte_tree::out,
|
|
rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out__resolve_proc_addresses(ByteTree0, ByteTree) -->
|
|
rl_out_info_get_labels(Labels),
|
|
{ ResolveAddr =
|
|
lambda([Code0::in, Code::out] is det, (
|
|
%
|
|
% The actual code addresses of rl_PROC_goto_label
|
|
% are resolved at runtime, we possibly could resolve
|
|
% them here and use rl_PROC_goto instead.
|
|
%
|
|
( Code0 = rl_PROC_goto_label(Label0) ->
|
|
maybe_lookup(Labels, Label0, Label),
|
|
Code = rl_PROC_goto_label(Label)
|
|
; Code0 = rl_PROC_conditional_goto_label(Label0) ->
|
|
maybe_lookup(Labels, Label0, Label),
|
|
Code = rl_PROC_conditional_goto_label(Label)
|
|
%
|
|
% rl_PROC_goto and rl_PROC_conditional_goto are
|
|
% used by Aditi for resolved label addresses. We
|
|
% use them here for labels which don't need renaming.
|
|
%
|
|
; Code0 = rl_PROC_goto(Label0) ->
|
|
Code = rl_PROC_goto_label(Label0)
|
|
; Code0 = rl_PROC_conditional_goto(Label0) ->
|
|
Code = rl_PROC_conditional_goto_label(Label0)
|
|
;
|
|
Code = Code0
|
|
)
|
|
)) },
|
|
{ rl_out__resolve_addresses(ResolveAddr, ByteTree0, ByteTree) }.
|
|
|
|
% Labels introduced as optimizations in rl_out.m don't
|
|
% need to be resolved.
|
|
:- pred maybe_lookup(map(K, K)::in, K::in, K::out) is det.
|
|
|
|
maybe_lookup(Map, K0, K) :-
|
|
( map__search(Map, K0, K1) ->
|
|
K = K1
|
|
;
|
|
K = K0
|
|
).
|
|
|
|
rl_out__resolve_addresses(_, empty, empty).
|
|
rl_out__resolve_addresses(ResolveAddr, node(InstrList0), node(InstrList)) :-
|
|
list__map(ResolveAddr, InstrList0, InstrList).
|
|
rl_out__resolve_addresses(ResolveAddr, tree(CodeA0, CodeB0),
|
|
tree(CodeA, CodeB)) :-
|
|
rl_out__resolve_addresses(ResolveAddr, CodeA0, CodeA),
|
|
rl_out__resolve_addresses(ResolveAddr, CodeB0, CodeB).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred rl_out__instr_code_size(byte_tree::in, int::out) is det.
|
|
|
|
rl_out__instr_code_size(empty, 0).
|
|
rl_out__instr_code_size(node(Instrs), Size) :-
|
|
AddSize = lambda([Instr::in, S0::in, S::out] is det, (
|
|
bytecode_to_intlist(Instr, IntList),
|
|
list__length(IntList, S1),
|
|
S is S0 + S1
|
|
)),
|
|
list__foldl(AddSize, Instrs, 0, Size).
|
|
rl_out__instr_code_size(tree(CodeA, CodeB), Size) :-
|
|
rl_out__instr_code_size(CodeA, SizeA),
|
|
rl_out__instr_code_size(CodeB, SizeB),
|
|
Size is SizeA + SizeB.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Generate a general join/project condition.
|
|
:- pred rl_out__generate_exprn(rl_goal::in, int::in, int::out,
|
|
rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out__generate_exprn(RLGoal, OutputSchemaOffset, ExprnNum) -->
|
|
|
|
rl_out_info_get_module_info(ModuleInfo),
|
|
{ rl_exprn__generate(ModuleInfo, RLGoal, ExprnCode,
|
|
NumParams, ExprnMode, Decls) },
|
|
|
|
rl_out__schema_to_string([], EmptySchemaOffset),
|
|
% Nothing is built on the stack, so this will be enough.
|
|
{ StackSize = 10 },
|
|
rl_out__package_exprn(ExprnCode, NumParams, ExprnMode,
|
|
OutputSchemaOffset, EmptySchemaOffset, StackSize,
|
|
Decls, ExprnNum).
|
|
|
|
:- pred rl_out__generate_aggregate_exprn(pred_proc_id::in,
|
|
pred_proc_id::in, relation_id::in, output_rel::in,
|
|
int::out, rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out__generate_aggregate_exprn(ComputeInitial, UpdateAcc,
|
|
Input, Output, ExprnNum) -->
|
|
rl_out_info_get_relation_schema(Input, InputSchema),
|
|
rl_out_info_get_output_relation_schema(Output, OutputSchema),
|
|
rl_out__schema_to_string(OutputSchema, OutputSchemaOffset),
|
|
(
|
|
{ InputSchema = [GrpByType, NonGrpByType] },
|
|
{ OutputSchema = [_, AccType] }
|
|
->
|
|
rl_out_info_get_module_info(ModuleInfo),
|
|
{ rl_exprn__aggregate(ModuleInfo, ComputeInitial, UpdateAcc,
|
|
GrpByType, NonGrpByType, AccType, AggCode, Decls) },
|
|
rl_out__schema_to_string([], EmptySchemaOffset),
|
|
|
|
% Nothing is built on the stack, so this will be enough.
|
|
{ StackSize = 10 },
|
|
rl_out__package_exprn(AggCode, 2, generate, OutputSchemaOffset,
|
|
EmptySchemaOffset, StackSize, Decls, ExprnNum)
|
|
;
|
|
{ error("rl_out__generate_aggregate_exprn: invalid relation schemas") }
|
|
).
|
|
|
|
% Generate an expression to compare tuples with the
|
|
% given schema on the given attributes.
|
|
:- pred rl_out__generate_compare_exprn(sort_spec::in, list(type)::in,
|
|
int::out, rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out__generate_compare_exprn(Spec, Schema, ExprnNum) -->
|
|
rl_out_info_get_compare_exprns(CompareExprns0),
|
|
rl_out__schema_to_string(Schema, InputSchemaOffset),
|
|
(
|
|
{ map__search(CompareExprns0, Spec - InputSchemaOffset,
|
|
ExprnNum0) }
|
|
->
|
|
{ ExprnNum = ExprnNum0 }
|
|
;
|
|
rl_out_info_get_module_info(ModuleInfo),
|
|
{ rl_exprn__generate_compare_exprn(ModuleInfo, Spec,
|
|
Schema, Instrs) },
|
|
|
|
% Comparison expressions don't use any variables
|
|
% or create an output tuple.
|
|
rl_out__schema_to_string([], EmptySchemaOffset),
|
|
|
|
% Nothing is built on the stack, so this will be enough.
|
|
{ StackSize = 10 },
|
|
{ Decls = [] },
|
|
rl_out__package_exprn(Instrs, 2, test, EmptySchemaOffset,
|
|
EmptySchemaOffset, StackSize, Decls, ExprnNum),
|
|
|
|
{ map__det_insert(CompareExprns0, Spec - InputSchemaOffset,
|
|
ExprnNum, CompareExprns) },
|
|
rl_out_info_set_compare_exprns(CompareExprns)
|
|
).
|
|
|
|
:- pred rl_out__generate_key_range(key_range::in, int::out,
|
|
rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out__generate_key_range(Range, RangeExprn) -->
|
|
rl_out_info_get_module_info(ModuleInfo),
|
|
{ rl_exprn__generate_key_range(ModuleInfo, Range, ExprnCode,
|
|
NumParams, Output1Schema, Output2Schema, TermDepth, Decls) },
|
|
rl_out__schema_to_string(Output1Schema, Output1SchemaOffset),
|
|
rl_out__schema_to_string(Output2Schema, Output2SchemaOffset),
|
|
|
|
% Terms take 2 slots in the stack, so to be safe we
|
|
% multiply the depth by 2. The +10 is for temporary storage
|
|
% and probably isn't used.
|
|
{ StackSize is TermDepth * 2 + 10 },
|
|
rl_out__package_exprn(ExprnCode, NumParams, generate2,
|
|
Output1SchemaOffset, Output2SchemaOffset, StackSize,
|
|
Decls, RangeExprn).
|
|
|
|
:- pred rl_out__package_exprn(list(bytecode)::in, int::in, exprn_mode::in,
|
|
int::in, int::in, int::in, list(type)::in, int::out,
|
|
rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out__package_exprn(ExprnCode, NumParams, ExprnMode, OutputSchemaOffset,
|
|
Schema2Offset, StackSize, Decls, ExprnNum) -->
|
|
rl_out__schema_to_string(Decls, VarSchemaOffset),
|
|
|
|
% Note that this field is for the benefit of ROSI which counts
|
|
% everything in bytes, so we don't use rl_out__exprn_code_size.
|
|
{ rl_out__instr_code_size(node(ExprnCode), CodeSize) },
|
|
|
|
{ Exprnession = expression(OutputSchemaOffset, Schema2Offset,
|
|
VarSchemaOffset, StackSize, NumParams, ExprnMode,
|
|
CodeSize, ExprnCode) },
|
|
|
|
rl_out_info_add_expression(Exprnession, ExprnNum).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- type rl_out_info
|
|
---> rl_out_info(
|
|
int, % PC
|
|
compare_exprns,
|
|
map(relation_id, int), % relation vars
|
|
int, % next relation address
|
|
map(relation_id, relation_info),
|
|
map(label_id, int), % proc label offsets
|
|
unit,
|
|
module_info,
|
|
int, % expression PC
|
|
map(rl_const, int), % procedure consts
|
|
int, % next proc const address
|
|
int, % next materialise number -
|
|
% used for debugging the
|
|
% generated code.
|
|
unit,
|
|
unit,
|
|
unit,
|
|
int, % next proc label.
|
|
list(procedure), % procedure bytecodes
|
|
% in reverse order.
|
|
unit,
|
|
unit,
|
|
unit,
|
|
set(relation), % permanent relations.
|
|
list(variable), % variables used in
|
|
% reverse order.
|
|
list(expression), % expressions for the current
|
|
% procedure in reverse order.
|
|
int, % next expression.
|
|
multi_map(int, int) % temporary relation variables:
|
|
% map from schema constant
|
|
% to list of variables.
|
|
% These must only be used
|
|
% within one rl.m instruction.
|
|
).
|
|
|
|
% We only want to generate a single comparison expression for
|
|
% each combination of attributes and types.
|
|
% Key:
|
|
% The int gives the offset of the schema of the input relation
|
|
% in the constant table.
|
|
% Value:
|
|
% The number of the expression.
|
|
:- type compare_exprns == map(pair(sort_spec, int), int).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred rl_out_info_init(module_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out_info_init(ModuleInfo, Info0) :-
|
|
map__init(CompareExprns),
|
|
map__init(Relations),
|
|
map__init(RelationAddrs),
|
|
map__init(Consts),
|
|
map__init(Labels),
|
|
set__init(PermRels),
|
|
map__init(TmpVars),
|
|
PC = 0,
|
|
FirstRelAddr = 0,
|
|
FirstConst = 1,
|
|
FirstMaterialise = 1,
|
|
Label = 0,
|
|
NextExprn = 0,
|
|
Info0 = rl_out_info(PC, CompareExprns, RelationAddrs, FirstRelAddr,
|
|
Relations, Labels, unit, ModuleInfo, PC, Consts,
|
|
FirstConst, FirstMaterialise, unit, unit, unit, Label,
|
|
[], unit, unit, unit, PermRels, [], [],
|
|
NextExprn, TmpVars).
|
|
|
|
:- pred rl_out_info_init_proc(map(relation_id, relation_info)::in,
|
|
list(relation_id)::in, rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out_info_init_proc(Relations, _Args, Info0, Info) :-
|
|
map__init(Labels),
|
|
map__init(RelationAddrs),
|
|
map__init(CompareExprns),
|
|
PC = 0,
|
|
Label = 0,
|
|
NextExprn = 0,
|
|
map__init(TmpVars),
|
|
Info0 = rl_out_info(_, _, _, NextAddr, _, _, _,
|
|
ModuleInfo, _, ProcConsts, NextConst, Materialise, _, _,
|
|
_, _, Procs, _, _, _, PermRelations, Variables, _, _, _),
|
|
Info = rl_out_info(PC, CompareExprns, RelationAddrs, NextAddr,
|
|
Relations, Labels, unit, ModuleInfo, PC, ProcConsts,
|
|
NextConst, Materialise, unit, unit, unit, Label, Procs,
|
|
unit, unit, unit, PermRelations, Variables, [],
|
|
NextExprn, TmpVars).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred rl_out_info_get_compare_exprns(compare_exprns::out,
|
|
rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out_info_get_compare_exprns(Exprns, Info, Info) :-
|
|
Info = rl_out_info(_,Exprns,_,_,_,_,_,_,_,_,_,_,_,_,_,_,
|
|
_,_,_,_,_,_,_,_,_).
|
|
|
|
:- pred rl_out_info_set_compare_exprns(compare_exprns::in,
|
|
rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out_info_set_compare_exprns(Exprns, Info0, Info) :-
|
|
Info0 = rl_out_info(A,_,C,D,E,F,G,H,I,J,K,L,M,
|
|
N,O,P,Q,R,S,T,U,V,W,X,Y),
|
|
Info = rl_out_info(A,Exprns,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,
|
|
V,W,X,Y).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred rl_out_info_get_relation_addr(relation_id::in, int::out,
|
|
rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out_info_get_relation_addr(RelationId, Addr) -->
|
|
rl_out_info_get_relation_addrs(Addrs0),
|
|
( { map__search(Addrs0, RelationId, Addr0) } ->
|
|
{ Addr = Addr0 }
|
|
;
|
|
rl_out_info_get_relation_schema_offset(RelationId,
|
|
SchemaAddr),
|
|
rl_out_info_add_relation_variable(SchemaAddr, Addr),
|
|
{ map__det_insert(Addrs0, RelationId, Addr, Addrs) },
|
|
rl_out_info_set_relation_addrs(Addrs)
|
|
).
|
|
|
|
:- pred rl_out_info_get_relation_addrs(map(relation_id, int)::out,
|
|
rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out_info_get_relation_addrs(Addrs, Info, Info) :-
|
|
Info = rl_out_info(_,_,Addrs,_,_,_,_,_,_,_,_,_,_,_,_,_,
|
|
_,_,_,_,_,_,_,_,_).
|
|
|
|
:- pred rl_out_info_set_relation_addrs(map(relation_id, int)::in,
|
|
rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out_info_set_relation_addrs(Addrs, Info0, Info) :-
|
|
Info0 = rl_out_info(A,B,_,D,E,F,G,H,I,J,K,L,M,
|
|
N,O,P,Q,R,S,T,U,V,W,X,Y),
|
|
Info = rl_out_info(A,B,Addrs,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,
|
|
V,W,X,Y).
|
|
|
|
:- pred rl_out_info_add_relation_variable(int::in, int::out,
|
|
rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out_info_add_relation_variable(Schema, Addr) -->
|
|
rl_out_info_get_next_relation_addr(Addr),
|
|
{ string__int_to_string(Addr, AddrStr) },
|
|
{ string__append("v_", AddrStr, VarName) },
|
|
rl_out_info_assign_const(string(VarName), VarNameConst),
|
|
rl_out_info_add_relation_variable_2(VarNameConst, Schema).
|
|
|
|
:- pred rl_out_info_add_relation_variable_2(int::in, int::in,
|
|
rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out_info_add_relation_variable_2(Name, Schema, Info0, Info) :-
|
|
Info0 = rl_out_info(A,B,C,D,E,F,G,H,I,J,K,L,M,
|
|
N,O,P,Q,R,S,T,U,Vars0,W,X,Y),
|
|
Info = rl_out_info(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,
|
|
[variable(Name, Schema) | Vars0], W,X,Y).
|
|
|
|
:- pred rl_out_info_get_next_relation_addr(int::out,
|
|
rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out_info_get_next_relation_addr(NextAddr0, Info0, Info) :-
|
|
Info0 = rl_out_info(A,B,C,NextAddr0,E,F,G,H,I,J,K,L,M,
|
|
N,O,P,Q,R,S,T,U,V,W,X,Y),
|
|
NextAddr is NextAddr0 + 1,
|
|
Info = rl_out_info(A,B,C,NextAddr,E,F,G,H,
|
|
I,J,K,L,M, N,O,P,Q,R,S,T,U,V,W,X,Y).
|
|
|
|
:- pred rl_out_info_get_relation_variables(list(variable)::out,
|
|
rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out_info_get_relation_variables(Vars, Info, Info) :-
|
|
Info = rl_out_info(_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,
|
|
Vars0,_,_,_),
|
|
list__reverse(Vars0, Vars).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred rl_out_info_add_label(label_id::in, int::out,
|
|
rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out_info_add_label(LabelId, NextLabel, Info0, Info) :-
|
|
Info0 = rl_out_info(A,B,C,D,E,Labels0,G,H,I,J,K,L,M,N,O,NextLabel,
|
|
Q,R,S,T,U,V,W,X,Y),
|
|
map__det_insert(Labels0, LabelId, NextLabel, Labels),
|
|
NextLabel1 is NextLabel + 1,
|
|
Info = rl_out_info(A,B,C,D,E,Labels,G,H,I,J,K,L,M,N,O,NextLabel1,
|
|
Q,R,S,T,U,V,W,X,Y).
|
|
|
|
:- pred rl_out_info_add_label(int::out,
|
|
rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out_info_add_label(NextLabel, Info0, Info) :-
|
|
Info0 = rl_out_info(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,NextLabel,
|
|
Q,R,S,T,U,V,W,X,Y),
|
|
NextLabel1 is NextLabel + 1,
|
|
Info = rl_out_info(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,NextLabel1,
|
|
Q,R,S,T,U,V,W,X,Y).
|
|
|
|
:- pred rl_out_info_get_labels(map(label_id, int)::out,
|
|
rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out_info_get_labels(Labels, Info, Info) :-
|
|
Info = rl_out_info(_,_,_,_,_,Labels,_,_,_,_,_,_,_,_,_,_,_,_,
|
|
_,_,_,_,_,_,_).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred rl_out_info_get_module_info(module_info::out, rl_out_info::in,
|
|
rl_out_info::out) is det.
|
|
|
|
rl_out_info_get_module_info(ModuleInfo, Info, Info) :-
|
|
Info = rl_out_info(_,_,_,_,_,_,_,ModuleInfo,
|
|
_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred rl_out_info_assign_const(rl_const::in, int::out,
|
|
rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out_info_assign_const(Const, ConstOffset, Info0, Info) :-
|
|
Info0 = rl_out_info(A,B,C,D,E,F,G,H,I,Consts0,NextAddr0,
|
|
L,M,N,O,P,Q,R,S,T,U,V,W,X,Y),
|
|
( map__search(Consts0, Const, Addr1) ->
|
|
ConstOffset = Addr1,
|
|
NextAddr = NextAddr0,
|
|
Consts = Consts0
|
|
;
|
|
map__det_insert(Consts0, Const, NextAddr0, Consts),
|
|
ConstOffset = NextAddr0,
|
|
NextAddr is NextAddr0 + 1
|
|
),
|
|
Info = rl_out_info(A,B,C,D,E,F,G,H,I,Consts,NextAddr,
|
|
L,M,N,O,P,Q,R,S,T,U,V,W,X,Y).
|
|
|
|
:- pred rl_out_info_get_consts(map(rl_const, int)::out,
|
|
rl_out_info::in,
|
|
rl_out_info::out) is det.
|
|
|
|
rl_out_info_get_consts(Consts, Info, Info) :-
|
|
Info = rl_out_info(_,_,_,_,_,_,_,_,_,Consts,
|
|
_,_,_,_,_,_,_,_,_,_,_,_,_,_,_).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred rl_out_info_get_next_materialise_id(int::out,
|
|
rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out_info_get_next_materialise_id(MaterialiseId, Info0, Info) :-
|
|
Info0 = rl_out_info(A,B,C,D,E,F,G,H,I,J,K,
|
|
MaterialiseId, M,N,O,P,Q,R,S,T,U,V,W,X,Y),
|
|
Info = rl_out_info(A,B,C,D,E,F,G,H,I,J,K,
|
|
MaterialiseId + 1, M,N,O,P,Q,R,S,T,U,V,W,X,Y).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred rl_out_info_get_relations(map(relation_id, relation_info)::out,
|
|
rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out_info_get_relations(Relations, Info, Info) :-
|
|
Info = rl_out_info(_,_,_,_,Relations,
|
|
_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred rl_out_info_get_output_relation_schema(output_rel::in, list(type)::out,
|
|
rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out_info_get_output_relation_schema(output_rel(RelId, _), Schema) -->
|
|
rl_out_info_get_relation_schema(RelId, Schema).
|
|
|
|
:- pred rl_out_info_get_relation_schema(relation_id::in, list(type)::out,
|
|
rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out_info_get_relation_schema(RelId, Schema, Info0, Info) :-
|
|
rl_out_info_get_relations(Relations, Info0, Info),
|
|
map__lookup(Relations, RelId, RelInfo),
|
|
RelInfo = relation_info(_, Schema, _, _).
|
|
|
|
:- pred rl_out_info_get_relation_indexes(relation_id::in,
|
|
list(index_spec)::out, rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out_info_get_relation_indexes(RelId, Indexes, Info0, Info) :-
|
|
rl_out_info_get_relations(Relations, Info0, Info),
|
|
map__lookup(Relations, RelId, RelInfo),
|
|
RelInfo = relation_info(_, _, Indexes, _).
|
|
|
|
:- pred rl_out_info_get_relation_type(relation_id::in,
|
|
relation_type::out, rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out_info_get_relation_type(RelId, Type, Info0, Info) :-
|
|
rl_out_info_get_relations(Relations, Info0, Info),
|
|
map__lookup(Relations, RelId, RelInfo),
|
|
RelInfo = relation_info(Type, _, _, _).
|
|
|
|
:- pred rl_out_info_get_output_relation_schema_offset(output_rel::in, int::out,
|
|
rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out_info_get_output_relation_schema_offset(output_rel(RelId, _),
|
|
SchemaOffset) -->
|
|
rl_out_info_get_relation_schema_offset(RelId, SchemaOffset).
|
|
|
|
:- pred rl_out_info_get_relation_schema_offset(relation_id::in, int::out,
|
|
rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out_info_get_relation_schema_offset(RelId, SchemaOffset) -->
|
|
rl_out_info_get_relation_schema(RelId, Schema),
|
|
rl_out__schema_to_string(Schema, SchemaOffset).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred rl_out_info_incr_pc(int::in, rl_out_info::in,
|
|
rl_out_info::out) is det.
|
|
|
|
rl_out_info_incr_pc(Incr, Info0, Info) :-
|
|
Info0 = rl_out_info(PC0,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,
|
|
S,T,U,V,W,X,Y),
|
|
PC = PC0 + Incr,
|
|
Info = rl_out_info(PC,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,
|
|
T,U,V,W,X,Y).
|
|
|
|
:- pred rl_out_info_get_pc(int::out, rl_out_info::in,
|
|
rl_out_info::out) is det.
|
|
|
|
rl_out_info_get_pc(PC0, Info, Info) :-
|
|
Info = rl_out_info(PC0,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,
|
|
_,_,_,_,_,_,_).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred rl_out_info_add_proc(procedure::in,
|
|
rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out_info_add_proc(Proc, Info0, Info) :-
|
|
Info0 = rl_out_info(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Procs0,
|
|
R,S,T,U,V,W,X,Y),
|
|
Info = rl_out_info(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,[Proc | Procs0],
|
|
R,S,T,U,V,W,X,Y).
|
|
|
|
:- pred rl_out_info_get_procs(list(procedure)::out,
|
|
rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out_info_get_procs(Procs, Info, Info) :-
|
|
Info = rl_out_info(_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,Procs0,
|
|
_,_,_,_,_,_,_,_),
|
|
list__reverse(Procs0, Procs).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred rl_out_info_get_permanent_relations(set(relation)::out,
|
|
rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out_info_get_permanent_relations(Rels, Info, Info) :-
|
|
Info = rl_out_info(_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,
|
|
Rels,_,_,_,_).
|
|
|
|
:- pred rl_out_info_set_permanent_relations(set(relation)::in,
|
|
rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out_info_set_permanent_relations(Rels, Info0, Info) :-
|
|
Info0 = rl_out_info(A,B,C,D,E,F,G,H,I,J,K,L,M,
|
|
N,O,P,Q,R,S,T,_,V,W,X,Y),
|
|
Info = rl_out_info(A,B,C,D,E,F,G,H,I,J,K,L,M,
|
|
N,O,P,Q,R,S,T,Rels, V,W,X,Y).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred rl_out_info_get_proc_expressions(list(expression)::out,
|
|
rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out_info_get_proc_expressions(Exprns, Info, Info) :-
|
|
Info = rl_out_info(_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,
|
|
_,_,Exprns0,_,_),
|
|
list__reverse(Exprns0, Exprns).
|
|
|
|
:- pred rl_out_info_add_expression(expression::in, int::out,
|
|
rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out_info_add_expression(Exprn, NextExprn0, Info0, Info) :-
|
|
Info0 = rl_out_info(A,B,C,D,E,F,G,H,I,J,K,L,M,
|
|
N,O,P,Q,R,S,T,U,V,Exprns0,NextExprn0,Y),
|
|
NextExprn is NextExprn0 + 1,
|
|
Info = rl_out_info(A,B,C,D,E,F,G,H,I,J,K,L,M,
|
|
N,O,P,Q,R,S,T,U,V,[Exprn | Exprns0], NextExprn,Y).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred rl_out_info_get_tmp_var(int::in, int::out,
|
|
rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out_info_get_tmp_var(Schema, Var) -->
|
|
rl_out_info_get_tmp_vars(TmpVars0),
|
|
( { multi_map__search(TmpVars0, Schema, [Var0 | Vars]) } ->
|
|
{ Var = Var0 },
|
|
{ multi_map__det_replace(TmpVars0, Schema, Vars, TmpVars) },
|
|
rl_out_info_set_tmp_vars(TmpVars)
|
|
;
|
|
rl_out_info_add_relation_variable(Schema, Var)
|
|
).
|
|
|
|
:- pred rl_out_info_return_tmp_var(int::in, int::in, byte_tree::out,
|
|
rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out_info_return_tmp_var(Schema, Var, TmpClearCode) -->
|
|
rl_out_info_get_tmp_vars(TmpVars0),
|
|
( { multi_map__search(TmpVars0, Schema, Vars) } ->
|
|
{ multi_map__det_replace(TmpVars0, Schema,
|
|
[Var | Vars], TmpVars) }
|
|
;
|
|
{ multi_map__det_insert(TmpVars0, Schema, Var, TmpVars) }
|
|
),
|
|
{ TmpClearCode = node([rl_PROC_unsetrel(Var)]) },
|
|
rl_out_info_set_tmp_vars(TmpVars).
|
|
|
|
:- pred rl_out_info_return_tmp_vars(assoc_list(int, int)::in, byte_tree::out,
|
|
rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out_info_return_tmp_vars([], empty) --> [].
|
|
rl_out_info_return_tmp_vars([Schema - Var | Vars], tree(Clear0, Clear1)) -->
|
|
rl_out_info_return_tmp_var(Schema, Var, Clear0),
|
|
rl_out_info_return_tmp_vars(Vars, Clear1).
|
|
|
|
:- pred rl_out_info_get_tmp_vars(multi_map(int, int)::out,
|
|
rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out_info_get_tmp_vars(TmpVars, Info, Info) :-
|
|
Info = rl_out_info(_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,
|
|
_,_,_,_, TmpVars).
|
|
|
|
:- pred rl_out_info_set_tmp_vars(multi_map(int, int)::in,
|
|
rl_out_info::in, rl_out_info::out) is det.
|
|
|
|
rl_out_info_set_tmp_vars(TmpVars, Info0, Info) :-
|
|
Info0 = rl_out_info(A,B,C,D,E,F,G,H,I,J,K,L,M,
|
|
N,O,P,Q,R,S,T,U,V,W,X,_),
|
|
Info = rl_out_info(A,B,C,D,E,F,G,H,I,J,K,L,M,
|
|
N,O,P,Q,R,S,T,U,V,W,X,TmpVars).
|
|
|
|
#else % !INCLUDE_ADITI_OUTPUT
|
|
#endif
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|