% Copyright (c) 2013-2015, Gabriel Hjort Blindell % 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 (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" ];