%-----------------------------------------------------------------------------% % Copyright (C) 1998-2001, 2003 The University of Melbourne. % This file may only be copied under the terms of the GNU General % Public License - see the file COPYING in the Mercury distribution. %-----------------------------------------------------------------------------% % 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 aditi_backend__rl_out. :- interface. :- import_module aditi_backend__rl. :- import_module aditi_backend__rl_file. :- import_module hlds__hlds_module. #if INCLUDE_ADITI_OUTPUT % See ../Mmake.common.in. :- import_module aditi_backend__rl_code. :- import_module libs__tree. #else #endif :- import_module list, io, std_util. % Output schemas for locally defined base and derived relations to % .base_schema and .derived_schema respectively. :- pred rl_out__generate_schema_file(module_info::in, io__state::di, io__state::uo) is det. % Output bytecode to `.rlo' if --aditi-only was set and a text % representation to `.rla' if --dump-rl-bytecode was specified. % Output schema information for derived relations to % `.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 aditi_backend__magic_util. :- import_module aditi_backend__rl_file. :- import_module check_hlds__det_analysis. :- import_module check_hlds__type_util. :- import_module hlds__hlds_data. :- import_module hlds__hlds_goal. :- import_module hlds__hlds_pred. :- import_module hlds__instmap. :- import_module hlds__passes_aux. :- import_module libs__globals. :- import_module libs__options. :- import_module libs__tree. :- import_module parse_tree__modules. :- import_module parse_tree__prog_data. :- import_module parse_tree__prog_out. :- import_module parse_tree__prog_util. #if INCLUDE_ADITI_OUTPUT % See ../Mmake.common.in. :- import_module aditi_backend__rl_exprn. #else #endif :- import_module getopt. :- import_module assoc_list, bool, char, int, map, multi_map, require, set. :- import_module string, term, libs__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) }, { PredModule = pred_info_module(PredInfo) }, ( { 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 `.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) }, { PredModule0 = pred_info_module(PredInfo) }, { prog_out__sym_name_to_string(PredModule0, PredModule) }, { PredName = pred_info_name(PredInfo) }, { PredArity0 = pred_info_arity(PredInfo) }, { 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_out__get_proc_schema(ModuleInfo, ArgSchemas, SchemaString). :- pred rl_out__get_proc_schema(module_info::in, list(list(type))::in, string::out) is det. rl_out__get_proc_schema(ModuleInfo, ArgSchemas, SchemaString) :- 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, ArgSchemaDeclList, "", 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) }, { module_info_predids(ModuleInfo, PredIds) }, { list__foldl(rl_out__generate_update_procs, PredIds, RLInfo1, RLInfo2) }, globals__io_lookup_string_option(aditi_user, Owner), { rl_out_info_assign_const(string(Owner), OwnerIndex, RLInfo2, RLInfo3) }, { 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, RLInfo3, RLInfo4) }, { rl_out_info_assign_const(string(IntFileName), IntIndex, RLInfo4, RLInfo5) }, { rl_out_info_assign_const(string(SourceFileName), SourceIndex, RLInfo5, RLInfo6) }, { rl_out_info_get_procs(RLProcs, RLInfo6, RLInfo7) }, { rl_out_info_get_consts(Consts, RLInfo7, RLInfo8) }, { rl_out_info_get_permanent_relations(PermRelsSet, RLInfo8, RLInfo9) }, { rl_out_info_get_relation_variables(RelVars, RLInfo9, _) }, { map__to_assoc_list(Consts, ConstsAL) }, { assoc_list__reverse_members(ConstsAL, ConstsLA0) }, { list__sort(ConstsLA0, ConstsLA) }, { list__length(ConstsLA, ConstTableSize0) }, { ConstTableSize = 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 `.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 `.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 % `.derived_schema'. % globals__io_lookup_bool_option(generate_schemas, GenSchemas), ( { GenSchemas = yes } -> module_name_to_file_name(ModuleName0, ".derived_schema", yes, 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 % For each base relation defined in this module, generate % a procedure to be used by aditi_bulk_modify to update % the relation. % % In the procedure below, UpdateRel is the relation returned % by the closure passed to aditi_bulk_modify. Each tuple returned % by that closure contains two sets of arguments -- the tuple % to delete, and the tuple to insert. % % DummyOutput is not actually used -- it is there just so % that the procedure matches the usual convention for calling % Aditi procedures from Mercury. % % ModifyProcFor__p_3(UpdateRel, DummyOutput) % { % delete(p/3, project(UpdateRel, FirstTuple); % insert(p/3, project(UpdateRel, SecondTuple), % init(DummyOutput). % } :- pred rl_out__generate_update_procs(pred_id::in, rl_out_info::in, rl_out_info::out) is det. rl_out__generate_update_procs(PredId) --> rl_out_info_get_module_info(ModuleInfo), { module_info_pred_info(ModuleInfo, PredId, PredInfo) }, { module_info_name(ModuleInfo, ModuleName) }, { PredModule = pred_info_module(PredInfo) }, ( { ModuleName = PredModule }, { hlds_pred__pred_info_is_base_relation(PredInfo) } -> rl_out__generate_update_proc(insert, PredId, PredInfo), rl_out__generate_update_proc(delete, PredId, PredInfo), rl_out__generate_update_proc(modify, PredId, PredInfo) ; [] ). :- type update_type ---> insert ; delete ; modify . :- pred rl_out__generate_update_proc(update_type::in, pred_id::in, pred_info::in, rl_out_info::in, rl_out_info::out) is det. rl_out__generate_update_proc(UpdateType, PredId, PredInfo) --> { map__init(Relations) }, rl_out_info_init_proc(Relations), { pred_info_arg_types(PredInfo, ArgTypes) }, { pred_info_get_indexes(PredInfo, Indexes) }, rl_out__schema_to_string(ArgTypes, PermSchemaOffset), rl_out_info_add_relation_variable(PermSchemaOffset, PermanentAddr), rl_out__collect_permanent_relation(PredId, PermanentAddr, OpenPermanentCode, UnsetPermanentCode), { UpdateType = insert, InputRelTypes = ArgTypes ; UpdateType = delete, InputRelTypes = ArgTypes ; UpdateType = modify, list__append(ArgTypes, ArgTypes, InputRelTypes) }, rl_out__schema_to_string(InputRelTypes, InputRelSchemaOffset), rl_out_info_add_relation_variable(InputRelSchemaOffset, InputRelAddr), rl_out__schema_to_string([], NullSchemaOffset), rl_out_info_add_relation_variable(NullSchemaOffset, DummyOutputAddr), rl_out_info_get_module_info(ModuleInfo), { LockSpec = 0 }, % default lock spec ( { UpdateType = insert }, { rl__get_insert_proc_name(ModuleInfo, PredId, ProcName) }, rl_out_info_get_next_materialise_id(MaterialiseId), { InsertCode = node([ rl_PROC_materialise(MaterialiseId), rl_PROC_stream, rl_PROC_var(InputRelAddr, LockSpec), rl_PROC_stream_end, rl_PROC_var_list_cons(PermanentAddr, LockSpec), rl_PROC_var_list_nil ]) }, { DeleteCode = empty } ; { UpdateType = delete }, { rl__get_delete_proc_name(ModuleInfo, PredId, ProcName) }, { DeleteInputStream = node([ rl_PROC_stream, rl_PROC_var(InputRelAddr, LockSpec), rl_PROC_stream_end ]) }, { InsertCode = empty }, rl_out__generate_delete_code(PermanentAddr, Indexes, ArgTypes, PermSchemaOffset, DeleteInputStream, DeleteCode) ; { UpdateType = modify }, { rl__get_modify_proc_name(ModuleInfo, PredId, ProcName) }, rl_out__generate_modify_project_exprn(ArgTypes, PermSchemaOffset, one, DeleteProjectExpr), rl_out__generate_modify_project_exprn(ArgTypes, PermSchemaOffset, two, InsertProjectExpr), % % Project the input relation onto % the first half of its attributes, % deleting the result from the base % relation. % { DeleteInputStream = node([ rl_PROC_stream, rl_PROC_project_tee, rl_PROC_stream, rl_PROC_var(InputRelAddr, LockSpec), rl_PROC_stream_end, rl_PROC_expr(DeleteProjectExpr), rl_PROC_var_list_nil, rl_PROC_expr_list_nil, rl_PROC_stream_end ]) }, rl_out__generate_delete_code(PermanentAddr, Indexes, ArgTypes, PermSchemaOffset, DeleteInputStream, DeleteCode), rl_out_info_get_next_materialise_id(MaterialiseId), { InsertCode = node([ % % Project the input relation onto % the second half of its attributes, % inserting the result into the base % relation. % rl_PROC_materialise(MaterialiseId), rl_PROC_stream, rl_PROC_project_tee, rl_PROC_stream, rl_PROC_var(InputRelAddr, LockSpec), rl_PROC_stream_end, rl_PROC_expr(InsertProjectExpr), rl_PROC_var_list_nil, rl_PROC_expr_list_nil, rl_PROC_stream_end, rl_PROC_var_list_cons(PermanentAddr, LockSpec), rl_PROC_var_list_nil ]) } ), { Codes = tree( % % Open the permanent relation. % node([OpenPermanentCode]), % % Do the deletion. % tree(DeleteCode, % % Do the insertion for an `aditi_bulk_modify' % or `aditi_bulk_insert' goal. % tree(InsertCode, node([ % % Clean up. % UnsetPermanentCode, rl_PROC_unsetrel(InputRelAddr), % % Create the dummy output variable. % rl_PROC_createtemprel(DummyOutputAddr, NullSchemaOffset), rl_PROC_ret ]) ))) }, { tree__flatten(Codes, CodeList) }, { list__condense(CodeList, Code) }, { ArgSchemas = [InputRelTypes, []] }, { rl_out__get_proc_schema(ModuleInfo, ArgSchemas, ProcSchemaString) }, rl_out_info_assign_const(string(ProcSchemaString), ProcSchemaConst), { Args = [InputRelAddr, DummyOutputAddr] }, rl_out__package_proc(ProcName, Args, Code, ProcSchemaConst). :- pred rl_out__generate_delete_code(int::in, list(index_spec)::in, list(type)::in, int::in, byte_tree::in, byte_tree::out, rl_out_info::in, rl_out_info::out) is det. rl_out__generate_delete_code(PermanentAddr, _Indexes, ArgTypes, SchemaOffset, DeleteInputStream, DeleteCode) --> ( { ArgTypes = [] }, { CondCode = tree(node([rl_PROC_empty]), DeleteInputStream ) }, { ThenCode = empty }, % We use clear here because otherwise the relation manager % may complain about deleting a tuple from an empty relation. { ElseCode = node([rl_PROC_clear(PermanentAddr)]) }, rl_out__generate_ite(CondCode, ThenCode, ElseCode, DeleteCode) ; { ArgTypes = [_ | _] }, % % The tuples to delete must come from the relation to % delete from -- Aditi does the deletion by tuple-id, % not tuple contents. To get the correct tuples, we must % do a sem-join of the tuples to delete against the relation % to delete from. % % Note that the indexed semi-join won't work because it returns % tuples from the non-indexed relation, which are no good for % deleting from the indexed relation. % % XXX For a permanent relation with a unique B-tree index % on all attributes, we may be able to use a sort-merge % semi-join to collect the tuples to delete. % { list__length(ArgTypes, Arity) }, { list__foldl2( (pred(_::in, L0::in, L::out, N0::in, N::out) is det :- L = [N0 | L0], N = N0 - 1 ), ArgTypes, [], Attrs, Arity, _) }, rl_out__do_generate_hash_exprn(ArgTypes, SchemaOffset, Attrs, HashExprn), rl_out__do_generate_equijoin_exprn(ArgTypes, Attrs, JoinCond), { LockSpec = 0 }, % default lock spec { DeleteCode = tree(node([ rl_PROC_delete(PermanentAddr), rl_PROC_stream, rl_PROC_semijoin_hj, rl_PROC_stream, rl_PROC_var(PermanentAddr, LockSpec), rl_PROC_stream_end ]), tree(DeleteInputStream, node([ % Both relations can use the same % hash expression. rl_PROC_expr(HashExprn), rl_PROC_expr(HashExprn), rl_PROC_expr(JoinCond), rl_PROC_stream_end ]) )) } ). :- 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, _) }, { list__append(Inputs, Outputs, Args) }, rl_out_info_init_proc(Relations), 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. { Name = rl_proc_name(Owner, _, _, _) }, 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, [], PermUnsetCodes), rl_out__resolve_proc_addresses(RLInstrCodeTree0, RLInstrCodeTree1), { RLInstrCodeTree = tree(node(PermRelCodes), tree(node(CollectCode), tree(RLInstrCodeTree1, tree(node(NameCode), tree(GroupCode, tree(node(PermUnsetCodes), node([rl_PROC_ret]) )))))) }, { tree__flatten(RLInstrCodeTree, CodeLists) }, { list__condense(CodeLists, Codes) }, list__map_foldl(rl_out_info_get_relation_addr, Args, ArgLocs), rl_out__generate_proc_schema(Args, ProcSchemaConst), rl_out__package_proc(Name, ArgLocs, Codes, ProcSchemaConst). :- pred rl_out__package_proc(rl_proc_name::in, list(int)::in, list(bytecode)::in, int::in, rl_out_info::in, rl_out_info::out) is det. rl_out__package_proc(Name, ArgLocs, Codes, ProcSchemaConst) --> { Name = rl_proc_name(Owner, Module, ProcName, _) }, rl_out_info_get_proc_expressions(Exprns), { list__length(Exprns, NumExprns) }, 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(ArgLocs, NumArgs) }, { RLProc = procedure(OwnerConst, ModuleConst, NameConst, ProcSchemaConst, 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 = (pred(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 = 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, list(bytecode)::in, list(bytecode)::out, rl_out_info::in, rl_out_info::out) is det. rl_out__collect_permanent_relations([], Codes, Codes, UnsetCodes, UnsetCodes) --> []. rl_out__collect_permanent_relations([RelationId - Addr | Rels], Codes0, Codes, UnsetCodes0, UnsetCodes) --> rl_out_info_get_relations(Relations), { map__lookup(Relations, RelationId, RelInfo) }, { RelInfo = relation_info(RelType, _Schema, _Index, _) }, ( { RelType = permanent(proc(PredId, _)) } -> rl_out__collect_permanent_relation(PredId, Addr, SetCode, UnsetCode), { UnsetCodes1 = [UnsetCode | UnsetCodes0] }, { Codes1 = [SetCode | Codes0] } ; { UnsetCodes1 = UnsetCodes0 }, { Codes1 = Codes0 } ), rl_out__collect_permanent_relations(Rels, Codes1, Codes, UnsetCodes1, UnsetCodes). :- pred rl_out__collect_permanent_relation(pred_id::in, int::in, bytecode::out, bytecode::out, rl_out_info::in, rl_out_info::out) is det. rl_out__collect_permanent_relation(PredId, Addr, SetCode, UnsetCode) --> 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) }, { UnsetCode = rl_PROC_unsetrel(Addr) }. %-----------------------------------------------------------------------------% :- 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 = (pred(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, SemiJoin, TrivialJoin) - _, Code) --> rl_out__generate_join(Output, Input1, Input2, Type, Cond, SemiJoin, TrivialJoin, Code). rl_out__generate_instr( subtract(Output, Input1, Input2, Type, Cond, TrivialSubtract) - _, Code) --> rl_out__generate_subtract(Output, Input1, Input2, Type, Cond, TrivialSubtract, 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__generate_project(Output, Input, Cond0, OtherOutputs, ProjectType, Code). 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), Input) - _, Code) --> % Generated as % if (is_permanent(Input) and !has_index(Input, Indexes)) { % copy(Output, Input); % } else { % ref(Output, Input); % if (has_index(Input, Indexes)) { % ; % } else { % rl_PROC_add_index(Output, Indexes); % } % } % rl_out__generate_test_for_non_indexed_permanent(Input, Indexes, CondCode), rl_out__generate_instr(copy(output_rel(Rel, Indexes), Input) - "", ThenCode), rl_out__generate_instr(ref(Rel, Input) - "", RefCode), rl_out__add_indexes_to_rel(may_have_index, Rel, Indexes, IndexCode), { ElseCode = tree(RefCode, IndexCode) }, rl_out__generate_ite(CondCode, ThenCode, ElseCode, 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 = add_index(InputRel) % } else { % OutputRel = copy(InputRel) % } rl_out_info_get_relation_addr(Input, InputAddr), { CondCode = node([rl_PROC_one_reference(InputAddr)]) }, % 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, Input) - Comment, ThenCode), 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((pred(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_copy_permanents(may_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(output_rel::in, relation_id::in, relation_id::in, join_type::in, rl_goal::in, maybe(semi_join_info)::in, maybe(trivial_join_info)::in, byte_tree::out, rl_out_info::in, rl_out_info::out) is det. rl_out__generate_join(Output, Input1a, Input2a, JoinType0, Cond0, MaybeSemiJoin, MaybeTrivialJoin, Code) --> % % Work out the bytecode to use for the join, and whether the % join is actually a semi-join. % { rl_out__compute_join_bytecode(JoinType0, MaybeSemiJoin, JoinBytecode, SwapInputs) }, { MaybeSemiJoin = yes(_), rl__strip_goal_outputs(Cond0, Cond1) ; MaybeSemiJoin = no, Cond1 = Cond0 }, { SwapInputs = yes, Input1 = Input2a, Input2 = Input1a, rl__swap_goal_inputs(Cond1, Cond), rl__swap_join_type_inputs(JoinType0, JoinType) ; SwapInputs = no, Input1 = Input1a, Input2 = Input2a, Cond = Cond1, JoinType = JoinType0 }, % % Optimize a common case here - if the join condition does not % depend on the second relation, we generate this as: % if (empty(rel2)) { % init(output); % } else { % output = project(rel1); % } % % This happens often for joins with zero-arity input relations. % % The projection will be unnecessary for a semi-join where % the condition is deterministic. % ( { MaybeTrivialJoin = yes(TrivialJoinInfo) }, { TrivialJoinInfo = trivial_join_or_subtract_info( ProjectTupleNum0, MaybeProjectType) }, { SwapInputs = yes, rl__swap_tuple_num(ProjectTupleNum0, ProjectTupleNum) ; SwapInputs = no, ProjectTupleNum = ProjectTupleNum0 }, { ProjectTupleNum = one, ProjectInput = Input1, TestRel = Input2 ; ProjectTupleNum = two, ProjectInput = Input2, TestRel = Input1 }, ( { MaybeProjectType = yes(ProjectType) }, { rl__swap_tuple_num(ProjectTupleNum, InputToRemove) }, { rl__remove_goal_input(InputToRemove, Cond, ProjectCond) }, { ProjectInstr = project(Output, ProjectInput, ProjectCond, [], ProjectType) - "" }, rl_out__generate_instr(ProjectInstr, ElseCode) ; { MaybeProjectType = no }, rl_out__maybe_materialise(Output, ProjectInput, ElseCode) ), rl_out__generate_stream(TestRel, TestStreamCode), { CondCode = tree(node([rl_PROC_empty]), TestStreamCode) }, rl_out__generate_instr(init(Output) - "", ThenCode), rl_out__generate_ite(CondCode, ThenCode, ElseCode, Code) ; { MaybeTrivialJoin = no }, rl_out__generate_join_or_subtract(Output, Input1, Input2, JoinType, JoinBytecode, Cond, Code) ). :- pred rl_out__compute_join_bytecode(join_type::in, maybe(semi_join_info)::in, bytecode::out, bool::out) is det. rl_out__compute_join_bytecode(nested_loop, no, rl_PROC_join_nl, no). rl_out__compute_join_bytecode(nested_loop, yes(Tuple), rl_PROC_semijoin_nl, Swap) :- rl_out__should_swap_inputs(Tuple, Swap). rl_out__compute_join_bytecode(sort_merge(_, _), no, rl_PROC_join_sm, no). rl_out__compute_join_bytecode(sort_merge(_, _), yes(Tuple), rl_PROC_semijoin_sm, Swap) :- rl_out__should_swap_inputs(Tuple, Swap), error( "rl_out__compute_join_bytecode: sort-merge semi-joins not yet implemented in Aditi"). rl_out__compute_join_bytecode(hash(_, _), no, rl_PROC_join_hj, no). rl_out__compute_join_bytecode(hash(_, _), yes(Tuple), rl_PROC_semijoin_hj, Swap) :- rl_out__should_swap_inputs(Tuple, Swap). rl_out__compute_join_bytecode(index(_, _), no, rl_PROC_join_index_simple(IndexRangeTypes), no) :- % both ends of the key range are closed. % XXX we should detect and use open ranges IndexRangeTypes = 0. rl_out__compute_join_bytecode(index(_, _), yes(Tuple), rl_PROC_semijoin_index(Output, IndexRangeTypes), no) :- % The tuple from the non-indexed relation is returned -- % returning the tuple from the index relation is not % yet implemented in Aditi. Output = 0, % both ends of the key range are closed. % XXX we should detect and use open ranges IndexRangeTypes = 0, require(unify(Tuple, one), "indexed semi_join doesn't return first tuple"). % For semi_joins, the first input tuple is the one returned. :- pred rl_out__should_swap_inputs(tuple_num::in, bool::out) is det. rl_out__should_swap_inputs(one, no). rl_out__should_swap_inputs(two, yes). %-----------------------------------------------------------------------------% :- pred rl_out__generate_subtract(output_rel::in, relation_id::in, relation_id::in, subtract_type::in, rl_goal::in, maybe(trivial_subtract_info)::in, byte_tree::out, rl_out_info::in, rl_out_info::out) is det. rl_out__generate_subtract(Output, Input1, Input2, Type, Cond, TrivialSubtract, Code) --> ( { TrivialSubtract = yes( trivial_join_or_subtract_info(TupleNum, MaybeProject)) }, ( { TupleNum = one }, % Output = subtract(Input1, Input2, Cond), % where Cond is independent of input two, % is generated as: % % if (empty(Input2)) { % Output = Input1 % else { % Output = select(Input1, not(Cond)) % } ( { MaybeProject = yes(ProjectType) }, { rl__remove_goal_input(two, Cond, ProjectCond0) }, { Goals0 = ProjectCond0 ^ goal }, { goal_list_nonlocals(Goals0, NonLocals) }, { goal_list_determinism(Goals0, Detism0) }, { det_negation_det(Detism0, MaybeDetism) }, { MaybeDetism = yes(NegDetism0) -> NegDetism = NegDetism0 ; % This should probably never happen, % but semidet is a safe approximation. NegDetism = semidet }, { instmap_delta_init_reachable(IMDelta) }, { goal_info_init(NonLocals, IMDelta, Detism0, pure, GoalInfo) }, { conj_list_to_goal(Goals0, GoalInfo, Conj) }, { goal_info_init(NonLocals, IMDelta, NegDetism, pure, NegGoalInfo) }, { NegGoal = not(Conj) - NegGoalInfo }, { ProjectCond = ProjectCond0 ^ goal := [NegGoal] }, { ProjectInstr = project(Output, Input1, ProjectCond, [], ProjectType) - "" }, rl_out__generate_instr(ProjectInstr, ElseCode) ; { MaybeProject = no }, % The selection is not removed by % rl__is_trivial_subtract in this case. { error( "rl_out__generate_subtract: trivial subtract without select") } ), rl_out__generate_stream(Input2, InputStream2Code), { CondCode = tree(node([rl_PROC_empty]), InputStream2Code) }, rl_out__generate_instr(init(Output) - "", ThenCode), rl_out__generate_ite(CondCode, ThenCode, ElseCode, Code) ; { TupleNum = two }, % Output = subtract(Input1, Input2, Cond), % where Cond is independent of input one, % is generated as: % % if (empty(select(Input2, Cond))) { % Output = Input1 % else { % init(Output) % } ( { MaybeProject = yes(ProjectType) }, { rl__remove_goal_input(one, Cond, ProjectCond) }, rl_out_info_get_relation_schema(Input2, Input2Schema), rl_out_info_add_temporary_relation(Input2Schema, stream, TmpRel), { ProjectInstr = project(output_rel(TmpRel, []), Input2, ProjectCond, [], ProjectType) - "" }, rl_out__generate_instr(ProjectInstr, ProjectCode), { ProjectRel = TmpRel } ; { MaybeProject = no }, { ProjectRel = Input2 }, { ProjectCode = empty } ), rl_out__generate_stream(ProjectRel, ProjectStreamCode), { CondCode = tree(node([rl_PROC_empty]), ProjectStreamCode) }, rl_out__maybe_materialise(Output, Input1, ThenCode), rl_out__generate_instr(init(Output) - "", ElseCode), rl_out__generate_ite(CondCode, ThenCode, ElseCode, ITECode), { Code = tree(ProjectCode, ITECode) } ) ; { TrivialSubtract = no }, { rl_out__compute_subtract_bytecode(Type, SubtractBytecode, JoinType) }, rl_out__generate_join_or_subtract(Output, Input1, Input2, JoinType, SubtractBytecode, Cond, Code) ). % Work out which bytecode to use, and which join type % uses the same instruction format. :- pred rl_out__compute_subtract_bytecode(subtract_type::in, bytecode::out, join_type::out) is det. rl_out__compute_subtract_bytecode(semi_nested_loop, rl_PROC_semisubtract_nl, nested_loop). rl_out__compute_subtract_bytecode(semi_sort_merge(_, _), _, _) :- error( "rl_out__compute_subtract_bytecode: subtract_sm not yet implemented"). rl_out__compute_subtract_bytecode(semi_hash(Attrs1, Attrs2), rl_PROC_semisubtract_hj, hash(Attrs1, Attrs2)). rl_out__compute_subtract_bytecode(semi_index(IndexSpec, KeyRange), rl_PROC_semisubtract_index(IndexRangeTypes), index(IndexSpec, KeyRange)) :- % both ends of the key range are closed. % XXX we should detect and use open ranges IndexRangeTypes = 0. %-----------------------------------------------------------------------------% :- pred rl_out__generate_join_or_subtract(output_rel::in, relation_id::in, relation_id::in, join_type::in, bytecode::in, rl_goal::in, byte_tree::out, rl_out_info::in, rl_out_info::out) is det. rl_out__generate_join_or_subtract(Output, Input1, Input2, JoinType, JoinBytecode, Cond, Code) --> ( { JoinType = nested_loop }, rl_out__generate_join_or_subtract_2(JoinBytecode, Output, Input1, Input2, [], Cond, Code) ; { JoinType = hash(Attrs1, Attrs2) }, % % Hash values are not necessarily unique, so % the entire join condition must run when % performing the nested loop join on tuples % with the same hash value. % rl_out__generate_hash_exprn(Input1, Attrs1, HashExprn1), rl_out__generate_hash_exprn(Input2, Attrs2, HashExprn2), rl_out__generate_join_or_subtract_2(JoinBytecode, Output, Input1, Input2, [HashExprn1, HashExprn2], Cond, Code) ; { JoinType = sort_merge(Spec1, Spec2) }, rl_out_info_get_relation_schema(Input1, Schema1), rl_out_info_get_relation_schema(Input1, Schema2), rl_out__generate_sort_merge_compare_exprn(Spec1, Schema1, Spec2, Schema2, CompareExprn), % % XXX We should strip out the parts of the join % condition which are already tested by the CompareExprns. % rl_out__generate_join_or_subtract_2(JoinBytecode, Output, Input1, Input2, [CompareExprn], Cond, Code) ; { JoinType = 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), % % XXX We should strip out the parts of the join % condition which are already tested by the comparison % against the ends of the key range. % rl_out__generate_exprn(Cond, OutputSchemaOffset, CondExprn), { InstrCode = tree(node([JoinBytecode]), tree(Stream1Code, node([ rl_PROC_indexed_var(Input2Addr, 0, IndexConst), rl_PROC_expr(RangeExprn), rl_PROC_expr(CondExprn) ]) )) }, rl_out__generate_stream_instruction(Output, InstrCode, Code) ). :- pred rl_out__generate_join_or_subtract_2(bytecode::in, output_rel::in, relation_id::in, relation_id::in, list(int)::in, rl_goal::in, byte_tree::out, rl_out_info::in, rl_out_info::out) is det. rl_out__generate_join_or_subtract_2(JoinCode, Output, Input1, Input2, ExtraExprns, 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), { list__append(ExtraExprns, [CondExprn], Exprns) }, { list__map(pred(Exprn::in, rl_PROC_expr(Exprn)::out) is det, Exprns, ExprnInstrs) }, { InstrCode = tree(node([JoinCode]), tree(Stream1Code, tree(Stream2Code, node(ExprnInstrs) ))) }, rl_out__generate_stream_instruction(Output, InstrCode, Code). %-----------------------------------------------------------------------------% :- pred rl_out__generate_project(output_rel::in, relation_id::in, rl_goal::in, assoc_list(output_rel, rl_goal)::in, project_type::in, byte_tree::out, rl_out_info::in, rl_out_info::out) is det. rl_out__generate_project(Output, Input, Cond0, OtherOutputs, ProjectType, Code) --> rl_out_info_get_output_relation_schema_offset(Output, OutputSchemaOffset), % % If the goal passes the input tuple through unmodified, % the projection is actually a selection. % { rl__goal_returns_input_tuple(Cond0, one) -> rl__strip_goal_outputs(Cond0, Cond) ; Cond = Cond0 }, ( { OtherOutputs = [] }, { rl__goal_is_independent_of_input(one, Cond) } -> % % 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. % { rl__remove_goal_input(one, Cond, TupleGoal) }, rl_out__generate_exprn(TupleGoal, 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. % % XXX We should strip out the parts of the join % condition which are already tested by the % CompareExprns. The project_tee operation % may not be necessary. % { 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), % both ends of the key range are closed. % XXX we should detect and use open ranges { IndexRangeTypes = 0 }, { StreamCode = node([ rl_PROC_stream, rl_PROC_btree_scan(IndexRangeTypes), rl_PROC_indexed_var(InputAddr, 0, IndexConst), rl_PROC_expr(RangeExprn), rl_PROC_stream_end ]) } ), rl_out__generate_exprn(Cond, 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) } ). %-----------------------------------------------------------------------------% % 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) :- % Aditi counts attributes starting from 0. string__int_to_string(Attr - 1, AttrStr), string__append("#:", AttrStr, Str). %-----------------------------------------------------------------------------% :- type check_index ---> may_have_index ; does_not_have_index . :- pred rl_out__add_indexes_to_rels_copy_permanents(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_copy_permanents(CheckIndex, OutputRels, IndexCode) --> list__foldl2(rl_out__add_indexes_to_rel_copy_permanent(CheckIndex), OutputRels, empty, IndexCode). :- pred rl_out__add_indexes_to_rel_copy_permanent(check_index::in, output_rel::in, byte_tree::in, byte_tree::out, rl_out_info::in, rl_out_info::out) is det. rl_out__add_indexes_to_rel_copy_permanent(CheckIndex, OutputRel, Code0, Code) --> { OutputRel = output_rel(Output, Indexes) }, ( { Indexes = [] }, { Code = Code0 } ; { Indexes = [_ | _] }, rl_out__generate_test_for_non_indexed_permanent(Output, Indexes, CondCode), % Copy the base relation, because queries can't add % indexes to a base relation. rl_out__generate_instr(copy(OutputRel, Output) - "", CopyCode), rl_out__add_indexes_to_rel(CheckIndex, Output, Indexes, IndexCode), rl_out__generate_ite(CondCode, CopyCode, IndexCode, Code) ). :- 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) }. % Test whether the input relation is a base relation which does % not have one of the given indexes. If we are going to add % the indexes, the base relation needs to be copied (queries should % never add indexes to base relations). :- pred rl_out__generate_test_for_non_indexed_permanent(relation_id::in, list(index_spec)::in, byte_tree::out, rl_out_info::in, rl_out_info::out) is det. rl_out__generate_test_for_non_indexed_permanent(Input, Indexes, CondCode) --> rl_out_info_get_relation_addr(Input, InputAddr), { GenerateHasIndexCode = (pred(Index::in, HasIndexCode::out, Info0::in, Info::out) is det :- rl_out__index_spec_to_string(Index, IndexStr), rl_out_info_assign_const(string(IndexStr), IndexConst, Info0, Info), HasIndexCode = node([ rl_PROC_not, rl_PROC_has_index(InputAddr, IndexConst) ]) ) }, list__map_foldl(GenerateHasIndexCode, Indexes, IndexTests), { IndexTests = [IndexTest | IndexTests1], CombineTest = (pred(Test1::in, CombinedCode0::in, CombinedCode::out) is det :- CombinedCode = tree(node([rl_PROC_or]), tree(Test1, CombinedCode0 )) ), list__foldl(CombineTest, IndexTests1, IndexTest, IndexTestCode), CondCode = tree(node([ rl_PROC_and, rl_PROC_is_permanent(InputAddr) ]), IndexTestCode ) ; IndexTests = [], error( "rl_out__generate_test_for_non_indexed_permanent: no indexes") }. %-----------------------------------------------------------------------------% % 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), { 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), rl_out_info_return_tmp_var(SchemaOffset, TmpVar, TmpClearCode), { 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__maybe_materialise(output_rel::in, relation_id::in, byte_tree::out, rl_out_info::in, rl_out_info::out) is det. rl_out__maybe_materialise(OutputRel, Input, Code) --> { OutputRel = output_rel(Output, Indexes) }, rl_out_info_get_relation_type(Input, InputType), rl_out_info_get_relation_type(Output, OutputType), ( { InputType = temporary(stream) }, { OutputType = temporary(materialised) } -> % % Materialise the input into the output. % rl_out__generate_instr(copy(OutputRel, Input) - "", Code) ; ( { Indexes = [] } -> rl_out__generate_instr(ref(Output, Input) - "", Code) ; rl_out__generate_instr( add_index(OutputRel, Input) - "", Code) ) ). %-----------------------------------------------------------------------------% :- 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 = 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 = (pred(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 = (pred(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 = (pred(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 = (pred(Instr::in, S0::in, S::out) is det :- bytecode_to_intlist(Instr, IntList), list__length(IntList, S1), S = 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 = 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 }, { NumParams = 1 }, rl_out__package_exprn(AggCode, NumParams, 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) ). % Generate an expression to compare the join attributes % in a sort-merge equi-join. :- pred rl_out__generate_sort_merge_compare_exprn(sort_spec::in, list(type)::in, sort_spec::in, list(type)::in, int::out, rl_out_info::in, rl_out_info::out) is det. rl_out__generate_sort_merge_compare_exprn(Attrs1, Schema1, Attrs2, Schema2, ExprnNum) --> rl_out_info_get_sort_merge_compare_exprns(CompareExprns0), rl_out__schema_to_string(Schema1, Schema1Offset), rl_out__schema_to_string(Schema2, Schema2Offset), { CompareExprnId = (Attrs1 - Schema1Offset) - (Attrs2 - Schema2Offset) }, ( { map__search(CompareExprns0, CompareExprnId, ExprnNum0) } -> { ExprnNum = ExprnNum0 } ; rl_out_info_get_module_info(ModuleInfo), { rl_exprn__generate_sort_merge_compare_exprn(ModuleInfo, Attrs1, Schema1, Attrs2, Schema2, 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, CompareExprnId, ExprnNum, CompareExprns) }, rl_out_info_set_sort_merge_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 = TermDepth * 2 + 10 }, rl_out__package_exprn(ExprnCode, NumParams, generate2, Output1SchemaOffset, Output2SchemaOffset, StackSize, Decls, RangeExprn). :- pred rl_out__generate_hash_exprn(relation_id::in, list(int)::in, int::out, rl_out_info::in, rl_out_info::out) is det. rl_out__generate_hash_exprn(Input, Attrs, ExprnNum) --> rl_out_info_get_relation_schema(Input, InputSchema), rl_out__schema_to_string(InputSchema, InputSchemaOffset), rl_out__do_generate_hash_exprn(InputSchema, InputSchemaOffset, Attrs, ExprnNum). :- pred rl_out__do_generate_hash_exprn(list(type)::in, int::in, list(int)::in, int::out, rl_out_info::in, rl_out_info::out) is det. rl_out__do_generate_hash_exprn(InputSchema, InputSchemaOffset, Attrs, ExprnNum) --> rl_out_info_get_hash_exprns(HashExprns0), ( { map__search(HashExprns0, Attrs - InputSchemaOffset, ExprnNum0) } -> { ExprnNum = ExprnNum0 } ; rl_out_info_get_module_info(ModuleInfo), { rl_exprn__generate_hash_function(ModuleInfo, Attrs, InputSchema, ExprnCode) }, rl_out__schema_to_string([], EmptySchemaOffset), % Nothing is built on the stack, so this will be enough. { StackSize = 10 }, { NumParams = 1 }, { Decls = [] }, rl_out__package_exprn(ExprnCode, NumParams, test, EmptySchemaOffset, EmptySchemaOffset, StackSize, Decls, ExprnNum), { map__det_insert(HashExprns0, Attrs - InputSchemaOffset, ExprnNum, HashExprns) }, rl_out_info_set_hash_exprns(HashExprns) ). % This is only used by the code to generate the modification % and deletion procedures for base relations, so avoiding % generating multiple copies of one of these is pointless -- % only one will ever be generated for each procedure. :- pred rl_out__do_generate_equijoin_exprn(list(type)::in, list(int)::in, int::out, rl_out_info::in, rl_out_info::out) is det. rl_out__do_generate_equijoin_exprn(InputSchema, Attrs, ExprnNum) --> rl_out_info_get_module_info(ModuleInfo), { rl_exprn__generate_equijoin_exprn(ModuleInfo, Attrs, InputSchema, ExprnCode) }, % Nothing is built on the stack, so this will be enough. { StackSize = 10 }, { NumParams = 2 }, { Decls = [] }, rl_out__schema_to_string([], EmptySchemaOffset), rl_out__package_exprn(ExprnCode, NumParams, test, EmptySchemaOffset, EmptySchemaOffset, StackSize, Decls, ExprnNum). % This is only used by the code to generate the modification % procedures for base relations, so avoiding generating multiple % copies of one of these is pointless -- only one will ever be % generated for each procedure. :- pred rl_out__generate_modify_project_exprn(list(type)::in, int::in, tuple_num::in, int::out, rl_out_info::in, rl_out_info::out) is det. rl_out__generate_modify_project_exprn(Schema, SchemaOffset, TupleNum, ExprnNum) --> rl_out_info_get_module_info(ModuleInfo), { rl_exprn__generate_modify_project_exprn(ModuleInfo, TupleNum, Schema, Code) }, % Nothing is built on the stack, so this will be enough. { StackSize = 10 }, { NumParams = 1 }, { ExprnMode = generate }, { Decls = [] }, rl_out__schema_to_string([], EmptySchemaOffset), rl_out__package_exprn(Code, NumParams, ExprnMode, SchemaOffset, EmptySchemaOffset, StackSize, Decls, ExprnNum). :- 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( module_info :: module_info, pc :: int, procs :: list(procedure), % bytecodes for each procedure, % in reverse order. % % Tables used to avoid generating multiple % copies of the expressions used to % compare tuples and compute hash values. % compare_exprns :: compare_exprns, sort_merge_compare_exprns :: sort_merge_compare_exprns, hash_exprns :: hash_exprns, permanent_relations :: set(relation), relation_addrs :: map(relation_id, int), % relation vars next_relation_addr :: int, % next relation address relation_variables :: list(variable), % variables used in % reverse order. relations :: map(relation_id, relation_info), proc_labels :: map(label_id, int), % proc label offsets next_proc_label :: int, consts :: map(rl_const, int), % procedure consts next_const :: int, % next proc const address next_materialise :: int, % next materialise number - % used for debugging the % generated code. exprns :: list(expression), % expressions for the current % procedure in reverse order. next_exprn :: int, % next expression. tmp_vars :: 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 or hash 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). % The comparison in a sort-merge join takes for each relation % the list of attributes being joined on and the schema offset. :- type sort_merge_compare == pair(pair(sort_spec, int)). :- type sort_merge_compare_exprns == map(sort_merge_compare, int). :- type hash_exprns == map(pair(list(int), 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(SortMergeCompareExprns), map__init(HashExprns), map__init(Relations), map__init(RelationAddrs), map__init(Consts), map__init(Labels), map__init(TmpVars), PC = 0, FirstRelAddr = 0, FirstConst = 1, FirstMaterialise = 1, FirstLabel = 0, Exprns = [], FirstExprn = 0, Procs = [], set__init(PermanentRelations), RelationVariables = [], Info0 = rl_out_info(ModuleInfo, PC, Procs, CompareExprns, SortMergeCompareExprns, HashExprns, PermanentRelations, RelationAddrs, FirstRelAddr, RelationVariables, Relations, Labels, FirstLabel, Consts, FirstConst, FirstMaterialise, Exprns, FirstExprn, TmpVars). :- pred rl_out_info_init_proc(map(relation_id, relation_info)::in, rl_out_info::in, rl_out_info::out) is det. rl_out_info_init_proc(Relations) --> ^ relations := Relations, { map__init(Labels) }, ^ proc_labels := Labels, ^ next_proc_label := 0, { map__init(RelationAddrs) }, ^ relation_addrs := RelationAddrs, { map__init(CompareExprns) }, ^ compare_exprns := CompareExprns, { map__init(HashExprns) }, ^ hash_exprns := HashExprns, ^ pc := 0, ^ exprns := [], ^ next_exprn := 0, { map__init(TmpVars) }, ^ tmp_vars := 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) --> Exprns =^ compare_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) --> ^ compare_exprns := Exprns. %-----------------------------------------------------------------------------% :- pred rl_out_info_get_sort_merge_compare_exprns( sort_merge_compare_exprns::out, rl_out_info::in, rl_out_info::out) is det. rl_out_info_get_sort_merge_compare_exprns(Exprns) --> Exprns =^ sort_merge_compare_exprns. :- pred rl_out_info_set_sort_merge_compare_exprns( sort_merge_compare_exprns::in, rl_out_info::in, rl_out_info::out) is det. rl_out_info_set_sort_merge_compare_exprns(Exprns) --> ^ sort_merge_compare_exprns := Exprns. %-----------------------------------------------------------------------------% :- pred rl_out_info_get_hash_exprns(hash_exprns::out, rl_out_info::in, rl_out_info::out) is det. rl_out_info_get_hash_exprns(HashExprns) --> HashExprns =^ hash_exprns. :- pred rl_out_info_set_hash_exprns(hash_exprns::in, rl_out_info::in, rl_out_info::out) is det. rl_out_info_set_hash_exprns(HashExprns) --> ^ hash_exprns := HashExprns. %-----------------------------------------------------------------------------% :- 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) --> Addrs =^ relation_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) --> ^ relation_addrs := Addrs. :- 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) --> Vars0 =^ relation_variables, ^ relation_variables := [variable(Name, Schema) | Vars0]. :- 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) --> NextAddr0 =^ next_relation_addr, ^ next_relation_addr := NextAddr0 + 1. :- 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) :- list__reverse(Info ^ relation_variables, 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) --> rl_out_info_add_label(NextLabel), Labels0 =^ proc_labels, { map__det_insert(Labels0, LabelId, NextLabel, Labels) }, ^ proc_labels := Labels. :- pred rl_out_info_add_label(int::out, rl_out_info::in, rl_out_info::out) is det. rl_out_info_add_label(NextLabel) --> NextLabel =^ next_proc_label, ^ next_proc_label := NextLabel + 1. :- 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) --> Labels =^ proc_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) --> ModuleInfo =^ module_info. %-----------------------------------------------------------------------------% :- 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) --> Consts0 =^ consts, ( { map__search(Consts0, Const, ConstOffset0) } -> { ConstOffset = ConstOffset0 } ; ConstOffset =^ next_const, { map__det_insert(Consts0, Const, ConstOffset, Consts) }, ^ consts := Consts, ^ next_const := ConstOffset + 1 ). :- 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) --> Consts =^ 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) --> MaterialiseId =^ next_materialise, ^ next_materialise := MaterialiseId + 1. %-----------------------------------------------------------------------------% :- 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) --> Relations =^ relations. :- pred rl_out_info_add_temporary_relation(list(type)::in, relation_state::in, relation_id::out, rl_out_info::in, rl_out_info::out) is det. rl_out_info_add_temporary_relation(Schema, State, RelationId) --> Relations0 =^ relations, % This should be pretty rare (this predicate is currently only % used in optimizing one type of trivial subtract), so efficiency % isn't a concern. { map__sorted_keys(Relations0, RelationIds0) }, { list__last(RelationIds0, HighestRelationId) -> RelationId = HighestRelationId + 1 ; RelationId = 0 }, { rl__relation_id_to_string(RelationId, RelName) }, { RelationInfo = relation_info(temporary(State), Schema, [], RelName) }, { map__det_insert(Relations0, RelationId, RelationInfo, Relations) }, ^ relations := 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) --> PC0 =^ pc, ^ pc := PC0 + Incr. :- pred rl_out_info_get_pc(int::out, rl_out_info::in, rl_out_info::out) is det. rl_out_info_get_pc(PC) --> PC =^ pc. %-----------------------------------------------------------------------------% :- pred rl_out_info_add_proc(procedure::in, rl_out_info::in, rl_out_info::out) is det. rl_out_info_add_proc(Proc) --> Procs0 =^ procs, ^ procs := [Proc | Procs0]. :- 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) --> Procs0 =^ procs, { 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) --> Rels =^ permanent_relations. :- 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) --> ^ permanent_relations := Rels. %-----------------------------------------------------------------------------% :- 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) --> Exprns0 =^ exprns, { 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) --> Exprns0 =^ exprns, NextExprn0 =^ next_exprn, ^ exprns := [Exprn | Exprns0], ^ next_exprn := NextExprn0 + 1. %-----------------------------------------------------------------------------% :- 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) --> TmpVars =^ tmp_vars. :- 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) --> ^ tmp_vars := TmpVars. #else % !INCLUDE_ADITI_OUTPUT #endif %-----------------------------------------------------------------------------% %-----------------------------------------------------------------------------%