518 lines
17 KiB
MiniZinc
518 lines
17 KiB
MiniZinc
% Copyright (c) 2013-2015, Gabriel Hjort Blindell <ghb@kth.se>
|
|
% All rights reserved.
|
|
%
|
|
% Redistribution and use in source and binary forms, with or without
|
|
% modification, are permitted provided that the following conditions are met:
|
|
%
|
|
% 1. Redistributions of source code must retain the above copyright notice,
|
|
% this list of conditions and the following disclaimer.
|
|
% 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
% this list of conditions and the following disclaimer in the documentation
|
|
% and/or other materials provided with the distribution.
|
|
%
|
|
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
% POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
|
|
|
%=====================
|
|
% EXTERNAL PARAMETERS
|
|
%=====================
|
|
|
|
% Function data.
|
|
int: numOperationsInFunction;
|
|
int: numDataInFunction;
|
|
int: numBlocksInFunction;
|
|
int: entryBlockOfFunction;
|
|
array[allBlocksInFunction] of set of int: domSetOfBlockInFunction;
|
|
array[allBlocksInFunction] of set of int: defEdgesForBlockInFunction;
|
|
array[int] of allDataInFunction: statesInFunction;
|
|
array[allBlocksInFunction] of int: execFrequencyOfBlockInFunction;
|
|
|
|
% Target machine data.
|
|
int: numLocations;
|
|
|
|
% Match data.
|
|
int: numMatches;
|
|
array[allMatches] of set of int: operationsCoveredByMatch;
|
|
array[allMatches] of set of int: dataDefinedByMatch;
|
|
array[allMatches] of set of int: dataUsedByMatch;
|
|
array[allMatches] of set of int: entryBlockOfMatch;
|
|
array[allMatches] of set of int: spannedBlocksInMatch;
|
|
array[allMatches] of set of int: consumedBlocksInMatch;
|
|
array[allMatches] of int: codeSizeOfMatch;
|
|
array[allMatches] of int: latencyOfMatch;
|
|
array[allMatches] of bool: applyDefDomUseConstraintForMatch;
|
|
set of allMatches: nonCopyMatches;
|
|
|
|
% Arrays that encode constraints
|
|
array[int,int] of int: sameLoc;
|
|
array[int,int] of int: inBlockSucc;
|
|
array[int,int] of int: inBlock;
|
|
array[int,int] of int: locDomain;
|
|
array[int,int] of int: funLocDomain;
|
|
|
|
set of allMatches: Dominated;
|
|
|
|
%=====================
|
|
% INTERNAL PARAMETERS
|
|
%=====================
|
|
|
|
% Total number of location values (an additional value will be needed for
|
|
% representing the null location, for when the datum cannot be reused by other
|
|
% matches).
|
|
int: numLocValues = numLocations + 1;
|
|
|
|
% Total number of blocks values (an additional value will be needed for
|
|
% representing the null block, to which non-selected matches will be placed).
|
|
int: numBlockValues = numBlocksInFunction + 1;
|
|
|
|
% Reference to the null-block value.
|
|
int: blockValueForNull = numBlockValues - 1;
|
|
|
|
% Reference to to the null-location value.
|
|
int: locValueForNull = numLocValues - 1;
|
|
|
|
% Extends the external parameter 'domSetOfBlockInFunction' with a value
|
|
% for the null block (which is assumed to be dominated by all blocks).
|
|
array[allBlocksInFunctionPlusNull] of set of int:
|
|
domSetOfBlockInFunctionPlusNull =
|
|
array1d( allBlocksInFunctionPlusNull
|
|
, domSetOfBlockInFunction
|
|
++
|
|
array1d( blockValueForNull..blockValueForNull,
|
|
[allBlocksInFunction]
|
|
)
|
|
);
|
|
|
|
% Extends the external parameter 'execFrequencyOfBlockInFunction' with a value
|
|
% of 0 for the null block.
|
|
array[allBlocksInFunctionPlusNull] of int: execFrequencyOfBlocksPlusNull =
|
|
array1d( allBlocksInFunctionPlusNull
|
|
, execFrequencyOfBlockInFunction
|
|
++
|
|
array1d(blockValueForNull..blockValueForNull, [0])
|
|
);
|
|
|
|
% Sets to be used as array ranges.
|
|
set of int: allOperationsInFunction = 0..numOperationsInFunction-1;
|
|
set of int: allDataInFunction = 0..numDataInFunction-1;
|
|
set of int: allBlocksInFunction = 0..numBlocksInFunction-1;
|
|
set of int: allBlocksInFunctionPlusNull = 0..numBlockValues-1;
|
|
set of int: allMatches = 0..numMatches-1;
|
|
set of int: allLocValues = 0..numLocValues-1;
|
|
|
|
|
|
|
|
%===========
|
|
% VARIABLES
|
|
%===========
|
|
|
|
% Data definitions and locations.
|
|
array[allDataInFunction] of var allBlocksInFunction: def;
|
|
array[allDataInFunction] of var allLocValues: loc;
|
|
|
|
% Match selection.
|
|
array[allMatches] of var bool: sel;
|
|
|
|
% Blocks wherein the matches are placed.
|
|
array[allMatches] of var allBlocksInFunctionPlusNull: place;
|
|
|
|
% Block ordering (succ[b] is the block appearing immeditely after block b in the
|
|
% generated code).
|
|
array[allBlocksInFunctionPlusNull] of var allBlocksInFunctionPlusNull: succ;
|
|
|
|
% Cost.
|
|
var 0..sum (m in allMatches diff Dominated)( latencyOfMatch[m]* max ([execFrequencyOfBlocksPlusNull[x] | x in allBlocksInFunction])): objective;
|
|
|
|
|
|
|
|
%====================
|
|
% GLOBAL CONSTRAINTS
|
|
%====================
|
|
|
|
include "circuit.mzn";
|
|
include "table.mzn";
|
|
|
|
|
|
|
|
%============
|
|
% CONSTRAINTS
|
|
%============
|
|
|
|
% Enforce that, for each operation, exactly one match must be selected such that
|
|
% the operation is covered.
|
|
constraint
|
|
forall (o in allOperationsInFunction)
|
|
(
|
|
let {
|
|
set of int: mset = { m | m in allMatches diff Dominated
|
|
where o in operationsCoveredByMatch[m]
|
|
}
|
|
}
|
|
in if card(mset) = 1
|
|
then sel[min(mset)]
|
|
else if card(mset) = 2
|
|
then sel[min(mset)] xor sel[max(mset)]
|
|
else sum (m in mset)
|
|
(
|
|
bool2int(sel[m])
|
|
) = 1
|
|
endif
|
|
endif
|
|
);
|
|
|
|
% Enforce that, for each datum, exactly one match must be selected such that
|
|
% the datum is defined.
|
|
%
|
|
% This is an implied constraint, but it also enforces that the patterns for
|
|
% defining the function input and constants are selected. Such patterns do not
|
|
% cover any operations, they are not entailed in the above constraint for
|
|
% exactly covering each operation.
|
|
constraint
|
|
forall (e in allDataInFunction)
|
|
(
|
|
let {
|
|
set of int: mset = { m | m in allMatches diff Dominated
|
|
where e in dataDefinedByMatch[m]
|
|
}
|
|
}
|
|
in if card(mset) = 1
|
|
then sel[min(mset)]
|
|
else if card(mset) = 2
|
|
then sel[min(mset)] xor sel[max(mset)]
|
|
else sum (m in mset)
|
|
(
|
|
bool2int(sel[m])
|
|
) = 1
|
|
endif
|
|
endif
|
|
);
|
|
|
|
% Implied: The total number of data defined by the selected matches must be
|
|
% equal to the number of data in the function graph.
|
|
constraint redundant_constraint (
|
|
sum (m in allMatches diff Dominated)
|
|
(
|
|
card(dataDefinedByMatch[m]) * bool2int(sel[m])
|
|
) = numDataInFunction
|
|
);
|
|
|
|
% Selected matches must not be placed in the null block.
|
|
constraint
|
|
forall (m in allMatches)
|
|
(
|
|
sel[m] <-> place[m] != blockValueForNull
|
|
);
|
|
|
|
% Selected matches that have an entry block must be placed in entry block.
|
|
constraint
|
|
forall (m in allMatches diff Dominated)
|
|
(
|
|
% If a match has no entry block, then this set will be empty and hence there
|
|
% will be no such constraint. It is assumed that there will be at most one
|
|
% entry.
|
|
forall (r in entryBlockOfMatch[m])
|
|
(
|
|
place[m] in {r, blockValueForNull}
|
|
)
|
|
);
|
|
|
|
% Data defined by a selected match must be defined in either the block wherein
|
|
% the match is placed or in one of the blocks spanned by the match.
|
|
constraint
|
|
forall (m in allMatches diff Dominated)
|
|
(
|
|
forall (e in dataDefinedByMatch[m])
|
|
(
|
|
if (card(spannedBlocksInMatch[m]) > 0)
|
|
then
|
|
sel[m] -> def[e] in spannedBlocksInMatch[m]
|
|
else
|
|
sel[m] -> def[e] = place[m]
|
|
endif
|
|
)
|
|
);
|
|
|
|
% No selected matches may be placed in a block which is consumed by some
|
|
% selected match.
|
|
constraint
|
|
forall (m, mm in allMatches, b in consumedBlocksInMatch[m])
|
|
(
|
|
sel[m] -> place[mm] != b
|
|
);
|
|
|
|
% For every block wherein a datum is defined, there must exist some selected
|
|
% match such that it is either placed in that block or that block is part of
|
|
% one of the blocks that appear in the selected match.
|
|
%
|
|
% I am not certain whether this is a strictly required or just an implied
|
|
% constraint...
|
|
constraint
|
|
forall (e in allDataInFunction, l in allBlocksInFunction)
|
|
(
|
|
def[e] = l
|
|
->
|
|
exists (m in allMatches diff Dominated)
|
|
(
|
|
place[m] = l \/ (sel[m] /\ l in spannedBlocksInMatch[m])
|
|
)
|
|
);
|
|
|
|
% A datum with a definition edge with a block must be defined in the block of
|
|
% that block.
|
|
constraint
|
|
forall (l in allBlocksInFunction)
|
|
(
|
|
forall (e in defEdgesForBlockInFunction[l])
|
|
(
|
|
def[e] = l
|
|
)
|
|
);
|
|
|
|
% Enforce that every datum is defined in a block such that the block dominates
|
|
% all blocks wherein the datum is used. This constraint shall not be applied to
|
|
% the generic phi patterns.
|
|
%
|
|
% The code below is essentially a more efficient implementation of:
|
|
% constraint
|
|
% forall ( m in allMatches, e in dataUsedByMatch[m]
|
|
% where applyDefDomUseConstraintForMatch[m]
|
|
% )
|
|
% (
|
|
% def[e] in domSetOfBlockInFunctionPlusNull[place[m]]
|
|
% );
|
|
int: DomRelSize =
|
|
sum (l in allBlocksInFunction)
|
|
(
|
|
card(domSetOfBlockInFunction[l])
|
|
) + numBlocksInFunction;
|
|
array[1..DomRelSize, 1..2] of allBlocksInFunctionPlusNull: DomRel =
|
|
array2d(1..DomRelSize, 1..2,
|
|
[ if k=1 then i else j endif | i in allBlocksInFunctionPlusNull,
|
|
j in domSetOfBlockInFunctionPlusNull[i],
|
|
k in 1..2
|
|
]);
|
|
|
|
constraint
|
|
forall ( m in allMatches diff Dominated, e in dataUsedByMatch[m]
|
|
where applyDefDomUseConstraintForMatch[m]
|
|
)
|
|
(
|
|
table([place[m], def[e]], DomRel)
|
|
);
|
|
|
|
% Ensure that succ forms a circuit (thus resulting in an ordering of blocks).
|
|
constraint
|
|
circuit(succ) :: domain;
|
|
|
|
% The block of the entry block (i.e. function entry point) must be placed as the
|
|
% first block, and the block of the null block must be placed as the last block.
|
|
constraint
|
|
succ[blockValueForNull] = entryBlockOfFunction;
|
|
|
|
% Constrain the loc value for all data that are states.
|
|
constraint
|
|
forall (e in statesInFunction)
|
|
(
|
|
loc[e] = locValueForNull
|
|
);
|
|
|
|
% Accumulate cost of selected patterns.
|
|
% TODO: make this part generic
|
|
constraint
|
|
objective = sum (m in allMatches diff Dominated)
|
|
( latencyOfMatch[m]
|
|
* execFrequencyOfBlocksPlusNull[place[m]]
|
|
);
|
|
|
|
|
|
|
|
%===========================
|
|
% PARAMETERIZED CONSTRAINTS
|
|
%===========================
|
|
|
|
constraint
|
|
forall(i in index_set_1of2(sameLoc))(
|
|
let {int: m = sameLoc[i,1],
|
|
int: p = sameLoc[i,2],
|
|
int: q = sameLoc[i,3]} in
|
|
sel[m] -> loc[p] = loc[q]
|
|
);
|
|
|
|
constraint
|
|
forall(i in index_set_1of2(inBlockSucc))(
|
|
let {int: m = inBlockSucc[i,1],
|
|
int: p = inBlockSucc[i,2],
|
|
int: q = inBlockSucc[i,3]} in
|
|
place[m] in {p,blockValueForNull} /\
|
|
(sel[m] -> succ[p] = q)
|
|
);
|
|
|
|
constraint
|
|
forall(i in index_set_1of2(inBlock))(
|
|
let {int: m = inBlock[i,1],
|
|
int: p = inBlock[i,2]} in
|
|
place[m] in {p,blockValueForNull}
|
|
);
|
|
|
|
constraint
|
|
forall(i in index_set_1of2(locDomain))(
|
|
let {int: m = locDomain[i,1],
|
|
int: l = locDomain[i,2]} in
|
|
sel[m] -> loc[l] in locDomain[i,3]..locDomain[i,4]
|
|
);
|
|
|
|
constraint
|
|
forall(i in index_set_1of2(funLocDomain))(
|
|
let {int: l = funLocDomain[i,1]} in
|
|
loc[l] in funLocDomain[i,2]..funLocDomain[i,3]
|
|
);
|
|
|
|
|
|
|
|
%======================================
|
|
% IMPLIED BY PARAMETERIZED CONSTRAINTS
|
|
%======================================
|
|
|
|
% Two matches can't both be selected, if they imply conflicting successor blocks.
|
|
constraint redundant_constraint(
|
|
forall(i in index_set_1of2(inBlockSucc), j in index_set_1of2(inBlockSucc) where i<j)(
|
|
let {int: mi = inBlockSucc[i,1],
|
|
int: pi = inBlockSucc[i,2],
|
|
int: qi = inBlockSucc[i,3],
|
|
int: mj = inBlockSucc[j,1],
|
|
int: pj = inBlockSucc[j,2],
|
|
int: qj = inBlockSucc[j,3]} in
|
|
(pi=pj xor qi=qj) -> (not sel[mi] \/ not sel[mj])
|
|
)
|
|
);
|
|
|
|
% Two matches can't both be selected, if the first implies that two locations
|
|
% are equal, and the second implies that the intersection of their domains is
|
|
% empty
|
|
constraint redundant_constraint(
|
|
forall(i in index_set_1of2(sameLoc))(
|
|
let {int: m1 = sameLoc[i,1],
|
|
int: p = sameLoc[i,2],
|
|
int: q = sameLoc[i,3]} in
|
|
forall(j1 in index_set_1of2(locDomain) where locDomain[j1,2]=p)(
|
|
forall(j2 in index_set_1of2(locDomain) where locDomain[j2,1]=locDomain[j1,1] /\
|
|
locDomain[j2,2]=q)(
|
|
let {int: m2 = locDomain[j1,1]} in
|
|
card((locDomain[j1,3]..locDomain[j1,4]) intersect (locDomain[j2,3]..locDomain[j2,4]))=0 ->
|
|
not sel[m1] \/ not sel[m2]
|
|
)
|
|
)
|
|
)
|
|
);
|
|
|
|
% Ad hoc: detect symmetry among location values 1..31
|
|
constraint redundant_constraint (
|
|
forall(i in index_set_1of2(funLocDomain))(
|
|
let {int: lo = funLocDomain[i,2],
|
|
int: hi = funLocDomain[i,3]} in
|
|
lo<=31 -> hi>=31 \/ hi=0
|
|
) /\
|
|
forall(i in index_set_1of2(locDomain))(
|
|
let {int: lo = locDomain[i,3],
|
|
int: hi = locDomain[i,4]} in
|
|
lo<=31 -> hi>=31 \/ hi=0
|
|
) ->
|
|
forall(i in allDataInFunction)(loc[i] in {0} union (31..numLocValues-1))
|
|
);
|
|
|
|
|
|
|
|
%===================================================
|
|
% DOMINATION DERIVED FROM PARAMETERIZED CONSTRAINTS
|
|
%===================================================
|
|
|
|
% Match m1 dominates match m2 if:
|
|
% - neither match occurs in sameLoc, inBlock, inBlockSucc (relax later?)
|
|
% - [latencyOfMatch[m1], m1] lex_lt [latencyOfMatch[m2], m2]
|
|
% - operationsCoveredByMatch is the same for both matches
|
|
% - dataDefinedByMatch is the same for both matches
|
|
% - dataUsedByMatch is the same for both matches
|
|
% - entryBlockOfMatch is the same for both matches
|
|
% - spannedBlocksInMatch is the same for both matches
|
|
% - applyDefDomUseConstraintForMatch is the same for both matches
|
|
% - if m1 defines any side-constraints in locDomain,
|
|
% then m2 defines side-constraints that are at least as strong
|
|
test is_dominated(int: m2) =
|
|
let {set of allMatches: exclude = {sameLoc[i,1] | i in index_set_1of2(sameLoc)} % relax later
|
|
union {inBlock[i,1] | i in index_set_1of2(inBlock)}
|
|
union {inBlockSucc[i,1] | i in index_set_1of2(inBlockSucc)}} in
|
|
exists(m1 in allMatches where not (m1=m2 \/ m1 in exclude \/ m2 in exclude))(
|
|
((latencyOfMatch[m1] < latencyOfMatch[m2]) \/
|
|
(latencyOfMatch[m1] = latencyOfMatch[m2] /\ m1 < m2)) /\
|
|
operationsCoveredByMatch[m1] = operationsCoveredByMatch[m2] /\
|
|
dataDefinedByMatch[m1] = dataDefinedByMatch[m2] /\
|
|
dataUsedByMatch[m1] = dataUsedByMatch[m2] /\
|
|
entryBlockOfMatch[m1] = entryBlockOfMatch[m2] /\
|
|
spannedBlocksInMatch[m1] = spannedBlocksInMatch[m2] /\
|
|
applyDefDomUseConstraintForMatch[m1] = applyDefDomUseConstraintForMatch[m2] /\
|
|
forall(i in index_set_1of2(locDomain) where locDomain[i,1] = m1)(
|
|
exists(j in index_set_1of2(locDomain)
|
|
where locDomain[j,1] = m2
|
|
/\ locDomain[j,2] = locDomain[i,2])(
|
|
(locDomain[j,3]..locDomain[j,4]) subset (locDomain[i,3]..locDomain[i,4])
|
|
)
|
|
)
|
|
);
|
|
|
|
Dominated = {m | m in allMatches where is_dominated(m)};
|
|
|
|
constraint
|
|
forall(d in Dominated)(not sel[d]);
|
|
|
|
%==================
|
|
% SOLVE AND OUTPUT
|
|
%==================
|
|
|
|
solve ::
|
|
seq_search(
|
|
[ bool_search([ sel[m] | m in nonCopyMatches diff Dominated
|
|
where card(operationsCoveredByMatch[m])
|
|
+
|
|
card(dataDefinedByMatch[m])
|
|
> 2
|
|
], input_order, indomain_max, complete)
|
|
, bool_search([ sel[m] | m in nonCopyMatches diff Dominated
|
|
where card(operationsCoveredByMatch[m])
|
|
+
|
|
card(dataDefinedByMatch[m])
|
|
= 2
|
|
], input_order, indomain_max, complete)
|
|
, bool_search([ sel[m] | m in nonCopyMatches diff Dominated
|
|
where card(operationsCoveredByMatch[m])
|
|
+
|
|
card(dataDefinedByMatch[m])
|
|
= 1
|
|
], input_order, indomain_max, complete)
|
|
, int_search( def, first_fail, indomain_min, complete)
|
|
, int_search( loc, first_fail, indomain_min, complete)
|
|
, int_search( place, first_fail, indomain_min, complete)
|
|
, int_search( succ, first_fail, indomain_min, complete)
|
|
|
|
]
|
|
)
|
|
minimize objective;
|
|
|
|
output [ "sel = array1d(\(allMatches), \(sel));\n"
|
|
, "def = array1d(\(allDataInFunction), \(def));\n"
|
|
, "loc = array1d(\(allDataInFunction), \(loc));\n"
|
|
, "place = array1d(\(allMatches), \(place));\n"
|
|
, "succ = array1d(\(allBlocksInFunctionPlusNull), \(succ));\n"
|
|
, "objective = \(objective);\n"
|
|
];
|