def gen_executor_loop(mapir): from iegen import Set,Relation from iegen.codegen import Statement,Comment import iegen.pycloog as pycloog from iegen.pycloog import codegen stmts=[] stmts.append(Comment('The executor main loop')) statement_names=mapir.statements.keys() statement_names.sort() #True if any statement has a sparse schedule sparse_loop=any((mapir.statements[statement_name].sparse_sched is not None for statement_name in statement_names)) cloog_stmts=[] for statement_name in statement_names: statement=mapir.statements[statement_name] #Approximate any iterators that are equal to a UFS using know range bounds cloog_iter_space=statement.iter_space.approximate(mapir.ufs_range_dict()) #Use approximate with empty sets to remove any UFS equalities in the scattering function scatter_fnames=statement.scatter.function_names empty_sets=[Set('{[a]}')]*len(scatter_fnames) empty_sets_ufs_map=dict(zip(scatter_fnames,empty_sets)) cloog_scatter=statement.scatter.approximate(empty_sets_ufs_map) #Calculate iteration space reduction relations orig_iters=','.join(cloog_iter_space.tuple_set) reduce_iters=','.join(cloog_iter_space.tuple_set[:-1]) reduce_iter_space=Relation('{[%s]->[%s]}'%(reduce_iters,orig_iters)).inverse() orig_full_iters=','.join(cloog_scatter.tuple_out) reduce_full_iters=','.join(cloog_scatter.tuple_out[:-2]) reduce_full_iter_space=Relation('{[%s]->[%s]}'%(reduce_full_iters,orig_full_iters)).inverse() #If the statement has a sparse schedule, reduce the dimensionality of the iteration space and scattering function if statement.sparse_sched is not None: cloog_iter_space=cloog_iter_space.apply(reduce_iter_space) cloog_scatter=reduce_full_iter_space.compose(cloog_scatter) cloog_scatter=reduce_iter_space.compose(cloog_scatter.inverse()).inverse() elif statement.sparse_sched is None and sparse_loop: cloog_scatter=reduce_full_iter_space.compose(cloog_scatter) #Create the statement to pass to CLooG using the modified iteration space and scattering function cloog_stmts.append(pycloog.Statement(cloog_iter_space,cloog_scatter)) #Run CLooG, get the result string from CLooG's codegen cloog_gen_str=codegen(cloog_stmts) #Split the generated code at newlines cloog_stmts=cloog_gen_str.split('\n') #Create Statement objects for each line of the code generated by CLooG for cloog_stmt in cloog_stmts: stmts.append(Statement(cloog_stmt)) return stmts
def testComposeSameObject(self): from iegen import Relation rel=Relation('{[a]->[a]}') composed=rel.compose(rel) composed_res=Relation('{[a2]->[a01]: a2=a01}') self.failUnless(composed==composed_res,'%s!=%s'%(composed,composed_res))
def testComposeNonDestructive(self): from iegen import Relation relation1=Relation('{[a]->[a]}') relation2=Relation('{[b]->[b]}') composed=relation1.compose(relation2) self.failIf(composed is relation1,'%s is %s'%(composed,relation1)) self.failIf(composed is relation2,'%s is %s'%(composed,relation2))
def testComposeRename2(self): from iegen import Relation relation1=Relation('{[a,b]->[b]:1<=a and a<=11 and 1<=b and b<=10}') relation2=Relation('{[a]->[a,b]:-10<=a and a<=0 and b=5}') composed=relation1.compose(relation2) composed_res=Relation('{[a2]->[b01]: a2+-1>=0 and -1a2>=0 and a2+10>=0 and -1a2+11>=0 and -1b01+5=0}') self.failUnless(composed==composed_res,'%s!=%s'%(composed,composed_res))
def testComposeRename1(self): from iegen import Relation relation1=Relation('{[a,b]->[c]:1<=a and a<=10 and 1<=b and b<=10}') relation2=Relation('{[a]->[b,c]:-10<=a and a<=0}') composed=relation1.compose(relation2) composed_res=Relation('{[a2]->[c1]: -1a2>=0 and a2+10>=0}') self.failUnless(composed==composed_res,'%s!=%s'%(composed,composed_res))
def testInOutMapping(self): from iegen import Relation relation1=Relation('{[i]->[j]: j=f(i)}') relation2=Relation('{[a]->[a]}') composed=relation1.compose(relation2) composed_res=Relation('{[a]->[j]: j=f(a)}') self.failUnless(composed==composed_res,'%s!=%s'%(composed,composed_res))
def testComposeNoRenameBack(self): from iegen import Relation relation1=Relation('{[a]->[b,c]: a=b and b=7 and c>10}') relation2=Relation('{[b,i]->[k]: i>0 and b<-5 and k>3 and b=k}') composed=relation1.compose(relation2) composed_res=Relation('{[b2,i2]->[b1,c1]: i2>0 and b2<-5 and b2>=4 and b1=7 and c1>10 and b2=b1}') self.failUnless(composed==composed_res,'%s!=%s'%(composed,composed_res))
def testComposeRenameBack(self): from iegen import Relation relation1=Relation('{[a]->[b,c]: a=6 and b=7 and c>10}') relation2=Relation('{[i,j]->[k]: i>0 and j<-5 and k>3}') composed=relation1.compose(relation2) composed_res=Relation('{[i,j]->[b,c]: i>0 and j<-5 and b=7 and c>10}') self.failUnless(composed==composed_res,'%s!=%s'%(composed,composed_res))
def testComposeEquality(self): from iegen import Relation relation1=Relation('{[a,b]->[c]:c=a}') relation2=Relation('{[d]->[e,f]:e=d and f=d}') composed=relation1.compose(relation2) composed_res=Relation('{[d]->[c]: d=c}') self.failUnless(composed==composed_res,'%s!=%s'%(composed,composed_res))
def testComposeRename3(self): from iegen import Relation relation1=Relation('{[a,b]->[a]:1<=a and a<=10 and 1<=b and b<=10}') relation2=Relation('{[a]->[a,b]:-10<=a and a<=0 and b=5}') composed=relation1.compose(relation2) composed_res=Relation('{[a2]->[a01]: a2<=10 and a2<=0 and a2>=1 and a2>=-10 and a2=a01}') self.failUnless(composed==composed_res,'%s!=%s'%(composed,composed_res))
def testComposeRealUnion(self): from iegen import Relation ar=Relation('{[ii,s]->[ar_out]:s=1 and ar_out=inter(ii)}').union(Relation('{[ii,s]->[ar_out]:s=2 and ar_out=inter(ii)}')).union(Relation('{[ii,s]->[ar_out]:s=3 and ar_out=inter(ii)}')) data_reordering=Relation('{[k]->[dr_out]:dr_out=sigma(k)}') ar_composed=data_reordering.compose(ar) ar_res=Relation('{[ii,s]->[dr_out]:s=1 and dr_out=sigma(inter(ii))}').union(Relation('{[ii,s]->[dr_out]:s=2 and dr_out=sigma(inter(ii))}')).union(Relation('{[ii,s]->[dr_out]:s=3 and dr_out=sigma(inter(ii))}')) self.failUnless(ar_composed==ar_res,'%s!=%s'%(ar_composed,ar_res))
def testCompose(self): from iegen import Relation relation1=Relation('{[a,b]->[c]:1<=a and a<=10 and 1<=b and b<=10}') relation2=Relation('{[d]->[e,f]:-10<=d and d<=0}') composed=relation1.compose(relation2) composed_res=Relation('{[d]->[c]: -10<=d and d<=0}') self.failUnless(composed==composed_res,'%s!=%s'%(composed,composed_res))
def ar_testing(): a4_0=Relation('{[s_out1,j]->[k]: k+-1inter1(j)=0}') #### DataPermuteTrans R_x0_x1 = Relation("{[sigma_in] -> [sigma_out] : sigma_out = sigma(sigma_in)}") a4_1 = R_x0_x1.compose(a4_0) #### Loop Alignment T_I0_to_I1 = Relation('{[s,i] -> [s,j]: j=sigma(i)}') a4_2 = a4_1.compose( T_I0_to_I1.inverse() ) #### IterPermuteTrans T_I1_to_I2 = Relation("{[s,ii]->[s,j]: j = delta(ii) }") a4_3 = a4_2.compose( T_I1_to_I2.inverse() ) print a4_3
def ar_working(): a4_0=Relation('{[c0,s_out1,c1,j,c2]->[k]: c0=0 and c2=0 and k+-1inter1(j)=0 and -1c1+1=0}') #### DataPermuteTrans R_x0_x1 = Relation("{[sigma_in] -> [sigma_out] : sigma_out = sigma(sigma_in)}") a4_1 = R_x0_x1.compose(a4_0) #### Loop Alignment T_I0_to_I1 = Relation('{[c0, s, c0, i, c0] -> [c0, s, c0, j, c0] : c0=0 && j=sigma(i)}').union( Relation('{[c0, s, c1, ii, x] -> [c0, s, c1, ii, x] : c0=0 && c1=1}') ) a4_2 = a4_1.compose( T_I0_to_I1.inverse() ) #### IterPermuteTrans T_I1_to_I2 = Relation("{[c0,s1,c1,i,c2] -> [c3,s2,c4,j,c5] : s1=s2 && c0=0 && c1=0 && c2=0 && c3=0 && c4=0 && c5=0 && i=j}").union( Relation("{[c6,s3,c7,ii,x] -> [c8,s4,c9,j,y] : s3=s4 && j = delta(ii) && c6=0 && c8=0 && c7=1 && c9=1 && x=y }")) a4_3 = a4_2.compose( T_I1_to_I2.inverse() ) print a4_3
print T_I0_to_I1 = Relation('{[c0, s, c0, i, c0] -> [c0, s, c0, j, c0] : c0=0 && j=sigma(i)}') T_I0_to_I1 = T_I0_to_I1.union( Relation('{[c0, s, c1, ii, x] -> [c0, s, c1, ii, x] : c0=0 && c1=1}') ) print "\tT_I0_to_I1 = " PrettyPrintVisitor().visit(T_I0_to_I1) print T_I1_to_I2 = Relation("{[c0,s,c0,i,c0] -> [c0,s,c0,i,c0] : c0=0 }") T_I1_to_I2 = T_I1_to_I2.union( Relation("{[c0,s,c1,ii,x] -> [c7,s,c8,j,x] : j = delta(ii) && c0=0 && c1=1 }")) print "T_I1_to_I2 = " PrettyPrintVisitor().visit(T_I1_to_I2) print D_1_2 = T_I0_to_I1.compose( D_1_2.compose( T_I0_to_I1.inverse() ) ) print "\t\tD_1_2 = T_I0_to_I1 compose ( D_1_2 compose (inverse T_I0_to_I1) ) ) = ", D_1_2 PrettyPrintVisitor().visit(D_1_2) print D_1_2 = T_I1_to_I2.compose( D_1_2.compose( T_I1_to_I2.inverse() ) ) print "\t\tD_1_2 = T_I1_to_I2 compose ( D_1_2 compose (inverse T_I1_to_I2) ) ) =", D_1_2 PrettyPrintVisitor().visit(D_1_2) print print "\t\tD_1_3 = T_I0_to_I1 compose ( D_1_3 compose (inverse T_I0_to_I1) ) ) =" D_1_3 = T_I0_to_I1.compose( D_1_3.compose( T_I0_to_I1.inverse() ) ) PrettyPrintVisitor().visit(D_1_3) print print "\t\tD_1_3 = T_I1_to_I2 compose ( D_1_3 compose (inverse T_I1_to_I2) ) ) =" D_1_3 = T_I1_to_I2.compose( D_1_3.compose( T_I1_to_I2.inverse() ) ) PrettyPrintVisitor().visit(D_1_3)
# compose_bug_136.py # import iegen from iegen import Set from iegen import Relation from iegen import Symbolic import iegen.simplify iegen.simplify.register_inverse_pair("delta", "delta_inv") from iegen.ast.visitor import PrettyPrintVisitor D_ST = Relation("{ [c0,i] -> [x,j] : i+-1*sigma(inter1(delta_inv(j)))=0 && c0=0 }") D_ST = D_ST.union(Relation("{ [c0,i] -> [x,j] : i+-1*sigma(inter2(delta_inv(j)))=0 && c0 = 0 }")) D_ST.compose(D_ST)
class DataPermuteTrans(Transformation): __slots__=('reordering_name','_data_reordering','data_arrays','iter_sub_space_relation','target_data_arrays','erg_func_name') _relation_fields=('iter_sub_space_relation',) _data_arrays_fields=('data_arrays','target_data_arrays') def __init__(self,name,reordering_name,data_arrays,iter_sub_space_relation,target_data_arrays,erg_func_name): Transformation.__init__(self,name) self.reordering_name=reordering_name self.data_arrays=data_arrays self.iter_sub_space_relation=iter_sub_space_relation self.target_data_arrays=target_data_arrays self.erg_func_name=erg_func_name #Calculate the data reordering relation self._data_reordering=Relation('{[%s_in]->[%s_out]: %s_out=%s(%s_in)}'%(5*(self.reordering_name,))) #Make sure the target data arrays all have the same bounds #We do this by unioning all bounds and simply checking that there is a single conjunction in the disjunction target_bounds=[data_array.bounds for data_array in self.target_data_arrays] unioned_bounds=reduce(lambda da1,da2: da1.union(da2),target_bounds) if len(unioned_bounds)!=1: raise ValueError('All target data arrays must have the same bounds') def __repr__(self): return 'DataPermuteTrans(%s,%s,%s,%s,%s)'%(self.data_reordering,self.data_arrays,self.iter_sub_space_relation,self.target_data_arrays,self.erg_func_name) def __str__(self): return self._get_string(0) def _get_string(self,indent): if indent>0: indent+=1 spaces=' '*indent inputs_string=StringIO() if len(self.inputs)>0: for input in self.inputs: print >>inputs_string,input._get_string(indent+5) inputs_string=inputs_string.getvalue()[:-1] if len(inputs_string)>0: inputs_string='\n'+inputs_string outputs_string=StringIO() if len(self.outputs)>0: for output in self.outputs: print >>outputs_string,output._get_string(indent+5) outputs_string=outputs_string.getvalue()[:-1] if len(outputs_string)>0: outputs_string='\n'+outputs_string data_arrays_string=StringIO() if len(self.data_arrays)>0: for data_array in self.data_arrays: print >>data_arrays_string,data_array._get_string(indent+13) data_arrays_string=data_arrays_string.getvalue()[:-1] if len(data_arrays_string)>0: data_arrays_string='\n'+data_arrays_string target_data_arrays_string=StringIO() if len(self.target_data_arrays)>0: for data_array in self.target_data_arrays: print >>target_data_arrays_string,data_array._get_string(indent+13) target_data_arrays_string=target_data_arrays_string.getvalue()[:-1] if len(target_data_arrays_string)>0: target_data_arrays_string='\n'+target_data_arrays_string return '''%sDataPermuteTrans: %s|-name: %s %s|-inputs: %s %s|-outputs: %s %s|-reordering_name: %s %s|-_data_reordering: %s %s|-data_arrays: %s %s|-iter_sub_space_relation: %s %s|-target_data_arrays: %s %s|-erg_func_name: %s'''%(spaces,spaces,self.name, spaces,inputs_string, spaces,outputs_string, spaces,self.reordering_name, spaces,self._data_reordering, spaces,data_arrays_string, spaces,self.iter_sub_space_relation, spaces,target_data_arrays_string, spaces,self.erg_func_name) #Calculate a specification for the explicit relation that is input to # the data reordering algorithm. #This input is a relation from the iteration sub space to the # the target data space. def calc_input(self,mapir): #Iteration Sub Space Relation issr=self.iter_sub_space_relation #Calculate the full iteration space to data space relation #Collect all iter_to_data relations in all access relations access_relations=[ar.iter_to_data for stmt in mapir.get_statements() for ar in stmt.get_access_relations() if set()!=set([ar.data_array]).intersection(set(self.target_data_arrays))] #Union all the relations that were collected into a single relation iter_to_data=reduce(lambda form1,form2: form1.union(form2),access_relations) #Compose the unioned access relation with the iteration subspace # relation to remove conjunctions we are not interested in iter_to_data=self.iter_sub_space_relation.compose(iter_to_data.inverse()).inverse() #Create the ERSpec for the relation that is input to the reordering self.inputs.append(ERSpec( name='%s_input'%(self.name), input_bounds=mapir.full_iter_space.apply(self.iter_sub_space_relation), output_bounds=self.target_data_arrays[0].bounds.copy(), relation=iter_to_data, is_function=True, er_type='er_u1d')) print 'In DataPermuteTrans, input type %s.is_er_u1d()=%s'%(self.inputs[-1].get_var_name(),self.inputs[-1].is_er_u1d()) #Add the ERSpec to the MapIR mapir.add_er_spec(self.inputs[0]) self.print_progress("Calculated input ERSpec '%s' for transformation '%s'..."%(self.inputs[0].name,self.name)) self.print_detail(self.inputs[0]) #Calculate a specification for the explicit relation that is the # output of this data reordering. #This relation is a permutation of the original data space, permuted # based on the heuristics of the reordering algorithm. def calc_output(self,mapir): #Need to create a static description of the output of the reordering self.outputs.append(ERSpec( name=self.reordering_name, input_bounds=self.target_data_arrays[0].bounds.copy(), output_bounds=self.target_data_arrays[0].bounds.copy(), relation=self._data_reordering.copy(), is_permutation=True)) #Add the ERSpec to the MapIR mapir.add_er_spec(self.outputs[0]) self.print_progress("Calculated output ERSpec '%s' for transformation '%s'..."%(self.outputs[0].name,self.name)) self.print_detail(self.outputs[0]) #Update the MapIR based on this transformation def update_mapir(self,mapir): #Data spaces are not changed #Scattering functions are not changed #Update the access relations of all statements that access # the reordered data array(s) self.print_progress('Updating access relations...') for statement in mapir.get_statements(): for access_relation in statement.get_access_relations(): if access_relation.data_array in self.data_arrays: access_relation.iter_to_data=self._data_reordering.compose(access_relation.iter_to_data) #Update the idg based on this transformation def update_idg(self,mapir): #Add the ERG call node to the IDG erg_call_node=mapir.idg.get_node(IDGCall,calc_erg_call(self.name,self.erg_func_name,self.inputs,self.outputs)) #Collection of reorder call nodes reorder_call_nodes=[] #Add the input ERSpecs to the IDG for input_er_spec in self.inputs: #Get a node for the ERSpec input_er_spec_node=mapir.idg.get_node(IDGERSpec,input_er_spec) #Get a gen node for the ERSpec gen_input_er_spec_node=mapir.idg.get_node(IDGGenERSpec,input_er_spec) #Add dependence of the GenERSpec node to ERSpec node input_er_spec_node.add_dep(gen_input_er_spec_node) #Add dependence of the call to the input erg_call_node.add_dep(input_er_spec_node) #Add reorder call nodes for each data array to be reordered for data_array in self.data_arrays: #Build the list of arguments to the function call #Add the reorder call node for this data array to the IDG reorder_call_node=mapir.idg.get_node(IDGCall,calc_reorder_call(self.name,data_array,self.reordering_name,mapir)) #Add the reorder call node to the collection of reorder call nodes reorder_call_nodes.append(reorder_call_node) #Get the data array node before the reordering before_data_array_node=mapir.idg.get_node(IDGDataArray,VersionedDataArray(data_array)) #Add the dependence of the reorder call on the before data array reorder_call_node.add_dep(before_data_array_node) #Get the data array node after the reordering after_data_array_node=mapir.idg.get_node(IDGDataArray,VersionedDataArray(data_array)) #Add the dependence of the after data array node on the reorder call after_data_array_node.add_dep(reorder_call_node) #Add the output ERSpecs to the IDG for output_er_spec in self.outputs: #Get a node for the ERSpec output_er_spec_node=mapir.idg.get_node(IDGOutputERSpec,output_er_spec) #Add dependence of the output on the call output_er_spec_node.add_dep(erg_call_node) #Add dependences of the reorder calls on the output for reorder_call_node in reorder_call_nodes: reorder_call_node.add_dep(output_er_spec_node)
def testComposeArityFail2(self): from iegen import Relation relation1=Relation('{[]->[c]}') relation2=Relation('{[a,b,c,d]->[e]}') composed=relation1.compose(relation2)
def testComposeArityFail1(self): from iegen import Relation relation1=Relation('{[a,b]->[c]}') relation2=Relation('{[b]->[b]}') composed=relation1.compose(relation2)
# full iteration space print "==== Full Iteration Space" full_I = S1_full_I.union(S2_full_I.union(S3_full_I)) print full_I #### Modifying the access relations so that their source is the #### full iteration space. # A1, access relation for S1, targets data array x print "==== A1, access relation for S1" a1 = Relation("{[s,i]->[i] }") #print a1 #PrettyPrintVisitor().visit(a1) print "Modified a1, or a1_0" a1_0 = a1.compose(S1_sched.inverse()) #print a1_0 #PrettyPrintVisitor().visit(a1_0) # A4, access relation for S2, targets data array x print "==== A4, access relation for S2" a4 = Relation("{[s,i]->[k]: k=inter1(i)}") #print a4 #PrettyPrintVisitor().visit(a4) print "Modified a4, or a4_0" a4_0 = a4.compose(S2_sched.inverse()) #print a4_0 #PrettyPrintVisitor().visit(a4_0) # A8, access relation for S3, targets data array x
import iegen from iegen import Set,Relation,Symbolic import iegen.simplify # Enabling debug output #from iegen import IEGenObject # None corresponds to stdout #IEGenObject.settings.outputs['debug'].append(None) # Some example visitors from iegen.ast.visitor import FindConstraintVisitor, PrintASTVisitor from iegen.ast import Equality everything = Relation('{[c0,i,j] -> [c0,i,k] : c0=0 && i>=1 && i<=10 && k=f(j) }') everythingCompose = everything.compose(everything) # Calling a visitor that just prints "I'm here" messages at every AST node #PrintASTVisitor().visit(everythingCompose) from iegen.ast.visitor import PrettyPrintVisitor print '=====================' PrettyPrintVisitor().visit(everythingCompose) print everythingCompose T_I1_to_I2 = Relation("{[c0,s1,c1,i,c2] -> [c3,s2,c4,j,c5] : s1=s2 && c0=0 && c1=0 && c2=0 && c3=0 && c4=0 && c5=0 && i=j}").union( Relation("{[c6,s3,c7,ii,x] -> [c8,s4,c9,j,y] : s3=s4 && j = delta(ii) && c6=0 && c8=0 && c7=1 && c9=1 && x=y }")) print '=====================' PrettyPrintVisitor().visit(T_I1_to_I2) print T_I1_to_I2