mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-12 20:34:19 +00:00
Estimated hours taken: 4 Branches: main This diff contains no changes in algorithms whatsoever. browser/*.m: compiler/*.m: library/*.m: Replace old-style lambdas with new-style lambdas or with named procedures.
1736 lines
61 KiB
Mathematica
1736 lines
61 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 1998-2000, 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_block_opt.
|
|
% Main author: stayl
|
|
%
|
|
% Optimizations local to a basic block.
|
|
% This builds a DAG representing the expressions computed by the block,
|
|
% applies some rewriting rules to the DAG, then generates the new
|
|
% instruction list from the DAG.
|
|
%
|
|
% Optimizations performed:
|
|
% - cse
|
|
% - copy elimination
|
|
% - optimize R3 = difference(R1, R2), R4 = union(R1, R3) into a union diff.
|
|
% - merge multiple projections of the same node into a single instruction.
|
|
% - merge projections of projections into a single instruction.
|
|
%
|
|
% To do -
|
|
% push selections through union, sort etc.
|
|
%-----------------------------------------------------------------------------%
|
|
:- module aditi_backend__rl_block_opt.
|
|
|
|
:- interface.
|
|
|
|
:- import_module aditi_backend__rl_block.
|
|
:- import_module io, list.
|
|
|
|
:- pred rl_block_opt(list(opt_flag)::in, rl_opt_info::in, rl_opt_info::out,
|
|
io__state::di, io__state::uo) is det.
|
|
|
|
:- type opt_flag
|
|
---> add_uniondiff
|
|
; merge_output_projections
|
|
; add_pre_projections
|
|
.
|
|
|
|
:- implementation.
|
|
|
|
:- import_module aditi_backend__rl.
|
|
:- import_module aditi_backend__rl_key.
|
|
:- import_module hlds__goal_util.
|
|
:- import_module hlds__hlds_goal.
|
|
:- import_module hlds__hlds_module.
|
|
:- import_module hlds__hlds_pred.
|
|
:- import_module libs__globals.
|
|
:- import_module libs__options.
|
|
:- import_module parse_tree__prog_data.
|
|
:- import_module transform_hlds__inlining.
|
|
|
|
:- import_module assoc_list, bool, int, map, multi_map.
|
|
:- import_module relation, require, set, std_util, string.
|
|
|
|
rl_block_opt(Flags, Info0, Info, IO0, IO) :-
|
|
rl_block_opt_2(Flags, IO0, IO, Info0, Info).
|
|
|
|
:- pred rl_block_opt_2(list(opt_flag)::in, io__state::di, io__state::uo,
|
|
rl_opt_info::in, rl_opt_info::out) is det.
|
|
|
|
rl_block_opt_2(Flags, IO, IO) -->
|
|
rl_opt_info_get_rev_block_order(RevOrder),
|
|
{ list__reverse(RevOrder, Order) },
|
|
|
|
list__foldl(rl_block_opt__block(Flags), Order).
|
|
|
|
:- pred rl_block_opt__block(list(opt_flag)::in, block_id::in,
|
|
rl_opt_info::in, rl_opt_info::out) is det.
|
|
|
|
rl_block_opt__block(Flags, BlockId, Info0, Info) :-
|
|
rl_opt_info_get_block(BlockId, Block0, Info0, Info1),
|
|
Block0 = block(MaybeLabel, Instrs0, MaybeBranch, BlockInfo),
|
|
|
|
dag_init(Info1, BlockInfo, Flags, Dag0),
|
|
BlockInfo = block_info(LiveAtStart0, _),
|
|
set__to_sorted_list(LiveAtStart0, LiveAtStart),
|
|
list__foldl(rl_block_opt__setup_input_relation, LiveAtStart,
|
|
Dag0, Dag1),
|
|
list__foldl(rl_block_opt__build_dag, Instrs0, Dag1, Dag2),
|
|
rl_block_opt__rewrite_dag(Dag2, Dag3),
|
|
rl_block_opt__deconstruct_dag(Instrs, Dag3, Dag),
|
|
dag_get_rl_opt_info(Info2, Dag, _),
|
|
Block = block(MaybeLabel, Instrs, MaybeBranch, BlockInfo),
|
|
rl_opt_info_set_block(BlockId, Block, Info2, Info).
|
|
|
|
:- pred rl_block_opt__setup_input_relation(relation_id::in,
|
|
dag::in, dag::out) is det.
|
|
|
|
rl_block_opt__setup_input_relation(Relation) -->
|
|
rl_block_opt__add_dag_node([output_rel(Relation, [])], [],
|
|
input(Relation), _).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Add an instruction to the DAG for the block.
|
|
:- pred rl_block_opt__build_dag(rl_instruction::in, dag::in, dag::out) is det.
|
|
|
|
rl_block_opt__build_dag(join(Output, Input1, Input2, Type, Exprn,
|
|
SemiJoin, TrivialJoin) - _) -->
|
|
rl_block_opt__lookup_relation(Input1, Input1Node),
|
|
rl_block_opt__lookup_relation(Input2, Input2Node),
|
|
rl_block_opt__add_dag_node([Output], [Input1Node, Input2Node],
|
|
join(Input1Node, Input2Node, Type, Exprn,
|
|
SemiJoin, TrivialJoin), _).
|
|
|
|
rl_block_opt__build_dag(subtract(Output, Input1, Input2, Type,
|
|
Exprn, TrivialSubtract) - _) -->
|
|
rl_block_opt__lookup_relation(Input1, Input1Node),
|
|
rl_block_opt__lookup_relation(Input2, Input2Node),
|
|
rl_block_opt__add_dag_node([Output], [Input1Node, Input2Node],
|
|
subtract(Input1Node, Input2Node, Type, Exprn, TrivialSubtract),
|
|
_).
|
|
|
|
rl_block_opt__build_dag(difference(Output, Input1, Input2, Type) - _) -->
|
|
rl_block_opt__lookup_relation(Input1, Input1Node),
|
|
rl_block_opt__lookup_relation(Input2, Input2Node),
|
|
rl_block_opt__add_dag_node([Output], [Input1Node, Input2Node],
|
|
difference(Input1Node, Input2Node, Type), _).
|
|
|
|
rl_block_opt__build_dag(project(Output, Input, Exprn,
|
|
OtherOutputs, Type) - _) -->
|
|
{ assoc_list__keys(OtherOutputs, MaterialisedOutputs) },
|
|
{ assoc_list__values(OtherOutputs, Exprns) },
|
|
rl_block_opt__lookup_relation(Input, InputNode),
|
|
rl_block_opt__add_dag_node([Output | MaterialisedOutputs], [InputNode],
|
|
project(InputNode, [Exprn | Exprns], Type), _).
|
|
|
|
rl_block_opt__build_dag(union(Output, Inputs, Type) - _) -->
|
|
rl_block_opt__lookup_relations(Inputs, InputNodes0),
|
|
|
|
% Optimize some common cases.
|
|
{ list__sort_and_remove_dups(InputNodes0, InputNodes) },
|
|
( { InputNodes = [] } ->
|
|
rl_block_opt__build_dag(init(Output) - "")
|
|
; { InputNodes = [InputNode] } ->
|
|
rl_block_opt__set_output_rel_id(Output, InputNode)
|
|
;
|
|
rl_block_opt__add_dag_node([Output], InputNodes,
|
|
union(InputNodes, Type), _)
|
|
).
|
|
|
|
rl_block_opt__build_dag(insert(UoOutput, DiInput, Input, Type, _) - _) -->
|
|
rl_block_opt__lookup_relation(Input, InputNode),
|
|
rl_block_opt__lookup_relation(DiInput, DiInputNode),
|
|
rl_block_opt__add_dag_node([output_rel(UoOutput, [])],
|
|
[DiInputNode, InputNode],
|
|
insert(DiInputNode, InputNode, Type), _).
|
|
|
|
rl_block_opt__build_dag(
|
|
union_diff(UoOutput, DiInput, Input, Diff, Index, _) - _) -->
|
|
rl_block_opt__lookup_relation(Input, InputNode),
|
|
rl_block_opt__lookup_relation(DiInput, DiInputNode),
|
|
rl_block_opt__add_dag_node([output_rel(UoOutput, []), Diff],
|
|
[DiInputNode, InputNode],
|
|
union_diff(DiInputNode, InputNode, Index), _).
|
|
|
|
rl_block_opt__build_dag(sort(Output, Input, Attrs) - _) -->
|
|
rl_block_opt__lookup_relation(Input, InputNode),
|
|
rl_block_opt__add_dag_node([Output], [InputNode],
|
|
sort(InputNode, Attrs), _).
|
|
|
|
rl_block_opt__build_dag(ref(Output, Input) - _) -->
|
|
rl_block_opt__lookup_relation(Input, InputNode),
|
|
rl_block_opt__set_relation_output_id(Output, InputNode).
|
|
|
|
% This may need to be reintroduced later by rl_liveness.m.
|
|
rl_block_opt__build_dag(copy(Output, Input) - _) -->
|
|
rl_block_opt__lookup_relation(Input, InputNode),
|
|
rl_block_opt__set_output_rel_id(Output, InputNode).
|
|
|
|
% This may need to be reintroduced later by rl_liveness.m.
|
|
rl_block_opt__build_dag(make_unique(Output, Input) - _) -->
|
|
rl_block_opt__lookup_relation(Input, InputNode),
|
|
rl_block_opt__set_output_rel_id(Output, InputNode).
|
|
|
|
rl_block_opt__build_dag(init(OutputRel) - _) -->
|
|
{ OutputRel = output_rel(Relation, _) },
|
|
dag_get_relation_info(Relation, OutputInfo),
|
|
{ OutputInfo = relation_info(_, Schema, _, _) },
|
|
rl_block_opt__add_dag_node([OutputRel], [], init(Schema), _).
|
|
|
|
rl_block_opt__build_dag(insert_tuple(Output, Input, Exprn) - _) -->
|
|
rl_block_opt__lookup_relation(Input, InputNode),
|
|
rl_block_opt__add_dag_node([Output], [InputNode],
|
|
insert_tuple(InputNode, Exprn), _).
|
|
|
|
rl_block_opt__build_dag(call(ProcName, Inputs, Outputs, _) - _) -->
|
|
rl_block_opt__lookup_relations(Inputs, InputNodes),
|
|
rl_block_opt__add_dag_node(Outputs, InputNodes,
|
|
call(ProcName, InputNodes), _).
|
|
|
|
rl_block_opt__build_dag(aggregate(Output, Input, Initial, Update) - _) -->
|
|
rl_block_opt__lookup_relation(Input, InputNode),
|
|
rl_block_opt__add_dag_node([Output], [InputNode],
|
|
aggregate(InputNode, Initial, Update), _).
|
|
|
|
rl_block_opt__build_dag(
|
|
add_index(Output, Input) - _) -->
|
|
rl_block_opt__lookup_relation(Input, InputNode),
|
|
rl_block_opt__set_output_rel_id(Output, InputNode).
|
|
|
|
rl_block_opt__build_dag(clear(_) - _) -->
|
|
{ error("rl_block_opt__build_dag: clear") }.
|
|
|
|
rl_block_opt__build_dag(unset(Output) - _) -->
|
|
rl_block_opt__unset_relation_node(Output).
|
|
|
|
rl_block_opt__build_dag(label(_) - _) -->
|
|
{ error("rl_block_opt__build_dag: label") }.
|
|
rl_block_opt__build_dag(conditional_goto(_, _) - _) -->
|
|
{ error("rl_block_opt__build_dag: conditional_goto") }.
|
|
rl_block_opt__build_dag(goto(_) - _) -->
|
|
{ error("rl_block_opt__build_dag: goto") }.
|
|
rl_block_opt__build_dag(comment - _) --> [].
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred rl_block_opt__lookup_relation(relation_id::in, output_id::out,
|
|
dag::in, dag::out) is det.
|
|
|
|
rl_block_opt__lookup_relation(RelationId, OutputId) -->
|
|
dag_get_rel_node_map(RelNodeMap0),
|
|
( { map__search(RelNodeMap0, RelationId, OutputId0) } ->
|
|
{ OutputId = OutputId0 }
|
|
;
|
|
(
|
|
dag_get_relation_info(RelationId,
|
|
relation_info(permanent(_), _, Indexes, _))
|
|
->
|
|
rl_block_opt__add_dag_node(
|
|
[output_rel(RelationId, Indexes)], [],
|
|
input(RelationId), OutputId)
|
|
;
|
|
{ string__format(
|
|
"rl_block_opt__lookup_relation: can't find relation %i",
|
|
[i(RelationId)], Msg) },
|
|
{ error(Msg) }
|
|
)
|
|
).
|
|
|
|
:- pred rl_block_opt__lookup_relations(list(relation_id)::in,
|
|
list(output_id)::out, dag::in, dag::out) is det.
|
|
|
|
rl_block_opt__lookup_relations([], []) --> [].
|
|
rl_block_opt__lookup_relations([Input | Inputs], [Node | Nodes]) -->
|
|
rl_block_opt__lookup_relation(Input, Node),
|
|
rl_block_opt__lookup_relations(Inputs, Nodes).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Set the location of a relation.
|
|
:- pred rl_block_opt__set_relation_output_id(relation_id::in, output_id::in,
|
|
dag::in, dag::out) is det.
|
|
|
|
rl_block_opt__set_relation_output_id(Relation, OutputId) -->
|
|
rl_block_opt__set_output_rel_id(output_rel(Relation, []), OutputId).
|
|
|
|
:- pred rl_block_opt__set_output_rel_id(output_rel::in, output_id::in,
|
|
dag::in, dag::out) is det.
|
|
|
|
rl_block_opt__set_output_rel_id(output_rel(Relation, Indexes),
|
|
OutputId) -->
|
|
rl_block_opt__unset_relation_node(Relation),
|
|
dag_get_node_info_map(NodeInfoMap0),
|
|
dag_get_rel_node_map(RelNodeMap0),
|
|
dag_get_output_loc_map(Locs),
|
|
{ map__lookup(Locs, OutputId, input_node(Node, InputIndex)) },
|
|
{ map__lookup(NodeInfoMap0, Node, NodeInfo0) },
|
|
{ NodeInfo0 = node_info(Instr, Outputs0) },
|
|
{ rl_block_opt__add_output(Outputs0, 0, InputIndex,
|
|
Relation, Indexes, Outputs) },
|
|
{ NodeInfo = node_info(Instr, Outputs) },
|
|
{ map__set(NodeInfoMap0, Node, NodeInfo, NodeInfoMap) },
|
|
{ map__set(RelNodeMap0, Relation, OutputId, RelNodeMap) },
|
|
dag_set_node_info_map(NodeInfoMap),
|
|
dag_set_rel_node_map(RelNodeMap).
|
|
|
|
:- pred rl_block_opt__add_output(list(output_node)::in, int::in, int::in,
|
|
relation_id::in, list(index_spec)::in, list(output_node)::out) is det.
|
|
|
|
rl_block_opt__add_output([], _, _, _, _, _) :-
|
|
error("rl_block_opt:rl_block_opt__add_output").
|
|
rl_block_opt__add_output([OutputRel0 | Outputs0], Index0, TheIndex,
|
|
Relation, RelIndex, [OutputRel | Outputs]) :-
|
|
( Index0 = TheIndex ->
|
|
OutputRel0 = output_node(Schema, IndexSpec0, Rels0),
|
|
list__append(IndexSpec0, RelIndex, IndexSpec),
|
|
set__insert(Rels0, Relation, Rels),
|
|
OutputRel = output_node(Schema, IndexSpec, Rels),
|
|
Outputs = Outputs0
|
|
;
|
|
OutputRel = OutputRel0,
|
|
Index = Index0 + 1,
|
|
rl_block_opt__add_output(Outputs0, Index, TheIndex,
|
|
Relation, RelIndex, Outputs)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Remove knowledge about a relation's location from the dag
|
|
% when it is `unset'.
|
|
:- pred rl_block_opt__unset_relation_node(relation_id::in,
|
|
dag::in, dag::out) is det.
|
|
|
|
rl_block_opt__unset_relation_node(RelationId) -->
|
|
dag_get_rel_node_map(RelNodeMap0),
|
|
( { map__search(RelNodeMap0, RelationId, OutputId) } ->
|
|
dag_get_output_loc_map(Locs),
|
|
{ map__lookup(Locs, OutputId, InputRel) },
|
|
{ InputRel = input_node(Node, _) },
|
|
dag_get_node_info_map(NodeInfoMap0),
|
|
{ map__lookup(NodeInfoMap0, Node, NodeInfo0) },
|
|
{ NodeInfo0 = node_info(Instr, Outputs0) },
|
|
{ DeleteOutput = (pred(Output0::in, Output::out) is det :-
|
|
Output0 = output_node(Schema, Index, Set0),
|
|
set__delete(Set0, RelationId, Set),
|
|
Output = output_node(Schema, Index, Set)
|
|
) },
|
|
{ list__map(DeleteOutput, Outputs0, Outputs) },
|
|
{ NodeInfo = node_info(Instr, Outputs) },
|
|
{ map__det_update(NodeInfoMap0, Node, NodeInfo, NodeInfoMap) },
|
|
{ map__delete(RelNodeMap0, RelationId, RelNodeMap) },
|
|
dag_set_rel_node_map(RelNodeMap),
|
|
dag_set_node_info_map(NodeInfoMap)
|
|
;
|
|
[]
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Add an instruction to the dag.
|
|
:- pred rl_block_opt__add_dag_node(list(output_rel)::in, list(output_id)::in,
|
|
instr::in, node_id::out, dag::in, dag::out) is det.
|
|
|
|
rl_block_opt__add_dag_node(OutputRels, InputNodes, Instr, MatchingNode) -->
|
|
{ list__map(rl__output_rel_relation, OutputRels, Outputs) },
|
|
list__foldl(rl_block_opt__unset_relation_node, Outputs),
|
|
|
|
% Search for a matching node in the DAG.
|
|
dag_get_exprns(Exprns),
|
|
( { rl_block_opt__find_matching_node(Exprns, Instr, MatchingNode0) } ->
|
|
{ MatchingNode = MatchingNode0 },
|
|
rl_block_opt__add_node_outputs(MatchingNode, OutputRels)
|
|
;
|
|
rl_block_opt__do_add_node(OutputRels, Instr, MatchingNode)
|
|
),
|
|
|
|
rl_block_opt__set_relation_nodes(MatchingNode, Outputs),
|
|
rl_block_opt__add_node_dependencies(MatchingNode, InputNodes).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Do common subexpression elimination.
|
|
:- pred rl_block_opt__find_matching_node(exprns::in,
|
|
instr::in, node_id::out) is semidet.
|
|
|
|
rl_block_opt__find_matching_node(Exprns, Instr, Node) :-
|
|
% Don't do cse on inits.
|
|
% XXX should we? yes - but make init nodes non-shared later.
|
|
Instr \= init(_),
|
|
rl_block_opt__find_matching_node_2(Exprns, Instr, Node).
|
|
|
|
:- pred rl_block_opt__find_matching_node_2(assoc_list(instr, node_id)::in,
|
|
instr::in, node_id::out) is semidet.
|
|
|
|
rl_block_opt__find_matching_node_2([InstrToCheck - Node | InstrsToCheck],
|
|
Instr, MatchingNode) :-
|
|
% Could do some more advanced equivalence checking here.
|
|
( InstrToCheck = Instr ->
|
|
MatchingNode = Node
|
|
;
|
|
rl_block_opt__find_matching_node_2(InstrsToCheck, Instr,
|
|
MatchingNode)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Register the locations of the outputs of an instruction.
|
|
:- pred rl_block_opt__add_node_outputs(node_id::in, list(output_rel)::in,
|
|
dag::in, dag::out) is det.
|
|
|
|
rl_block_opt__add_node_outputs(MatchingNode, OutputRels) -->
|
|
dag_get_node_info_map(NodeInfoMap0),
|
|
{ map__lookup(NodeInfoMap0, MatchingNode, NodeInfo0) },
|
|
{ NodeInfo0 = node_info(Instr0, Outputs0) },
|
|
{ rl_block_opt__do_add_node_outputs(Outputs0, OutputRels, Outputs) },
|
|
{ NodeInfo = node_info(Instr0, Outputs) },
|
|
{ map__det_update(NodeInfoMap0, MatchingNode, NodeInfo, NodeInfoMap) },
|
|
dag_set_node_info_map(NodeInfoMap).
|
|
|
|
:- pred rl_block_opt__do_add_node_outputs(list(output_node)::in,
|
|
list(output_rel)::in, list(output_node)::out) is det.
|
|
|
|
rl_block_opt__do_add_node_outputs([], [], []).
|
|
rl_block_opt__do_add_node_outputs([], [_|_], _) :-
|
|
error("rl_block_opt__do_add_node_outputs").
|
|
rl_block_opt__do_add_node_outputs([_|_], [], _) :-
|
|
error("rl_block_opt__do_add_node_outputs").
|
|
rl_block_opt__do_add_node_outputs([Output0 | Outputs0],
|
|
[output_rel(NewOutput, NewIndexes0) | NewOutputs],
|
|
[Output | Outputs]) :-
|
|
Output0 = output_node(Schema, Indexes0, OutputSet0),
|
|
list__delete_elems(NewIndexes0, Indexes0, NewIndexes),
|
|
list__append(Indexes0, NewIndexes, Indexes),
|
|
set__insert(OutputSet0, NewOutput, OutputSet),
|
|
Output = output_node(Schema, Indexes, OutputSet),
|
|
rl_block_opt__do_add_node_outputs(Outputs0, NewOutputs, Outputs).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred rl_block_opt__do_add_node(list(output_rel)::in, instr::in,
|
|
node_id::out, dag::in, dag::out) is det.
|
|
|
|
rl_block_opt__do_add_node(OutputRels, Instr, MatchingNode) -->
|
|
dag_get_next_node_id(MatchingNode),
|
|
list__map_foldl(rl_block_opt__init_output_node,
|
|
OutputRels, Outputs0),
|
|
{ NodeInfo = node_info(Instr, Outputs0) },
|
|
dag_get_node_info_map(NodeInfoMap0),
|
|
{ map__det_insert(NodeInfoMap0, MatchingNode,
|
|
NodeInfo, NodeInfoMap) },
|
|
dag_set_node_info_map(NodeInfoMap),
|
|
dag_get_exprns(Exprns0),
|
|
{ Exprns = [Instr - MatchingNode | Exprns0] },
|
|
dag_set_exprns(Exprns).
|
|
|
|
:- pred rl_block_opt__init_output_node(output_rel::in, output_node::out,
|
|
dag::in, dag::out) is det.
|
|
|
|
rl_block_opt__init_output_node(output_rel(OutputRel, Index), OutputRelInfo) -->
|
|
dag_get_relation_info(OutputRel, RelInfo),
|
|
{ RelInfo = relation_info(_, Schema, _, _) },
|
|
{ set__singleton_set(Outputs, OutputRel) },
|
|
{ OutputRelInfo = output_node(Schema, Index, Outputs) }.
|
|
|
|
% Set the location of the relations evaluated by the given node.
|
|
:- pred rl_block_opt__set_relation_nodes(node_id::in, list(relation_id)::in,
|
|
dag::in, dag::out) is det.
|
|
|
|
rl_block_opt__set_relation_nodes(MatchingNode, OutputRels) -->
|
|
list__foldl2(rl_block_opt__set_relation_node(MatchingNode),
|
|
OutputRels, 0, _).
|
|
|
|
:- pred rl_block_opt__set_relation_node(node_id::in, relation_id::in,
|
|
int::in, int::out, dag::in, dag::out) is det.
|
|
|
|
rl_block_opt__set_relation_node(NodeId, Output, Index0, Index) -->
|
|
dag_get_output_assign_map(Assign0),
|
|
{ InputRel = input_node(NodeId, Index0) },
|
|
( { map__search(Assign0, InputRel, OutputId0) } ->
|
|
{ OutputId = OutputId0 }
|
|
;
|
|
dag_get_next_output_id(OutputId),
|
|
{ map__det_insert(Assign0, InputRel, OutputId, Assign) },
|
|
dag_set_output_assign_map(Assign),
|
|
dag_get_output_loc_map(Locs0),
|
|
{ map__det_insert(Locs0, OutputId, InputRel, Locs) },
|
|
dag_set_output_loc_map(Locs)
|
|
),
|
|
{ Index = Index0 + 1 },
|
|
dag_get_rel_node_map(RelNodeMap0),
|
|
{ map__set(RelNodeMap0, Output, OutputId, RelNodeMap) },
|
|
dag_set_rel_node_map(RelNodeMap).
|
|
|
|
:- pred rl_block_opt__add_node_dependencies(node_id::in, list(output_id)::in,
|
|
dag::in, dag::out) is det.
|
|
|
|
rl_block_opt__add_node_dependencies(MatchingNode, InputNodes) -->
|
|
dag_get_output_use_map(Uses0),
|
|
{ AddDepArc = (pred(OutputId::in, UseMap0::in, UseMap::out) is det :-
|
|
multi_map__set(UseMap0, OutputId, MatchingNode, UseMap)
|
|
) },
|
|
{ list__foldl(AddDepArc, InputNodes, Uses0, Uses) },
|
|
dag_set_output_use_map(Uses),
|
|
|
|
dag_get_node_dep_map(Dep0),
|
|
{ MMapSet = (pred(Node::in, Depend0::in, Depend::out) is det :-
|
|
multi_map__set(Depend0, MatchingNode, Node, Depend)
|
|
) },
|
|
{ list__foldl(MMapSet, InputNodes, Dep0, Dep) },
|
|
dag_set_node_dep_map(Dep).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Keep rewriting until nothing changes.
|
|
:- pred rl_block_opt__rewrite_dag(dag::in, dag::out) is det.
|
|
|
|
rl_block_opt__rewrite_dag -->
|
|
dag_get_node_info_map(NodeInfoMap),
|
|
{ map__keys(NodeInfoMap, Nodes) },
|
|
rl_block_opt__rewrite_nodes(Nodes, no, Changed),
|
|
( { Changed = yes } ->
|
|
rl_block_opt__rewrite_dag
|
|
;
|
|
[]
|
|
).
|
|
|
|
:- pred rl_block_opt__rewrite_nodes(list(node_id)::in, bool::in, bool::out,
|
|
dag::in, dag::out) is det.
|
|
|
|
rl_block_opt__rewrite_nodes([], Changed, Changed) --> [].
|
|
rl_block_opt__rewrite_nodes([Node | Nodes], Changed0, Changed) -->
|
|
dag_get_node_info_map(NodeInfoMap0),
|
|
|
|
% This node may have been removed while processing an earlier node.
|
|
( { map__search(NodeInfoMap0, Node, NodeInfo) } ->
|
|
rl_block_opt__rewrite_node(Node, NodeInfo, Changed1),
|
|
{ bool__or(Changed0, Changed1, Changed2) }
|
|
;
|
|
{ Changed2 = Changed0 }
|
|
),
|
|
rl_block_opt__rewrite_nodes(Nodes, Changed2, Changed).
|
|
|
|
:- pred rl_block_opt__rewrite_node(node_id::in, node_info::in,
|
|
bool::out, dag::in, dag::out) is det.
|
|
|
|
rl_block_opt__rewrite_node(Node, NodeInfo0, Changed) -->
|
|
dag_get_output_loc_map(Locs),
|
|
dag_get_node_info_map(NodeInfoMap0),
|
|
{ NodeInfo0 = node_info(Instr, OutputRels0) },
|
|
dag_get_flags(Flags),
|
|
dag_get_rl_opt_info(RLInfo),
|
|
{ rl_opt_info_get_module_info(ModuleInfo, RLInfo, _) },
|
|
{ module_info_globals(ModuleInfo, Globals) },
|
|
{ globals__lookup_bool_option(Globals,
|
|
optimize_rl_index, OptimizeIndex) },
|
|
(
|
|
{ OptimizeIndex = yes },
|
|
|
|
% Look for a union and a difference which can be
|
|
% changed into a union_diff.
|
|
{ Instr = union(UnionInputLocs, _) },
|
|
|
|
{ OutputRels0 = [OutputRel0] },
|
|
{ OutputRel0 = output_node(Schema, _, _) },
|
|
|
|
% B-tree indexing a zero arity relation would be pretty silly.
|
|
{ Schema \= [] },
|
|
|
|
{ list__member(add_uniondiff, Flags) },
|
|
|
|
{
|
|
UnionInputLocs = [DiffLoc0, IOLoc0],
|
|
rl_block_opt__get_difference_info(NodeInfoMap0, Locs,
|
|
IOLoc0, DiffLoc0, DiffNode0,
|
|
NewRel0, DiffOutput0)
|
|
->
|
|
DiffLoc = DiffLoc0,
|
|
DiffNode = DiffNode0,
|
|
IOLoc = IOLoc0,
|
|
NewRel = NewRel0,
|
|
DiffOutput = DiffOutput0
|
|
;
|
|
UnionInputLocs = [IOLoc, DiffLoc],
|
|
rl_block_opt__get_difference_info(NodeInfoMap0, Locs,
|
|
IOLoc, DiffLoc, DiffNode, NewRel, DiffOutput)
|
|
}
|
|
->
|
|
rl_block_opt__ensure_index(IOLoc, IndexSpec),
|
|
{ NewInstr = union_diff(IOLoc, NewRel, IndexSpec) },
|
|
rl_block_opt__update_node(Node, NewInstr, [IOLoc, NewRel],
|
|
[OutputRel0, DiffOutput]),
|
|
|
|
% Point users of the difference output to its new location.
|
|
% The union output is still in the same place.
|
|
rl_block_opt__rename_node(DiffNode,
|
|
DiffLoc, input_node(Node, 1)),
|
|
rl_block_opt__remove_node(DiffNode),
|
|
{ Changed = yes }
|
|
;
|
|
% If there is a single use of one of the outputs of this node
|
|
% and the use is a project with a single expression (not a
|
|
% merged project produced below), try to merge the project
|
|
% into this operation.
|
|
{ Instr = project(Input, Goals0, ProjType) },
|
|
rl_block_opt__get_single_projects(Node, 0, no, FoundSingle,
|
|
Goals0, Goals, OutputRels0, OutputRels)
|
|
->
|
|
{ Changed = FoundSingle },
|
|
( { FoundSingle = yes } ->
|
|
{ Instr1 = project(Input, Goals, ProjType) },
|
|
{ NodeInfo = node_info(Instr1, OutputRels) },
|
|
dag_get_node_info_map(NodeInfoMap1),
|
|
{ map__det_update(NodeInfoMap1, Node,
|
|
NodeInfo, NodeInfoMap) },
|
|
dag_set_node_info_map(NodeInfoMap)
|
|
;
|
|
[]
|
|
)
|
|
;
|
|
{ list__member(merge_output_projections, Flags) }
|
|
->
|
|
% If there are multiple projections of this node,
|
|
% combine them into one instruction. If the conditions
|
|
% have a key range, we insist that they all have the
|
|
% same one.
|
|
rl_block_opt__merge_output_projections(Node, Changed)
|
|
;
|
|
{ Changed = no }
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Check that the given node holds a difference instruction,
|
|
% and extract some information from the instruction.
|
|
:- pred rl_block_opt__get_difference_info(map(node_id, node_info)::in,
|
|
output_loc_map::in, output_id::in, output_id::in,
|
|
node_id::out, output_id::out, output_node::out) is semidet.
|
|
|
|
rl_block_opt__get_difference_info(NodeInfoMap0, LocMap,
|
|
IOLoc, DiffOutputId, DiffNode, NewRel, DiffOutput) :-
|
|
map__lookup(LocMap, DiffOutputId, DiffLoc),
|
|
DiffLoc = input_node(DiffNode, _),
|
|
map__lookup(NodeInfoMap0, DiffNode, DiffNodeInfo),
|
|
DiffNodeInfo = node_info(DiffInstr, DiffOutputs),
|
|
DiffOutputs = [DiffOutput],
|
|
DiffInstr = difference(NewRel, IOLoc, _).
|
|
|
|
% Make sure that the given input to this node has a unique
|
|
% B-tree index on all its attributes.
|
|
:- pred rl_block_opt__ensure_index(output_id::in, index_spec::out,
|
|
dag::in, dag::out) is det.
|
|
|
|
rl_block_opt__ensure_index(OutputId, TheIndex) -->
|
|
rl_block_opt__update_node_index(rl_block_opt__add_full_index,
|
|
OutputId, AddedIndexes),
|
|
{ AddedIndexes = [TheIndex0] ->
|
|
TheIndex = TheIndex0
|
|
;
|
|
error("rl_block_opt__ensure_index")
|
|
}.
|
|
|
|
:- pred rl_block_opt__add_full_index(list(type)::in, list(index_spec)::in,
|
|
list(index_spec)::out, list(index_spec)::out) is det.
|
|
|
|
rl_block_opt__add_full_index(Schema, Indexes0, Indexes, [TheIndex]) :-
|
|
rl__attr_list(Schema, Attrs),
|
|
( rl_block_opt__contains_full_index(Attrs, Indexes0, TheIndex0) ->
|
|
TheIndex = TheIndex0,
|
|
Indexes = Indexes0
|
|
;
|
|
TheIndex = index_spec(unique_B_tree, Attrs),
|
|
Indexes = [TheIndex | Indexes0]
|
|
).
|
|
|
|
:- pred rl_block_opt__contains_full_index(list(int)::in, list(index_spec)::in,
|
|
index_spec::out) is semidet.
|
|
|
|
rl_block_opt__contains_full_index(Attrs, [Index | Indexes], TheIndex) :-
|
|
Index = index_spec(Type, Attrs0),
|
|
(
|
|
Type = unique_B_tree,
|
|
list__sort_and_remove_dups(Attrs0, Attrs)
|
|
->
|
|
TheIndex = Index
|
|
;
|
|
rl_block_opt__contains_full_index(Attrs, Indexes, TheIndex)
|
|
).
|
|
|
|
:- pred rl_block_opt__add_index_to_node(output_id::in,
|
|
list(index_spec)::in, dag::in, dag::out) is det.
|
|
|
|
rl_block_opt__add_index_to_node(OutputId, Index) -->
|
|
rl_block_opt__update_node_index(
|
|
rl_block_opt__add_specific_index(Index), OutputId, _).
|
|
|
|
:- pred rl_block_opt__add_specific_index(list(index_spec)::in, list(type)::in,
|
|
list(index_spec)::in, list(index_spec)::out,
|
|
list(index_spec)::out) is det.
|
|
|
|
rl_block_opt__add_specific_index(AddedIndexes0, _,
|
|
Indexes0, Indexes, AddedIndexes) :-
|
|
list__delete_elems(AddedIndexes0, Indexes0, AddedIndexes),
|
|
list__append(Indexes0, AddedIndexes, Indexes).
|
|
|
|
:- pred rl_block_opt__update_node_index(
|
|
pred(list(type), list(index_spec), list(index_spec), list(index_spec)),
|
|
output_id, list(index_spec), dag, dag).
|
|
:- mode rl_block_opt__update_node_index(pred(in, in, out, out) is det,
|
|
in, out, in, out) is det.
|
|
|
|
rl_block_opt__update_node_index(Update, OutputId, AddedIndexes) -->
|
|
dag_get_output_loc_map(Locs),
|
|
dag_get_node_info_map(NodeInfoMap0),
|
|
{ map__lookup(Locs, OutputId, InputRel) },
|
|
{ InputRel = input_node(InputNode, OutputNo) },
|
|
{ map__lookup(NodeInfoMap0, InputNode, InputInfo0) },
|
|
{ InputInfo0 = node_info(Instr, OutputRels0) },
|
|
( { rl_block_opt__add_index_to_input(Instr, OutputNo, InputLoc) } ->
|
|
rl_block_opt__update_node_index(Update, InputLoc, AddedIndexes)
|
|
;
|
|
{ list__index0_det(OutputRels0, OutputNo, OutputRel0) },
|
|
{ OutputRel0 = output_node(Schema, Index0, RelationIds0) },
|
|
{ call(Update, Schema, Index0, Index, AddedIndexes) },
|
|
|
|
{ OutputRel = output_node(Schema, Index, RelationIds0) },
|
|
{ N = OutputNo + 1 },
|
|
{ list__replace_nth(OutputRels0, N, OutputRel, OutputRels1) ->
|
|
OutputRels = OutputRels1
|
|
;
|
|
error(
|
|
"rl_block_opt__ensure_index: list__replace_nth failed")
|
|
},
|
|
{ InputInfo = node_info(Instr, OutputRels) },
|
|
{ map__det_update(NodeInfoMap0, InputNode,
|
|
InputInfo, NodeInfoMap) },
|
|
dag_set_node_info_map(NodeInfoMap)
|
|
).
|
|
|
|
:- pred rl_block_opt__get_node_indexes(output_id::in, list(index_spec)::out,
|
|
dag::in, dag::out) is det.
|
|
|
|
rl_block_opt__get_node_indexes(OutputId, Indexes) -->
|
|
dag_get_output_loc_map(Locs),
|
|
{ map__lookup(Locs, OutputId, input_node(InputNode, OutputNo)) },
|
|
dag_get_node_info_map(NodeInfoMap),
|
|
{ map__lookup(NodeInfoMap, InputNode, NodeInfo) },
|
|
{ NodeInfo = node_info(Instr, NodeOutputs) },
|
|
|
|
( { rl_block_opt__add_index_to_input(Instr, OutputNo, InputLoc) } ->
|
|
rl_block_opt__get_node_indexes(InputLoc, Indexes)
|
|
;
|
|
{ list__index0_det(NodeOutputs, OutputNo, NodeOutput) },
|
|
{ NodeOutput = output_node(_, Indexes, _) }
|
|
).
|
|
|
|
% It doesn't make much sense to add an index after a union_diff
|
|
% or insert has destructively updated a relation - the index
|
|
% should be added to the input relation.
|
|
:- pred rl_block_opt__add_index_to_input(instr::in, int::in,
|
|
output_id::out) is semidet.
|
|
|
|
rl_block_opt__add_index_to_input(Instr, OutputNo, InputLoc) :-
|
|
( Instr = union_diff(_, InputLoc, _)
|
|
; Instr = insert(_, InputLoc, _)
|
|
),
|
|
OutputNo = 0.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Update the nodes instruction, inputs and outputs.
|
|
:- pred rl_block_opt__update_node(node_id::in, instr::in, list(output_id)::in,
|
|
list(output_node)::in, dag::in, dag::out) is det.
|
|
|
|
rl_block_opt__update_node(Node, Instr, InputLocs, OutputRels) -->
|
|
dag_get_node_info_map(NodeInfoMap0),
|
|
{ map__det_update(NodeInfoMap0, Node, node_info(Instr, OutputRels),
|
|
NodeInfoMap) },
|
|
dag_set_node_info_map(NodeInfoMap),
|
|
|
|
% Remove all old dependencies.
|
|
dag_get_output_use_map(Uses0),
|
|
dag_get_node_dep_map(Dep0),
|
|
( { multi_map__search(Dep0, Node, UsedNodes) } ->
|
|
{ multi_map__det_replace(Dep0, Node, [], Dep) },
|
|
{ list__foldl(
|
|
(pred(Use::in, UseMap0::in, UseMap::out) is det :-
|
|
multi_map__delete(UseMap0, Use, Node, UseMap)
|
|
), UsedNodes, Uses0, Uses) },
|
|
dag_set_output_use_map(Uses),
|
|
dag_set_node_dep_map(Dep)
|
|
;
|
|
[]
|
|
),
|
|
rl_block_opt__add_node_dependencies(Node, InputLocs).
|
|
|
|
:- pred rl_block_opt__rename_node(node_id::in, output_id::in, input_node::in,
|
|
dag::in, dag::out) is det.
|
|
|
|
rl_block_opt__rename_node(OldNode, OldLoc, InputRel) -->
|
|
dag_get_output_assign_map(Assign0),
|
|
{ map__set(Assign0, InputRel, OldLoc, Assign) },
|
|
dag_set_output_assign_map(Assign),
|
|
dag_get_output_loc_map(Locs0),
|
|
{ map__det_update(Locs0, OldLoc, InputRel, Locs) },
|
|
dag_set_output_loc_map(Locs),
|
|
|
|
{ InputRel = input_node(NewNode, _) },
|
|
|
|
dag_get_node_dep_map(Deps),
|
|
( { map__search(Deps, OldNode, UsedNodes) } ->
|
|
dag_get_output_use_map(Uses0),
|
|
{ list__foldl(
|
|
(pred(Use::in, UseMap0::in, UseMap::out) is det :-
|
|
multi_map__delete(UseMap0,
|
|
Use, OldNode, UseMap1),
|
|
multi_map__set(UseMap1, Use, NewNode, UseMap)
|
|
), UsedNodes, Uses0, Uses) },
|
|
dag_set_output_use_map(Uses)
|
|
;
|
|
[]
|
|
).
|
|
|
|
:- pred rl_block_opt__remove_node(node_id::in, dag::in, dag::out) is det.
|
|
|
|
rl_block_opt__remove_node(Node) -->
|
|
dag_get_node_info_map(NodeInfoMap0),
|
|
{ map__delete(NodeInfoMap0, Node, NodeInfoMap) },
|
|
dag_set_node_info_map(NodeInfoMap).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% If there are multiple projections of the current node, they
|
|
% can all be merged into one.
|
|
% Problems with this
|
|
% - all except one of the outputs must be materialised.
|
|
% - the materialised outputs aren't produced until the stream
|
|
% is evaluated. This may cause problems if a user of a materialised
|
|
% output comes before the user of the non-materialised output. In
|
|
% the worst case, it could cause a circular dependency.
|
|
% The quick hack solution is to just materialise all outputs of a
|
|
% projection if there are more than one.
|
|
:- pred rl_block_opt__merge_output_projections(node_id::in, bool::out,
|
|
dag::in, dag::out) is det.
|
|
|
|
rl_block_opt__merge_output_projections(Node, no) -->
|
|
dag_get_node_info_map(NodeInfoMap),
|
|
{ map__lookup(NodeInfoMap, Node, NodeInfo) },
|
|
{ NodeInfo = node_info(_Instr, OutputNodes) },
|
|
rl_block_opt__find_output_project_nodes(Node, 0, OutputNodes,
|
|
[], OutputProjectNodes),
|
|
{ list__map(rl_block_opt__partition_project_outputs,
|
|
OutputProjectNodes, Partitions0) },
|
|
{ list__condense(Partitions0, Partitions) },
|
|
list__foldl(rl_block_opt__produce_merged_projection, Partitions).
|
|
|
|
:- type output_projection
|
|
---> output_projection(
|
|
node_id, % node containing the projection
|
|
instr,
|
|
project_type,
|
|
list(output_node)
|
|
).
|
|
|
|
% A partition of the output projections using the same
|
|
% access method (filter / key-range) on the input.
|
|
:- type project_partition
|
|
---> project_partition(
|
|
project_type,
|
|
list(output_projection)
|
|
).
|
|
|
|
% Work out which of the uses of the outputs of the current node
|
|
% are projections.
|
|
:- pred rl_block_opt__find_output_project_nodes(node_id::in, int::in,
|
|
list(output_node)::in, list(list(output_projection))::in,
|
|
list(list(output_projection))::out, dag::in, dag::out) is det.
|
|
|
|
rl_block_opt__find_output_project_nodes(_, _, [],
|
|
OutputProjns, OutputProjns) --> [].
|
|
rl_block_opt__find_output_project_nodes(NodeId, Index,
|
|
[_OutputNode | OutputNodes], OutputProjns0, OutputProjns) -->
|
|
dag_get_output_assign_map(Assign),
|
|
( { map__search(Assign, input_node(NodeId, Index), OutputId) } ->
|
|
dag_get_output_use_map(UseMap),
|
|
( { map__search(UseMap, OutputId, UsingNodes) } ->
|
|
rl_block_opt__find_output_project_nodes_2(UsingNodes,
|
|
[], NodeOutputProjnList),
|
|
|
|
% If there are zero or one projections,
|
|
% we have nothing to do.
|
|
( { NodeOutputProjnList = [_, _ | _] } ->
|
|
{ OutputProjns1 =
|
|
[NodeOutputProjnList | OutputProjns0] }
|
|
;
|
|
{ OutputProjns1 = OutputProjns0 }
|
|
)
|
|
;
|
|
{ OutputProjns1 = OutputProjns0 }
|
|
)
|
|
;
|
|
{ OutputProjns1 = OutputProjns0 }
|
|
),
|
|
{ NextIndex = Index + 1 },
|
|
rl_block_opt__find_output_project_nodes(NodeId, NextIndex,
|
|
OutputNodes, OutputProjns1, OutputProjns).
|
|
|
|
:- pred rl_block_opt__find_output_project_nodes_2(list(node_id)::in,
|
|
list(output_projection)::in, list(output_projection)::out,
|
|
dag::in, dag::out) is det.
|
|
|
|
rl_block_opt__find_output_project_nodes_2([],
|
|
OutputProjns, OutputProjns) --> [].
|
|
rl_block_opt__find_output_project_nodes_2([UsingNode | UsingNodes],
|
|
OutputProjns0, OutputProjns) -->
|
|
dag_get_node_info_map(NodeInfoMap),
|
|
{ map__lookup(NodeInfoMap, UsingNode, NodeInfo) },
|
|
{ NodeInfo = node_info(Instr, Outputs) },
|
|
{
|
|
Instr = project(_, OutputGoals, ProjType),
|
|
|
|
% Don't merge in projections where the output is independent
|
|
% of the input - these don't require a traversal of the input
|
|
% at all, and are generated by rl_out.m as an insertion of the
|
|
% generated tuple into the output if the input is non-empty
|
|
\+ (
|
|
OutputGoals = [OutputGoal],
|
|
rl__goal_is_independent_of_input(one, OutputGoal)
|
|
),
|
|
\+ (
|
|
list__member(OtherOutputProjn, OutputProjns0),
|
|
OtherOutputProjn = output_projection(UsingNode,
|
|
_, _, _)
|
|
)
|
|
->
|
|
OutputProjn = output_projection(UsingNode,
|
|
Instr, ProjType, Outputs),
|
|
OutputProjns1 = [OutputProjn | OutputProjns0]
|
|
;
|
|
OutputProjns1 = OutputProjns0
|
|
},
|
|
rl_block_opt__find_output_project_nodes_2(UsingNodes,
|
|
OutputProjns1, OutputProjns).
|
|
|
|
% Partition the projections according to the indexes / key ranges
|
|
% used to access the inputs.
|
|
:- pred rl_block_opt__partition_project_outputs(list(output_projection)::in,
|
|
list(project_partition)::out) is det.
|
|
|
|
rl_block_opt__partition_project_outputs(Projects, Partitions) :-
|
|
rl_block_opt__partition_project_outputs_2(Projects, [], Partitions).
|
|
|
|
:- pred rl_block_opt__partition_project_outputs_2(list(output_projection)::in,
|
|
list(project_partition)::in, list(project_partition)::out) is det.
|
|
|
|
rl_block_opt__partition_project_outputs_2([], Partitions, Partitions).
|
|
rl_block_opt__partition_project_outputs_2([Project | Projects],
|
|
Partitions0, Partitions) :-
|
|
rl_block_opt__add_to_partitions(Project, Partitions0, Partitions1),
|
|
rl_block_opt__partition_project_outputs_2(Projects,
|
|
Partitions1, Partitions).
|
|
|
|
:- pred rl_block_opt__add_to_partitions(output_projection::in,
|
|
list(project_partition)::in, list(project_partition)::out) is det.
|
|
|
|
rl_block_opt__add_to_partitions(Project, [],
|
|
[project_partition(ProjType, [Project])]) :-
|
|
Project = output_projection(_, _, ProjType, _).
|
|
rl_block_opt__add_to_partitions(Project, [Partition0 | Partitions0],
|
|
[Partition | Partitions]) :-
|
|
Project = output_projection(_, _, ProjType, _),
|
|
Partition0 = project_partition(PartitionType, Projects0),
|
|
( PartitionType = ProjType ->
|
|
Partition = project_partition(PartitionType,
|
|
[Project | Projects0]),
|
|
Partitions = Partitions0
|
|
;
|
|
Partition = Partition0,
|
|
rl_block_opt__add_to_partitions(Project,
|
|
Partitions0, Partitions)
|
|
).
|
|
|
|
% Put a node in the dag for the merged output projection.
|
|
:- pred rl_block_opt__produce_merged_projection(project_partition::in,
|
|
dag::in, dag::out) is det.
|
|
|
|
rl_block_opt__produce_merged_projection(project_partition(_, Projects)) -->
|
|
dag_get_next_node_id(NewNode),
|
|
rl_block_opt__collect_project_outputs(NewNode, 0, Projects,
|
|
[], OutputNodes0, [], Goals0),
|
|
{ list__reverse(OutputNodes0, OutputNodes) },
|
|
{ list__reverse(Goals0, Goals) },
|
|
(
|
|
{ Projects = [Project | _] },
|
|
{ Project = output_projection(_,
|
|
project(Input, _, ProjType), _, _) }
|
|
->
|
|
{ Instr = project(Input, Goals, ProjType) },
|
|
{ NodeInfo = node_info(Instr, OutputNodes) },
|
|
dag_get_node_info_map(NodeInfoMap0),
|
|
{ map__det_insert(NodeInfoMap0, NewNode,
|
|
NodeInfo, NodeInfoMap) },
|
|
dag_set_node_info_map(NodeInfoMap),
|
|
|
|
dag_get_output_use_map(Uses0),
|
|
{ multi_map__lookup(Uses0, Input, InputUses0) },
|
|
{ list__filter((pred(NodeId::in) is semidet :-
|
|
\+ (
|
|
list__member(Project, Projects),
|
|
Project = output_projection(NodeId, _, _, _)
|
|
)
|
|
), InputUses0, InputUses) },
|
|
{ multi_map__det_replace(Uses0, Input,
|
|
[NewNode | InputUses], Uses) },
|
|
dag_set_output_use_map(Uses),
|
|
|
|
dag_get_node_dep_map(Deps0),
|
|
{ multi_map__det_insert(Deps0, NewNode, Input, Deps) },
|
|
dag_set_node_dep_map(Deps)
|
|
;
|
|
{ error("rl_block_opt__produce_merged_projection") }
|
|
).
|
|
|
|
:- pred rl_block_opt__collect_project_outputs(node_id::in, int::in,
|
|
list(output_projection)::in, list(output_node)::in,
|
|
list(output_node)::out, list(rl_goal)::in, list(rl_goal)::out,
|
|
dag::in, dag::out) is det.
|
|
|
|
rl_block_opt__collect_project_outputs(_, _, [],
|
|
Outputs, Outputs, Goals, Goals) --> [].
|
|
rl_block_opt__collect_project_outputs(NewNode, Index0, [Project | Projects],
|
|
Outputs0, Outputs, Goals0, Goals) -->
|
|
{ Project = output_projection(ProjNode, Instr, _, OutputNodes) },
|
|
( { Instr = project(_, ProjGoals0, _) } ->
|
|
{ list__reverse(ProjGoals0, ProjGoals) },
|
|
{ list__append(ProjGoals, Goals0, Goals1) }
|
|
;
|
|
{ error("rl_block_opt__collect_project_outputs") }
|
|
),
|
|
|
|
rl_block_opt__collect_project_outputs_2(ProjNode, 0, OutputNodes,
|
|
NewNode, Index0, Index1, Outputs0, Outputs1),
|
|
rl_block_opt__remove_node(ProjNode),
|
|
rl_block_opt__collect_project_outputs(NewNode, Index1, Projects,
|
|
Outputs1, Outputs, Goals1, Goals).
|
|
|
|
:- pred rl_block_opt__collect_project_outputs_2(node_id::in, int::in,
|
|
list(output_node)::in, node_id::in, int::in, int::out,
|
|
list(output_node)::in, list(output_node)::out,
|
|
dag::in, dag::out) is det.
|
|
|
|
rl_block_opt__collect_project_outputs_2(_, _, [],
|
|
_, Index, Index, Outputs, Outputs) --> [].
|
|
rl_block_opt__collect_project_outputs_2(ProjNode, OutputIndex0,
|
|
[OutputNode | OutputNodes], NewNode, NewIndex0, NewIndex,
|
|
NewOutputs0, NewOutputs) -->
|
|
|
|
% Tell other instructions about the new location of this output.
|
|
dag_get_output_assign_map(Assign),
|
|
{ map__lookup(Assign, input_node(ProjNode, OutputIndex0),
|
|
OldOutputLoc) },
|
|
rl_block_opt__rename_node(ProjNode, OldOutputLoc,
|
|
input_node(NewNode, NewIndex0)),
|
|
|
|
{ OutputIndex = OutputIndex0 + 1 },
|
|
{ NewIndex1 = NewIndex0 + 1 },
|
|
rl_block_opt__collect_project_outputs_2(ProjNode, OutputIndex,
|
|
OutputNodes, NewNode, NewIndex1, NewIndex,
|
|
[OutputNode | NewOutputs0], NewOutputs).
|
|
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Find projections of the outputs of this node which are
|
|
% used by a single projection.
|
|
:- pred rl_block_opt__get_single_projects(node_id::in, int::in,
|
|
bool::in, bool::out, list(rl_goal)::in, list(rl_goal)::out,
|
|
list(output_node)::in, list(output_node)::out,
|
|
dag::in, dag::out) is det.
|
|
|
|
rl_block_opt__get_single_projects(_, _, FoundSingle, FoundSingle,
|
|
[], [], [], []) --> [].
|
|
rl_block_opt__get_single_projects(_, _, _, _,
|
|
[_|_], _, [], _) -->
|
|
{ error("rl_block_opt__get_single_projects") }.
|
|
rl_block_opt__get_single_projects(_, _, _, _,
|
|
[], _, [_|_], _) -->
|
|
{ error("rl_block_opt__get_single_projects") }.
|
|
rl_block_opt__get_single_projects(Node, Index0, FoundSingle0, FoundSingle,
|
|
[Goal0 | Goals0], [Goal | Goals],
|
|
[OutputRel0 | OutputRels0], [OutputRel | OutputRels]) -->
|
|
dag_get_output_assign_map(Assign),
|
|
( { map__search(Assign, input_node(Node, Index0), OutputId) } ->
|
|
dag_get_output_use_map(Uses0),
|
|
dag_get_node_info_map(NodeInfoMap),
|
|
dag_get_block_info(block_info(_, BlockLiveRels)),
|
|
(
|
|
% If any of the relations live at the end of the
|
|
% block are computed by this projection, it can't
|
|
% be merged.
|
|
{ OutputRel0 = output_node(_, _, Relations) },
|
|
{ set__intersect(Relations, BlockLiveRels,
|
|
NodeBlockLiveRels) },
|
|
{ set__empty(NodeBlockLiveRels) },
|
|
|
|
{ multi_map__search(Uses0, OutputId, [SingleNode]) },
|
|
{ map__lookup(NodeInfoMap, SingleNode, NodeInfo) },
|
|
{ NodeInfo = node_info(Instr, [ProjOutputRel]) },
|
|
{ Instr = project(_, [ProjGoal], _) }
|
|
->
|
|
dag_get_rl_opt_info(RLOptInfo),
|
|
{ rl_opt_info_get_module_info(ModuleInfo,
|
|
RLOptInfo, _) },
|
|
{ rl_block_opt__conjoin_goals(ModuleInfo,
|
|
Goal0, ProjGoal, Goal) },
|
|
{ FoundSingle1 = yes },
|
|
{ OutputRel = ProjOutputRel },
|
|
|
|
{ map__lookup(Assign, input_node(SingleNode, 0),
|
|
ProjOutputId) },
|
|
{
|
|
multi_map__search(Uses0, ProjOutputId,
|
|
UsingNodes0)
|
|
->
|
|
UsingNodes = UsingNodes0
|
|
;
|
|
UsingNodes = []
|
|
},
|
|
{ multi_map__det_replace(Uses0, OutputId,
|
|
UsingNodes, Uses) },
|
|
dag_set_output_use_map(Uses),
|
|
rl_block_opt__rename_node(SingleNode, ProjOutputId,
|
|
input_node(Node, Index0)),
|
|
rl_block_opt__remove_node(SingleNode)
|
|
;
|
|
|
|
{ FoundSingle1 = FoundSingle0 },
|
|
{ Goal = Goal0 },
|
|
{ OutputRel = OutputRel0 }
|
|
)
|
|
;
|
|
{ FoundSingle1 = FoundSingle0 },
|
|
{ Goal = Goal0 },
|
|
{ OutputRel = OutputRel0 }
|
|
),
|
|
{ Index = Index0 + 1 },
|
|
rl_block_opt__get_single_projects(Node, Index,
|
|
FoundSingle1, FoundSingle, Goals0, Goals,
|
|
OutputRels0, OutputRels).
|
|
|
|
:- pred rl_block_opt__conjoin_goals(module_info::in, rl_goal::in,
|
|
rl_goal::in, rl_goal::out) is det.
|
|
|
|
rl_block_opt__conjoin_goals(ModuleInfo, RLGoal1, RLGoal2, RLGoal) :-
|
|
RLGoal1 = rl_goal(_, VarSet1, VarTypes1, InstMap0,
|
|
Inputs1, Outputs1, Goals1, _),
|
|
RLGoal2 = rl_goal(_, VarSet2, VarTypes2, _,
|
|
Inputs2, Outputs2, Goals2, _),
|
|
(
|
|
Outputs1 = no,
|
|
( Inputs1 = one_input(InputArgs1) ->
|
|
RenameCallArgs = InputArgs1
|
|
;
|
|
error("rl_block_opt__conjoin_goals")
|
|
)
|
|
;
|
|
Outputs1 = yes(RenameCallArgs)
|
|
),
|
|
|
|
( Inputs2 = one_input(InputArgs2) ->
|
|
RenameHeadVars = InputArgs2
|
|
;
|
|
error("rl_block_opt__conjoin_goals")
|
|
),
|
|
|
|
goal_info_init(DummyGoalInfo),
|
|
|
|
% We don't need to do anything special with the var_types here
|
|
% because the types are guaranteed to be ground.
|
|
inlining__rename_goal(RenameHeadVars, RenameCallArgs,
|
|
VarSet1, VarSet2, VarSet, VarTypes1, VarTypes2, VarTypes,
|
|
Subn, conj(Goals2) - DummyGoalInfo, RenamedGoal),
|
|
|
|
( Outputs2 = yes(OutputArgs2) ->
|
|
goal_util__rename_var_list(OutputArgs2, yes, Subn, OutputArgs),
|
|
Outputs = yes(OutputArgs)
|
|
;
|
|
Outputs = no
|
|
),
|
|
|
|
( RenamedGoal = conj(RenamedGoals2) - _ ->
|
|
% XXX do some simplification, constraint propagation etc.
|
|
list__append(Goals1, RenamedGoals2, Goals)
|
|
;
|
|
error("rl_block_opt__conjoin_goals")
|
|
),
|
|
|
|
rl_key__extract_indexing(Inputs1, Goals, ModuleInfo, VarTypes, Bounds),
|
|
|
|
RLGoal = rl_goal(no, VarSet, VarTypes, InstMap0, Inputs1,
|
|
Outputs, Goals, Bounds).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Find a near-optimal ordering for the instructions in the dag.
|
|
%
|
|
% An optimal ordering minimises the total size of the live relations
|
|
% at any point. At the moment we just take a topological sort.
|
|
%
|
|
% After an instruction which has a stream as an output, the
|
|
% materialised relations that went in at the bottom of the stream
|
|
% are still live. For a non-stream instruction, only the output
|
|
% relation is definitely live.
|
|
|
|
:- pred rl_block_opt__deconstruct_dag(list(rl_instruction)::out,
|
|
dag::in, dag::out) is det.
|
|
|
|
rl_block_opt__deconstruct_dag(Instrs) -->
|
|
|
|
dag_get_block_info(block_info(_, LiveAtEnd0)),
|
|
{ set__to_sorted_list(LiveAtEnd0, LiveAtEnd) },
|
|
|
|
% Find out which of the non-locals are evaluated by this block.
|
|
dag_get_rel_node_map(RelNodeMap),
|
|
dag_get_output_loc_map(Locs),
|
|
{ IsProducedNonLocal = (pred(Rel::in, Node::out) is semidet :-
|
|
map__search(RelNodeMap, Rel, OutputId),
|
|
map__lookup(Locs, OutputId, input_node(Node, _))
|
|
) },
|
|
|
|
{ list__filter_map(IsProducedNonLocal, LiveAtEnd,
|
|
RequiredNodes0) },
|
|
|
|
{ set__init(RequiredNodes1) },
|
|
{ relation__init(NodeRel0) },
|
|
rl_block_opt__get_required_nodes(RequiredNodes0, RequiredNodes1,
|
|
RequiredNodes, NodeRel0, NodeRel),
|
|
|
|
{ relation__tsort(NodeRel, NodeSort0) ->
|
|
list__reverse(NodeSort0, NodeSort1)
|
|
;
|
|
error("rl_block_opt__deconstruct_dag: relation__tsort failed")
|
|
},
|
|
{ list__filter(
|
|
(pred(X::in) is semidet :- set__member(X, RequiredNodes)),
|
|
NodeSort1, NodeSort) },
|
|
{ map__init(NodeRels) },
|
|
rl_block_opt__evaluate_nodes(NodeSort, LiveAtEnd0,
|
|
NodeRels, [], [], Instrs).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Find out which instruction nodes are not dead code.
|
|
:- pred rl_block_opt__get_required_nodes(list(node_id)::in, set(node_id)::in,
|
|
set(node_id)::out, relation(node_id)::in, relation(node_id)::out,
|
|
dag::in, dag::out) is det.
|
|
|
|
rl_block_opt__get_required_nodes([], Required, Required,
|
|
NodeRel, NodeRel) --> [].
|
|
rl_block_opt__get_required_nodes([Node | Nodes0], Required0, Required,
|
|
NodeRel0, NodeRel) -->
|
|
( { set__member(Node, Required0) } ->
|
|
{ Required1 = Required0 },
|
|
{ NodeRel1 = NodeRel0 },
|
|
{ Nodes1 = Nodes0 }
|
|
;
|
|
{ set__insert(Required0, Node, Required1) },
|
|
dag_get_node_dep_map(Dep),
|
|
dag_get_output_loc_map(Locs),
|
|
{ map__search(Dep, Node, NeededOutputs) ->
|
|
GetNode =
|
|
(pred(OutputId::in, OutputNode::out) is det :-
|
|
map__lookup(Locs, OutputId,
|
|
input_node(OutputNode, _))
|
|
),
|
|
list__map(GetNode, NeededOutputs, SubNodes),
|
|
list__append(SubNodes, Nodes0, Nodes1),
|
|
AddDep =
|
|
(pred(DepNode::in, Rel0::in, Rel::out) is det :-
|
|
relation__add_values(Rel0,
|
|
Node, DepNode, Rel)
|
|
),
|
|
list__foldl(AddDep, SubNodes, NodeRel0, NodeRel1)
|
|
;
|
|
Nodes1 = Nodes0,
|
|
% Make sure this node is in the topological
|
|
% sort of the required nodes.
|
|
relation__add_element(NodeRel0, Node, _, NodeRel1)
|
|
}
|
|
),
|
|
rl_block_opt__get_required_nodes(Nodes1, Required1, Required,
|
|
NodeRel1, NodeRel).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- type node_rels == map(node_id, list(output_rel)).
|
|
|
|
:- pred rl_block_opt__evaluate_nodes(list(node_id)::in, set(relation_id)::in,
|
|
node_rels::in, list(rl_instruction)::in, list(rl_instruction)::in,
|
|
list(rl_instruction)::out, dag::in, dag::out) is det.
|
|
|
|
rl_block_opt__evaluate_nodes([], _, _, AssignInstrs, RevInstrs, Instrs) -->
|
|
{ list__append(AssignInstrs, RevInstrs, Instrs1) },
|
|
{ list__reverse(Instrs1, Instrs) }.
|
|
rl_block_opt__evaluate_nodes([Node | Nodes], NonLocalRels, NodeRels0,
|
|
AssignInstrs0, Instrs0, Instrs) -->
|
|
rl_block_opt__evaluate_node(Node, NonLocalRels, NodeRels0, NodeRels1,
|
|
AssignInstrs0, AssignInstrs1, Instrs0, Instrs1),
|
|
rl_block_opt__evaluate_nodes(Nodes, NonLocalRels,
|
|
NodeRels1, AssignInstrs1, Instrs1, Instrs).
|
|
|
|
:- pred rl_block_opt__evaluate_node(node_id::in, set(relation_id)::in,
|
|
node_rels::in, node_rels::out, list(rl_instruction)::in,
|
|
list(rl_instruction)::out, list(rl_instruction)::in,
|
|
list(rl_instruction)::out, dag::in, dag::out) is det.
|
|
|
|
rl_block_opt__evaluate_node(Node, NonLocalRels, NodeRels0, NodeRels,
|
|
AssignInstrs0, AssignInstrs, RevInstrs0, RevInstrs) -->
|
|
dag_get_node_info_map(NodeInfoMap),
|
|
{ map__lookup(NodeInfoMap, Node, NodeInfo) },
|
|
{ NodeInfo = node_info(Instr0, OutputRels) },
|
|
rl_block_opt__get_node_outputs(Instr0, NonLocalRels,
|
|
OutputRels, NodeOutputs, [], AssignInstrs1),
|
|
rl_block_opt__generate_instr(OutputRels, NodeOutputs,
|
|
NodeRels0, Instr0, NodeInstrs),
|
|
{ map__det_insert(NodeRels0, Node, NodeOutputs, NodeRels) },
|
|
{ list__reverse(NodeInstrs, RevNodeInstrs) },
|
|
{ list__append(RevNodeInstrs, RevInstrs0, RevInstrs) },
|
|
{ list__append(AssignInstrs1, AssignInstrs0, AssignInstrs) }.
|
|
|
|
% Select a relation variable to collect the output for each
|
|
% output argument of the node. Create `ref' instructions
|
|
% for each of the other relations produced by the position.
|
|
:- pred rl_block_opt__get_node_outputs(instr::in, set(relation_id)::in,
|
|
list(output_node)::in, list(output_rel)::out, list(rl_instruction)::in,
|
|
list(rl_instruction)::out, dag::in, dag::out) is det.
|
|
|
|
rl_block_opt__get_node_outputs(_, _, [], [], Instrs, Instrs) --> [].
|
|
rl_block_opt__get_node_outputs(Instr, NonLocalRels, [OutputNode | OutputNodes],
|
|
[OutputRel | OutputRels], RefInstrs0, RefInstrs) -->
|
|
{ OutputNode = output_node(Schema, Index, AllOutputs0) },
|
|
{ set__intersect(AllOutputs0, NonLocalRels, NonLocalOutputs0) },
|
|
{ Instr = input(InputRel1) ->
|
|
% Don't assign the input relation to itself.
|
|
set__delete(NonLocalOutputs0, InputRel1, NonLocalOutputs)
|
|
;
|
|
NonLocalOutputs = NonLocalOutputs0
|
|
},
|
|
(
|
|
{
|
|
% If an input relation is passed through without change,
|
|
% we don't need to generate any code for the instruction.
|
|
Instr = input(InputRel2),
|
|
Index = [],
|
|
|
|
% If the input relation is not an output of the `input'
|
|
% instruction, another instruction has overwritten its value.
|
|
set__member(InputRel2, NonLocalOutputs0)
|
|
}
|
|
->
|
|
{ RelationId = InputRel2 }
|
|
;
|
|
dag_add_relation(Schema, RelationId)
|
|
),
|
|
{ OutputRel = output_rel(RelationId, Index) },
|
|
{ set__to_sorted_list(NonLocalOutputs, OutputList) },
|
|
{ rl_block_opt__get_ref_instrs(RelationId, OutputList, NewRefInstrs) },
|
|
{ list__append(NewRefInstrs, RefInstrs0, RefInstrs1) },
|
|
rl_block_opt__get_node_outputs(Instr, NonLocalRels, OutputNodes,
|
|
OutputRels, RefInstrs1, RefInstrs).
|
|
|
|
:- pred rl_block_opt__get_ref_instrs(relation_id::in, list(relation_id)::in,
|
|
list(rl_instruction)::out) is det.
|
|
|
|
rl_block_opt__get_ref_instrs(RelationId, OutputList, RefInstrs) :-
|
|
GetRefInstr = (pred(OtherOutput::in, Instr::out) is det :-
|
|
Instr = ref(OtherOutput, RelationId) - ""
|
|
),
|
|
list__map(GetRefInstr, OutputList, RefInstrs).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred rl_block_opt__generate_instr(list(output_node)::in,
|
|
list(output_rel)::in, node_rels::in, instr::in,
|
|
list(rl_instruction)::out, dag::in, dag::out) is det.
|
|
|
|
rl_block_opt__generate_instr(_, NodeOutputs, NodeRels,
|
|
join(Input1Loc, Input2Loc, JoinType, Exprn,
|
|
SemiJoin, TrivialJoin),
|
|
[JoinInstr]) -->
|
|
|
|
rl_block_opt__get_relation_id(NodeRels, Input1Loc, Input1),
|
|
rl_block_opt__get_relation_id(NodeRels, Input2Loc, Input2),
|
|
{ rl_block_opt__one_output(NodeOutputs, Output) },
|
|
{ JoinInstr = join(Output, Input1, Input2, JoinType, Exprn,
|
|
SemiJoin, TrivialJoin) - "" }.
|
|
|
|
rl_block_opt__generate_instr(_, NodeOutputs, NodeRels,
|
|
subtract(Input1Loc, Input2Loc, Type, Exprn, TrivialSubtract),
|
|
[SubtractInstr]) -->
|
|
|
|
rl_block_opt__get_relation_id(NodeRels, Input1Loc, Input1),
|
|
rl_block_opt__get_relation_id(NodeRels, Input2Loc, Input2),
|
|
{ rl_block_opt__one_output(NodeOutputs, Output) },
|
|
{ SubtractInstr = subtract(Output, Input1, Input2, Type,
|
|
Exprn, TrivialSubtract) - "" }.
|
|
|
|
rl_block_opt__generate_instr(_, NodeOutputs, NodeRels,
|
|
difference(Input1Loc, Input2Loc, Type),
|
|
[difference(Output, Input1, Input2, Type) - ""]) -->
|
|
rl_block_opt__get_relation_id(NodeRels, Input1Loc, Input1),
|
|
rl_block_opt__get_relation_id(NodeRels, Input2Loc, Input2),
|
|
{ rl_block_opt__one_output(NodeOutputs, Output) }.
|
|
|
|
rl_block_opt__generate_instr(_, NodeOutputs, NodeRels,
|
|
project(Input1Loc, Exprns, Type),
|
|
[project(Output, Input1, Exprn, OtherOutputs, Type) - ""]) -->
|
|
rl_block_opt__get_relation_id(NodeRels, Input1Loc, Input1),
|
|
{
|
|
% XXX do smarter selection of the stream output, although
|
|
% currently the stream output isn't actually used as a stream
|
|
% if there are other outputs because it is difficult to
|
|
% handle streams with side effects.
|
|
NodeOutputs = [Output0 | Outputs],
|
|
Exprns = [Exprn0 | OtherExprns]
|
|
->
|
|
Output = Output0,
|
|
Exprn = Exprn0,
|
|
assoc_list__from_corresponding_lists(Outputs, OtherExprns,
|
|
OtherOutputs)
|
|
;
|
|
error("rl_block_opt__generate_instr: invalid project")
|
|
}.
|
|
|
|
rl_block_opt__generate_instr(_OutputRels, NodeOutputs, NodeRels,
|
|
union(InputLocs, Type), Instrs) -->
|
|
{ rl_block_opt__one_output(NodeOutputs, Output) },
|
|
list__map_foldl(rl_block_opt__get_relation_id(NodeRels),
|
|
InputLocs, Inputs),
|
|
{ Instrs = [union(Output, Inputs, Type) - ""] }.
|
|
|
|
rl_block_opt__generate_instr(_OutputRels, NodeOutputs,
|
|
NodeRels, insert(DiInputLoc, InputLoc, Type), Instrs) -->
|
|
rl_block_opt__get_relation_id(NodeRels, InputLoc, Input),
|
|
rl_block_opt__get_relation_id(NodeRels, DiInputLoc, DiInput),
|
|
{ rl_block_opt__one_output(NodeOutputs, output_rel(UoOutput, _)) },
|
|
rl_block_opt__get_copy_info(DiInputLoc, DiInput, CopyRel),
|
|
{ Instrs = [insert(UoOutput, DiInput, Input, Type,
|
|
yes(CopyRel)) - ""] }.
|
|
|
|
rl_block_opt__generate_instr(_, NodeOutputs, NodeRels,
|
|
union_diff(DiInputLoc, InputLoc, Index), Instrs) -->
|
|
rl_block_opt__get_relation_id(NodeRels, InputLoc, Input),
|
|
rl_block_opt__get_relation_id(NodeRels, DiInputLoc, DiInput),
|
|
rl_block_opt__get_copy_info(DiInputLoc, DiInput, CopyRel),
|
|
{ NodeOutputs = [output_rel(UoOutput, _), Diff] ->
|
|
Instrs = [union_diff(UoOutput, DiInput, Input,
|
|
Diff, Index, yes(CopyRel)) - ""]
|
|
;
|
|
error("rl_block_opt__generate_instr: error in union_diff")
|
|
}.
|
|
|
|
rl_block_opt__generate_instr(_, NodeOutputs, NodeRels,
|
|
sort(Input1Loc, Attrs), [sort(Output, Input1, Attrs) - ""]) -->
|
|
rl_block_opt__get_relation_id(NodeRels, Input1Loc, Input1),
|
|
{ rl_block_opt__one_output(NodeOutputs, Output) }.
|
|
|
|
rl_block_opt__generate_instr(_, NodeOutputs, NodeRels,
|
|
call(ProcName, InputLocs),
|
|
[call(ProcName, Inputs, NodeOutputs, Saved) - ""]) -->
|
|
% Liveness must be run after this.
|
|
{ set__init(Saved) },
|
|
list__map_foldl(rl_block_opt__get_relation_id(NodeRels),
|
|
InputLocs, Inputs).
|
|
|
|
rl_block_opt__generate_instr(_, NodeOutputs, NodeRels,
|
|
aggregate(Input1Loc, Initial, Update),
|
|
[aggregate(Output, Input1, Initial, Update) - ""]) -->
|
|
rl_block_opt__get_relation_id(NodeRels, Input1Loc, Input1),
|
|
{ rl_block_opt__one_output(NodeOutputs, Output) }.
|
|
|
|
rl_block_opt__generate_instr(_, NodeOutputs, NodeRels,
|
|
insert_tuple(Input1Loc, Exprn),
|
|
[insert_tuple(Output, Input1, Exprn) - ""]) -->
|
|
rl_block_opt__get_relation_id(NodeRels, Input1Loc, Input1),
|
|
{ rl_block_opt__one_output(NodeOutputs, Output) }.
|
|
|
|
rl_block_opt__generate_instr(_, NodeOutputs, _NodeRels,
|
|
init(_), [init(Output) - ""]) -->
|
|
{ rl_block_opt__one_output(NodeOutputs, Output) }.
|
|
|
|
rl_block_opt__generate_instr(_, NodeOutputs, _, input(Input), Instrs) -->
|
|
{ rl_block_opt__one_output(NodeOutputs, OutputRel) },
|
|
{ OutputRel = output_rel(Output, Indexes) },
|
|
|
|
{ Indexes = [] ->
|
|
( Output = Input ->
|
|
Instrs = []
|
|
;
|
|
Instrs = [ref(Output, Input) - ""]
|
|
)
|
|
;
|
|
Instrs = [add_index(OutputRel, Input) - ""]
|
|
}.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred rl_block_opt__get_relation_id(node_rels::in,
|
|
output_id::in, relation_id::out, dag::in, dag::out) is det.
|
|
|
|
rl_block_opt__get_relation_id(NodeRels, OutputId, RelationId) -->
|
|
dag_get_output_loc_map(Locs),
|
|
{ map__lookup(Locs, OutputId, input_node(Node, Index)) },
|
|
{ map__lookup(NodeRels, Node, NodeOutputs) },
|
|
{ list__index0_det(NodeOutputs, Index, output_rel(RelationId, _)) }.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Produce a new relation to hold a copy of the destructively updated
|
|
% input to a union_diff or insert instruction.
|
|
:- pred rl_block_opt__get_copy_info(output_id::in, relation_id::in,
|
|
output_rel::out, dag::in, dag::out) is det.
|
|
|
|
rl_block_opt__get_copy_info(DiInputLoc, DiInput,
|
|
output_rel(CopyRel, CopyIndexes)) -->
|
|
rl_block_opt__get_node_indexes(DiInputLoc, CopyIndexes),
|
|
dag_get_relation_info(DiInput, relation_info(_, Schema, _, _)),
|
|
dag_add_relation(Schema, CopyRel).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred rl_block_opt__one_output(list(T)::in, T::out) is det.
|
|
|
|
rl_block_opt__one_output(Outputs, Output) :-
|
|
( Outputs = [Output0] ->
|
|
Output = Output0
|
|
;
|
|
error("rl_block_opt__one_output")
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Cut down versions of the relational operations without their outputs.
|
|
:- type instr
|
|
---> join(output_id, output_id, join_type, rl_goal,
|
|
maybe(semi_join_info), maybe(trivial_join_info))
|
|
; subtract(output_id, output_id, subtract_type, rl_goal,
|
|
maybe(trivial_subtract_info))
|
|
; difference(output_id, output_id, difference_type)
|
|
% The first output of a project is distinguished -
|
|
% it can be a stream, the others must all be materialised.
|
|
; project(output_id, list(rl_goal), project_type)
|
|
; union(list(output_id), union_type)
|
|
; union_diff(output_id, output_id, index_spec)
|
|
; insert(output_id, output_id, insert_type)
|
|
; sort(output_id, assoc_list(int, sort_dir))
|
|
; call(rl_proc_name, list(output_id))
|
|
; aggregate(output_id, pred_proc_id, pred_proc_id)
|
|
; insert_tuple(output_id, rl_goal)
|
|
; init(list(type))
|
|
; input(relation_id) % input to block
|
|
.
|
|
|
|
:- type dag
|
|
---> dag(
|
|
node_id, % next node_id
|
|
output_id, % next output_id
|
|
rel_node_map,
|
|
exprns,
|
|
node_info_map,
|
|
output_assign_map,
|
|
output_loc_map,
|
|
output_use_map,
|
|
node_dep_map,
|
|
rl_opt_info,
|
|
block_info,
|
|
list(opt_flag),
|
|
unit
|
|
).
|
|
|
|
% An output_id represents an output of some relational operation.
|
|
:- type output_id == int.
|
|
|
|
:- type exprns == assoc_list(instr, node_id).
|
|
:- type node_info_map == map(node_id, node_info).
|
|
:- type rel_node_map == map(relation_id, output_id).
|
|
:- type output_assign_map == map(input_node, output_id).
|
|
:- type output_loc_map == map(output_id, input_node).
|
|
:- type output_use_map == multi_map(output_id, node_id).
|
|
:- type node_dep_map == multi_map(node_id, output_id).
|
|
|
|
:- type node_info
|
|
---> node_info(
|
|
instr,
|
|
list(output_node)
|
|
).
|
|
|
|
:- type output_node
|
|
---> output_node(
|
|
list(type), % schema.
|
|
list(index_spec), % indexes which should be added to
|
|
% the outputs.
|
|
set(relation_id) % relation_ids produced
|
|
% by this output.
|
|
).
|
|
|
|
:- type input_node
|
|
---> input_node(
|
|
node_id, % node producing the input
|
|
int % index in list of outputs
|
|
% of that node, starting at 0.
|
|
).
|
|
|
|
:- type node_id == int.
|
|
|
|
:- pred dag_init(rl_opt_info::in, block_info::in,
|
|
list(opt_flag)::in, dag::out) is det.
|
|
|
|
dag_init(RLOptInfo, BlockInfo, Flags, dag(0, 0, RelMap, [], Outputs, Assign,
|
|
Locs, Uses, Dep, RLOptInfo, BlockInfo, Flags, unit)) :-
|
|
map__init(RelMap),
|
|
map__init(Outputs),
|
|
map__init(Assign),
|
|
map__init(Locs),
|
|
map__init(Uses),
|
|
map__init(Dep).
|
|
|
|
:- pred dag_get_next_node_id(node_id, dag, dag).
|
|
:- mode dag_get_next_node_id(out, in, out) is det.
|
|
|
|
:- pred dag_get_next_output_id(node_id, dag, dag).
|
|
:- mode dag_get_next_output_id(out, in, out) is det.
|
|
|
|
:- pred dag_get_exprns(exprns, dag, dag).
|
|
:- mode dag_get_exprns(out, in, out) is det.
|
|
:- pred dag_set_exprns(exprns, dag, dag).
|
|
:- mode dag_set_exprns(in, in, out) is det.
|
|
|
|
:- pred dag_get_node_info_map(node_info_map, dag, dag).
|
|
:- mode dag_get_node_info_map(out, in, out) is det.
|
|
:- pred dag_set_node_info_map(node_info_map, dag, dag).
|
|
:- mode dag_set_node_info_map(in, in, out) is det.
|
|
|
|
:- pred dag_get_rel_node_map(rel_node_map, dag, dag).
|
|
:- mode dag_get_rel_node_map(out, in, out) is det.
|
|
:- pred dag_set_rel_node_map(rel_node_map, dag, dag).
|
|
:- mode dag_set_rel_node_map(in, in, out) is det.
|
|
|
|
:- pred dag_get_output_assign_map(output_assign_map, dag, dag).
|
|
:- mode dag_get_output_assign_map(out, in, out) is det.
|
|
:- pred dag_set_output_assign_map(output_assign_map, dag, dag).
|
|
:- mode dag_set_output_assign_map(in, in, out) is det.
|
|
|
|
:- pred dag_get_output_loc_map(output_loc_map, dag, dag).
|
|
:- mode dag_get_output_loc_map(out, in, out) is det.
|
|
:- pred dag_set_output_loc_map(output_loc_map, dag, dag).
|
|
:- mode dag_set_output_loc_map(in, in, out) is det.
|
|
|
|
:- pred dag_get_output_use_map(output_use_map, dag, dag).
|
|
:- mode dag_get_output_use_map(out, in, out) is det.
|
|
:- pred dag_set_output_use_map(output_use_map, dag, dag).
|
|
:- mode dag_set_output_use_map(in, in, out) is det.
|
|
|
|
:- pred dag_get_node_dep_map(node_dep_map, dag, dag).
|
|
:- mode dag_get_node_dep_map(out, in, out) is det.
|
|
:- pred dag_set_node_dep_map(node_dep_map, dag, dag).
|
|
:- mode dag_set_node_dep_map(in, in, out) is det.
|
|
|
|
:- pred dag_get_rl_opt_info(rl_opt_info, dag, dag).
|
|
:- mode dag_get_rl_opt_info(out, in, out) is det.
|
|
:- pred dag_set_rl_opt_info(rl_opt_info, dag, dag).
|
|
:- mode dag_set_rl_opt_info(in, in, out) is det.
|
|
|
|
:- pred dag_get_block_info(block_info, dag, dag).
|
|
:- mode dag_get_block_info(out, in, out) is det.
|
|
:- pred dag_set_block_info(block_info, dag, dag).
|
|
:- mode dag_set_block_info(in, in, out) is det.
|
|
|
|
:- pred dag_get_flags(list(opt_flag), dag, dag).
|
|
:- mode dag_get_flags(out, in, out) is det.
|
|
:- pred dag_set_flags(list(opt_flag), dag, dag).
|
|
:- mode dag_set_flags(in, in, out) is det.
|
|
|
|
dag_get_rel_node_map(C, Dag, Dag) :-
|
|
Dag = dag(_,_,C,_,_,_,_,_,_,_,_,_,_).
|
|
dag_get_exprns(D, Dag, Dag) :-
|
|
Dag = dag(_,_,_,D,_,_,_,_,_,_,_,_,_).
|
|
dag_get_node_info_map(E, Dag, Dag) :-
|
|
Dag = dag(_,_,_,_,E,_,_,_,_,_,_,_,_).
|
|
dag_get_output_assign_map(F, Dag, Dag) :-
|
|
Dag = dag(_,_,_,_,_,F,_,_,_,_,_,_,_).
|
|
dag_get_output_loc_map(G, Dag, Dag) :-
|
|
Dag = dag(_,_,_,_,_,_,G,_,_,_,_,_,_).
|
|
dag_get_output_use_map(H, Dag, Dag) :-
|
|
Dag = dag(_,_,_,_,_,_,_,H,_,_,_,_,_).
|
|
dag_get_node_dep_map(I, Dag, Dag) :-
|
|
Dag = dag(_,_,_,_,_,_,_,_,I,_,_,_,_).
|
|
dag_get_rl_opt_info(J, Dag, Dag) :-
|
|
Dag = dag(_,_,_,_,_,_,_,_,_,J,_,_,_).
|
|
dag_get_block_info(K, Dag, Dag) :-
|
|
Dag = dag(_,_,_,_,_,_,_,_,_,_,K,_,_).
|
|
dag_get_flags(L, Dag, Dag) :-
|
|
Dag = dag(_,_,_,_,_,_,_,_,_,_,_,L,_).
|
|
|
|
dag_set_rel_node_map(C, Dag0, Dag) :-
|
|
Dag0 = dag(A,B,_,D,E,F,G,H,I,J,K,L,M),
|
|
Dag = dag(A,B,C,D,E,F,G,H,I,J,K,L,M).
|
|
dag_set_exprns(D, Dag0, Dag) :-
|
|
Dag0 = dag(A,B,C,_,E,F,G,H,I,J,K,L,M),
|
|
Dag = dag(A,B,C,D,E,F,G,H,I,J,K,L,M).
|
|
dag_set_node_info_map(E, Dag0, Dag) :-
|
|
Dag0 = dag(A,B,C,D,_,F,G,H,I,J,K,L,M),
|
|
Dag = dag(A,B,C,D,E,F,G,H,I,J,K,L,M).
|
|
dag_set_output_assign_map(F, Dag0, Dag) :-
|
|
Dag0 = dag(A,B,C,D,E,_,G,H,I,J,K,L,M),
|
|
Dag = dag(A,B,C,D,E,F,G,H,I,J,K,L,M).
|
|
dag_set_output_loc_map(G, Dag0, Dag) :-
|
|
Dag0 = dag(A,B,C,D,E,F,_,H,I,J,K,L,M),
|
|
Dag = dag(A,B,C,D,E,F,G,H,I,J,K,L,M).
|
|
dag_set_output_use_map(H, Dag0, Dag) :-
|
|
Dag0 = dag(A,B,C,D,E,F,G,_,I,J,K,L,M),
|
|
Dag = dag(A,B,C,D,E,F,G,H,I,J,K,L,M).
|
|
dag_set_node_dep_map(I, Dag0, Dag) :-
|
|
Dag0 = dag(A,B,C,D,E,F,G,H,_,J,K,L,M),
|
|
Dag = dag(A,B,C,D,E,F,G,H,I,J,K,L,M).
|
|
dag_set_rl_opt_info(J, Dag0, Dag) :-
|
|
Dag0 = dag(A,B,C,D,E,F,G,H,I,_,K,L,M),
|
|
Dag = dag(A,B,C,D,E,F,G,H,I,J,K,L,M).
|
|
dag_set_block_info(K, Dag0, Dag) :-
|
|
Dag0 = dag(A,B,C,D,E,F,G,H,I,J,_,L,M),
|
|
Dag = dag(A,B,C,D,E,F,G,H,I,J,K,L,M).
|
|
dag_set_flags(L, Dag0, Dag) :-
|
|
Dag0 = dag(A,B,C,D,E,F,G,H,I,J,K,_,M),
|
|
Dag = dag(A,B,C,D,E,F,G,H,I,J,K,L,M).
|
|
|
|
dag_get_next_node_id(A0, Dag0, Dag) :-
|
|
Dag0 = dag(A0,B,C,D,E,F,G,H,I,J,K,L,M),
|
|
A = A0 + 1,
|
|
Dag = dag(A,B,C,D,E,F,G,H,I,J,K,L,M).
|
|
|
|
dag_get_next_output_id(B0, Dag0, Dag) :-
|
|
Dag0 = dag(A,B0,C,D,E,F,G,H,I,J,K,L,M),
|
|
B = B0 + 1,
|
|
Dag = dag(A,B,C,D,E,F,G,H,I,J,K,L,M).
|
|
|
|
:- pred dag_get_relation_info(relation_id::in, relation_info::out,
|
|
dag::in, dag::out) is det.
|
|
|
|
dag_get_relation_info(RelationId, RelationInfo) -->
|
|
dag_get_rl_opt_info(RLOptInfo),
|
|
{ rl_opt_info_get_relation_info(RelationId, RelationInfo,
|
|
RLOptInfo, _) }.
|
|
|
|
:- pred dag_add_relation(list(type)::in, relation_id::out,
|
|
dag::in, dag::out) is det.
|
|
|
|
dag_add_relation(Schema, RelationId) -->
|
|
dag_get_rl_opt_info(RLOptInfo0),
|
|
{ rl_opt_info_add_relation(Schema, RelationId,
|
|
RLOptInfo0, RLOptInfo) },
|
|
dag_set_rl_opt_info(RLOptInfo).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|