def substitute(self, stmt): """ Given a procedure call statement, return the process body for the procedure. """ if isinstance(stmt, ast.StmtPcall) and \ self.sig.is_mobile(stmt.name) and \ self.sig.has_channel_params(stmt.name): defn = [x for x in self.ast.defs if x.name == stmt.name][0] proc = copy.deepcopy(defn.stmt) # Rename actual parameters for ocurrances of formals for (x, y) in zip(defn.formals, stmt.args): if x.type == T_VAL_SINGLE: proc.accept(SubElem(ast.ElemId(x.name), y)) elif x.type == T_REF_SINGLE: proc.accept(SubElem(ast.ElemId(x.name), y.elem)) elif x.type == T_REF_ARRAY: proc.accept(Rename(x.name, y.elem.name, y.elem.symbol)) elif x.type == T_CHANEND_SINGLE: proc.accept(SubElem(ast.ElemId(x.name), y.elem)) elif x.type == T_CHANEND_ARRAY: proc.accept(Rename(x.name, y.elem.name, y.elem.symbol)) else: assert 0 return proc else: return stmt
def create_range_conn(s, chan, i_elem, group): diff2 = group[0] elem0 = group[1][0][0] offset = target_loc(chan, elem0, scope) # Form the location expression if elem0.indices_value > 0: location = ast.ElemGroup( ast.ExprBinop( '-', i_elem, ast.ExprSingle(ast.ElemNumber(elem0.indices_value)))) else: location = i_elem location = ast.ExprBinop('*', ast.ElemNumber(diff2 + 1), ast.ExprSingle(location)) location = ast.ExprBinop('+', ast.ElemGroup(location), ast.ExprSingle(ast.ElemNumber(offset))) chanend = ast.ElemId(chan.chanend) chanend.symbol = Symbol(chan.chanend, self.chanend_type(chan), None, scope=T_SCOPE_PROC) connid = tab.lookup_connid(chan.name, elem0.index, scope) chanid = ast.ExprSingle(ast.ElemNumber(connid)) begin = elem0.indices_value end = group[1][-1][0].indices_value cond = ast.ExprBinop( '>=', i_elem, ast.ExprSingle(ast.ElemNumber(min(begin, end)))) master = tab.lookup_is_master(chan, elem0, scope) conn = ast.StmtConnect(chanend, chanid, location, self.connect_type(chan, master)) return ast.StmtIf(cond, conn, s) if s else conn
def create_tree_conn(tab, scope, chan, phase, group_size, base_indices_value, loc_base, loc_diff, connid_min, connid_offset, connid_diff, i_elem): location = ast.ExprBinop( '-', i_elem, ast.ExprSingle(ast.ElemNumber(base_indices_value))) location = ast.ExprBinop( '/', ast.ElemGroup(location), ast.ExprSingle(ast.ElemNumber(group_size))) location = ast.ExprBinop('*', ast.ElemNumber(loc_diff), ast.ExprSingle(ast.ElemGroup(location))) location = ast.ExprBinop('+', ast.ElemNumber(loc_base), ast.ExprSingle(ast.ElemGroup(location))) chanend = ast.ElemId(chan.chanend) chanend.symbol = Symbol(chan.chanend, self.chanend_type(chan), None, scope=T_SCOPE_PROC) elem0 = chan.elems[phase] #connid = ast.ExprBinop('+', i_elem, # ast.ExprSingle(ast.ElemNumber(connid_offset))) connid = ast.ExprBinop('-', i_elem, ast.ExprSingle(ast.ElemNumber(phase))) connid = ast.ExprBinop('rem', ast.ElemGroup(connid), ast.ExprSingle(ast.ElemNumber(group_size))) connid = ast.ExprBinop('*', ast.ElemGroup(connid), ast.ExprSingle(ast.ElemNumber(connid_diff))) connid = ast.ExprBinop('+', ast.ElemGroup(connid), ast.ExprSingle(ast.ElemNumber(connid_min))) master = tab.lookup_is_master(chan, elem0, scope) return ast.StmtConnect(chanend, connid, location, self.connect_type(chan, master))
def create_single_conn(s, chan, scope, i_elem, elem): debug(self.debug, 'New connection for index {}'.format(elem.index)) master = tab.lookup_is_master(chan, elem, scope) if master: location = ast.ExprSingle( ast.ElemNumber( tab.lookup_slave_location(chan.name, elem.index, scope))) else: location = ast.ExprSingle( ast.ElemNumber( tab.lookup_master_location(chan.name, elem.index, scope))) chanend = ast.ElemId(chan.chanend) chanend.symbol = Symbol(chan.chanend, self.chanend_type(chan), None, scope=T_SCOPE_PROC) connid = tab.lookup_connid(chan.name, elem.index, scope) chanid = ast.ExprSingle(ast.ElemNumber(connid)) cond = ast.ExprBinop( '=', i_elem, ast.ExprSingle(ast.ElemNumber(elem.indices_value))) conn = ast.StmtConnect(chanend, chanid, location, self.connect_type(chan, master)) return ast.StmtIf(cond, conn, s) if s != None else conn
def subscript_channel(self, indices, tab, stmt, name, expr, chanend, chan_set): """ Expand the use of a channel subscript over a set of iterators and determine for each the value of its index and location, and then add this to the channel table and corresponding expansion. """ #print(Printer().expr(stmt.location)) chan_elems = [] # Iterate over cartesian product of iterator ranges ranges = [ range(x.base_value, x.base_value + x.count_value) for x in indices ] for x in product(*ranges): # Deep copy expressions so we can modify them index_expr = copy.deepcopy(expr) location_expr = copy.deepcopy(stmt.location) # Substitute index variables for values index_values = [] for (y, z) in zip(indices, x): index_expr.accept( SubElem(ast.ElemId(y.name), ast.ElemNumber(z))) location_expr.accept( SubElem(ast.ElemId(y.name), ast.ElemNumber(z - y.base_value))) index_values.append(z) # Evaluate the expressions index_value = EvalExpr().expr(index_expr) location_value = EvalExpr().expr(location_expr) # Add to the table tab.insert(name, index_value, location_value, chanend, chan_set) # Add the expanded channel use to a list chan_elems.append( ChanElem(index_value, location_value, indices, index_values)) debug( self.debug, ' {}[{}] at {} (chanend {})'.format(name, index_value, location_value, chanend)) return chan_elems
def insert(self, stmt): # Create the assignment e = ast.ElemId(PROC_ID_VAR) e.symbol = Symbol(PROC_ID_VAR, T_VAR_SINGLE, None, T_SCOPE_PROC) s = ast.StmtAss(e, ast.ExprSingle(ast.ElemFcall('procid', []))) # Add the assignent in sequence with the existing body process #if isinstance(node.stmt, ast.StmtSeq): # node.stmt.stmt.insert(0, s) #else: return ast.StmtSeq([s, stmt])
def single_channel(self, indices, tab, stmt, name, chanend, chan_set): """ Process a single (non-subscripted) channel use by evaluating its location and then adding it to the channel table and returning it as an element. Single channels may appear only in replicators in the client process of a server, hence we evaluate their location over the incident indices. """ #print(Printer().expr(stmt.location)) if indices: elem = ChanElem(None, None, None, None) # Iterate over cartesian product of iterator ranges ranges = [ range(x.base_value, x.base_value + x.count_value) for x in indices ] for x in product(*ranges): # Deep copy expressions so we can modify them location_expr = copy.deepcopy(stmt.location) # Substitute index variables for values index_values = [] for (y, z) in zip(indices, x): location_expr.accept( SubElem(ast.ElemId(y.name), ast.ElemNumber(z - y.base_value))) # Evaluate the expressions location_value = EvalExpr().expr(location_expr) # Add to the table tab.insert(name, None, location_value, chanend, chan_set) # Add the location to the channel element elem.add_location(location_value) debug( self.debug, ' {} at {} (chanend {})'.format(name, location_value, chanend)) elem.location = elem.locations[0] return [elem] else: location_value = EvalExpr().expr(stmt.location) tab.insert(name, None, location_value, chanend, chan_set) debug( self.debug, ' {} at {} (chanend {})'.format(name, location_value, chanend)) return [ChanElem(None, location_value, None, None)]
def gen_single_conn(self, tab, scope, chan): """ Generate a connection for a single channel declaration. 'chan' is a ChanElemSet with one element. """ elem = chan.elems[0] chanend = ast.ElemId(chan.chanend) chanend.symbol = Symbol(chan.chanend, self.chanend_type(chan), None, scope=T_SCOPE_PROC) connid = tab.lookup_connid(chan.name, elem.index, scope) chanid = ast.ExprSingle(ast.ElemNumber(connid)) # Server-end connections don't need targets if chan.symbol.scope == T_SCOPE_SERVER: return ast.StmtConnect(chanend, chanid, ast.ExprSingle(ast.ElemNumber(0)), self.connect_type(chan, False)) # All other connections do else: master = tab.lookup_is_master(chan, elem, scope) if master: location = ast.ExprSingle( ast.ElemNumber( tab.lookup_slave_location(chan.name, elem.index, scope))) else: location = ast.ExprSingle( ast.ElemNumber( tab.lookup_master_location(chan.name, elem.index, scope))) chanend = ast.ElemId(chan.chanend) chanend.symbol = Symbol(chan.chanend, self.chanend_type(chan), None, scope=T_SCOPE_PROC) return ast.StmtConnect(chanend, chanid, location, self.connect_type(chan, master))
def indices_expr(indices): """ Given a set of indices, return an expression computing their combined value. """ dims = [x.count_value for x in indices] r = None for (i, x) in enumerate(indices): c = reduce(lambda x, y: x * y, dims[i + 1:], 1) c_expr = ast.ExprSingle(ast.ElemNumber(c)) eid = ast.ElemId(x.name) eid.symbol = Symbol(x.name, T_VAL_SINGLE, None, scope=T_SCOPE_PROC) e = ast.ExprBinop('*', eid, c_expr) if c > 1 else ast.ExprSingle(eid) r = e if r == None else ast.ExprBinop('+', ast.ElemGroup(r), e) return r
def rename_chan(self, elem, chans): """ Rename a channel (elem) using the set of channel elements (ChanElemSet) which will contain the name of the channel end allocated to this instance. """ if isinstance(elem, ast.ElemId) and elem.symbol.type == T_CHAN_SINGLE: for x in chans: if elem.name == x.name: t = T_CHANEND_SINGLE if x.symbol.scope == T_SCOPE_SERVER: t = T_CHANEND_SERVER_SINGLE elif x.symbol.scope == T_SCOPE_CLIENT: t = T_CHANEND_CLIENT_SINGLE s = Symbol(x.chanend, t, T_SCOPE_PROC) e = ast.ElemId(x.chanend) e.symbol = s #print('renamed {} to {} as type {}'.format(x.name, x.chanend, t)) return ast.ExprSingle(e) elif isinstance(elem, ast.ElemSub) and elem.symbol.type == T_CHAN_ARRAY: for x in chans: if elem.name == x.name and CmpExpr().expr(elem.expr, x.expr): t = T_CHANEND_SINGLE if x.symbol.scope == T_SCOPE_SERVER: t = T_CHANEND_SERVER_SINGLE elif x.symbol.scope == T_SCOPE_CLIENT: t = T_CHANEND_CLIENT_SINGLE s = Symbol(x.chanend, t, T_SCOPE_PROC) e = ast.ElemId(x.chanend) e.symbol = s #print('renamed {} to {} as type {}'.format(x.name, x.chanend, t)) return ast.ExprSingle(e) else: # Don't worry about chanends return ast.ExprSingle(elem)
def distribute_stmt(self, m, elem_t, elem_n, elem_m, base, indices, proc_actuals, formals, pcall): """ Create the distribution process body statement. """ # Setup some useful expressions name = self.sig.unique_process_name() elem_x = ast.ElemId('_x') expr_x = ast.ExprSingle(elem_x) expr_t = ast.ExprSingle(elem_t) expr_n = ast.ExprSingle(elem_n) expr_m = ast.ExprSingle(elem_m) elem_base = ast.ElemNumber(base) expr_base = ast.ExprSingle(elem_base) # Replace ocurrances of index variables i with i = f(_t) divisor = m for x in indices: divisor = floor(divisor / x.count_value) # Calculate the index i as a function of _t and the dimensions. e = ast.ExprBinop( 'rem', ast.ElemGroup( ast.ExprBinop('/', elem_t, ast.ExprSingle(ast.ElemNumber(divisor)))), ast.ExprSingle(ast.ElemNumber(x.count_value))) if x.base_value > 0: e = ast.ExprBinop('+', ast.ElemNumber(x.base_value), ast.ExprSingle(ast.ElemGroup(e))) # Then replace it for each ocurrance of i for y in pcall.args: y.accept(SubElem(ast.ElemId(x.name), ast.ElemGroup(e))) d = ast.ExprBinop('+', elem_t, ast.ExprSingle(elem_x)) d = form_location(self.sym, elem_base, d, 1) # Create on the on statement on_stmt = ast.StmtOn( d, ast.StmtPcall(name, [ ast.ExprBinop('+', elem_t, ast.ExprSingle(elem_x)), expr_x, ast.ExprBinop('-', elem_m, ast.ExprSingle(elem_x)) ] + proc_actuals)) on_stmt.location = None # Conditionally recurse {d()|d()} or d() s1 = ast.StmtIf( # if m > n/2 ast.ExprBinop('>', elem_m, ast.ExprSingle(elem_x)), # then ast.StmtPar( [], [ # on id()+t+n/2 do d(t+n/2, n/2, m-n/2, ...) on_stmt, # d(t, n/2, n/2) ast.StmtPcall(name, [expr_t, expr_x, expr_x] + proc_actuals), ], False), # else d(t, n/2, m) ast.StmtPcall(name, [expr_t, expr_x, expr_m] + proc_actuals)) # _x = n/2 ; s1 n_div_2 = ast.ExprBinop('>>', elem_n, ast.ExprSingle(ast.ElemNumber(1))) s2 = ast.StmtSeq([], [ast.StmtAss(elem_x, n_div_2), s1]) # if n = 1 then process() else s1 s3 = ast.StmtIf( ast.ExprBinop('=', elem_n, ast.ExprSingle(ast.ElemNumber(1))), pcall, s2) # Create the local declarations decls = [ast.VarDecl(elem_x.name, T_VAR_SINGLE, None)] s4 = ast.StmtSeq(decls, [s3]) # Create the definition d = ast.ProcDef(name, T_PROC, formals, s4) return d
def transform_rep(self, stmt): """ Convert a replicated parallel statement into a divide-and-conquer form. - Return the tuple (process-def, process-call) We only allow replicators where their location is known; i.e. stmt.location is an Expr(Number(x)). """ assert isinstance(stmt, ast.StmtRep) assert isinstance(stmt.stmt, ast.StmtPcall) assert isinstance(stmt.location, ast.ExprSingle) assert isinstance(stmt.location.elem, ast.ElemNumber) pcall = stmt.stmt # The context of the procedure call is each variable occurance in the # set of arguments. context = FreeVars().compute(pcall) assert not stmt.m == None #assert not stmt.f == None n = util.next_power_of_2(stmt.m) # Create new variables formals = [] # Formals for the new distribution process actuals = [] # Actuals for the new distribution process proc_actuals = [] # All other live-in variables elem_t = ast.ElemId('_t') # Interval base elem_n = ast.ElemId('_n') # Interval width elem_m = ast.ElemId('_m') # Processes in interval #print(Printer().expr(stmt.location)) base = stmt.location.elem.value # Populate the distribution and replicator indices formals.append(ast.Param('_t', T_VAL_SINGLE, None)) formals.append(ast.Param('_n', T_VAL_SINGLE, None)) formals.append(ast.Param('_m', T_VAL_SINGLE, None)) actuals.append(ast.ExprSingle(ast.ElemNumber(0))) actuals.append(ast.ExprSingle(ast.ElemNumber(n))) actuals.append(ast.ExprSingle(ast.ElemNumber(stmt.m))) # For each non-index free-variable of the process call for x in context - set([x for x in stmt.indices]): # Add each unique variable ocurrance from context as a formal param formals.append( ast.Param(x.name, rep_var_to_param[x.symbol.type], x.symbol.expr)) # If the actual is an array subscript or slice, we only pass the id. if isinstance(x, ast.ElemSlice) or isinstance(x, ast.ElemSub): e = ast.ElemId(x.name) e.symbol = x.symbol proc_actuals.append(ast.ExprSingle(e)) else: proc_actuals.append(ast.ExprSingle(copy.copy(x))) # Add the extra actual params to the distribution actuals actuals.extend(proc_actuals) # Create the process definition and perform semantic analysis to # update symbol bindings. d = self.distribute_stmt(stmt.m, elem_t, elem_n, elem_m, base, stmt.indices, proc_actuals, formals, pcall) #Printer().defn(d, 0) self.sem.defn(d) # Create the corresponding call. c = ast.StmtPcall(d.name, actuals) self.sig.insert(d.type, d) return (d, c)
def remove_bound(self, c, decls): decl_elems = set() for x in decls: decl_elems.add(ast.ElemId(x.name)) return c - decl_elems
def p_left_name(self, p): 'left : name' p[0] = ast.ElemId(p[1], self.coord(p))
def p_elem_name(self, p): 'elem : name' p[0] = ast.ElemId(p[1], self.coord(p))
def stmt_to_process(self, stmt, indices=[]): """ Convert a statement into a process definition. - Create the definition node. - Create the corresponding Pcall node. - Insert the definition into the signature table. - Return the tuple (process-def, process-call) We pass process definitions recursively up the AST. Sets for live-in and local decls (for non-live, non-array targets). We want to add formal parameters for any live-in variable and for any variable that is both live-in and live-out, for a statement s:: Live-over(s) = (live-in(s) | live-out(s)) & free(s) where | is set union and & is set intersection. Also, the index varible (rep_var) of replicator statements must be treated as a value and not a variable in its use as a parameter (transform replicator stage) and passed-by-reference. """ assert isinstance(stmt, ast.Stmt) #out = set() #[out.update(y.inp) for y in succ.pred] #print('Successors: {}'.format(' '.join(['{}'.format(x.pred) for x in succ]))) #[print(y.out) for y in succ.pred] out = LiveOut().compute(stmt) free = FreeVars().compute(stmt) live = free & (stmt.inp | out) local_decls = free - live debug(self.debug, '==========================================') debug(self.debug, 'Free: {}'.format(free)) debug(self.debug, 'Live-in: {}'.format(stmt.inp)) debug(self.debug, 'Live-out: {}'.format(out)) debug(self.debug, 'live-over: {}'.format(live)) debug(self.debug, 'Local decls: {}'.format(local_decls)) #Printer().stmt(stmt) # Create the formal and actual paramerer and local declaration lists formals = [] formal_set = set() actuals = [] decls = [] # Deal with the index variable of a replicator statement: add it as a # single value (not a variable) to the formals and as-is to the actuals, # then remove it from the variable live-in set and locals so it is not # declared again. for x in indices: formals.append(ast.Param(x.name, T_VAL_SINGLE, None)) formal_set.add(x) actuals.append(ast.ExprSingle(ast.ElemId(x.name))) live -= set([x]) local_decls -= set([x]) # Remove values from local declarations s = set() for x in local_decls: if not( x.symbol.type == T_VAL_SINGLE and \ (x.symbol.scope == T_SCOPE_PROGRAM or \ x.symbol.scope == T_SCOPE_SYSTEM)): s.add(x) local_decls = s # Replicated parallel statements are more restrivtive var_to_param = rep_var_to_param if len( indices) > 0 else par_var_to_param # For each variable in the live-in set add accordingly to formals and actuals. for x in live: # Don't include constant values. if x.symbol.type == T_VAL_SINGLE and \ (x.symbol.scope == T_SCOPE_PROGRAM or \ x.symbol.scope == T_SCOPE_SYSTEM): continue # All parameters are added as formals with the appropriate conversion p = ast.Param(x.name, var_to_param[x.symbol.type], x.symbol.expr) p.symbol = x.symbol formals.append(p) formal_set.add(x) # If the actual is an array subscript or slice, we only pass the id. if isinstance(x, ast.ElemSlice) or isinstance(x, ast.ElemSub): e = ast.ElemId(x.name) e.symbol = x.symbol actuals.append(ast.ExprSingle(e)) else: actuals.append(ast.ExprSingle(copy.copy(x))) # For arrays with lengths specified with variables (i.e. arrays passed by # reference) then we must include the length as the next parameter as long # as it is not already in the live set OR the formals and it's not a # defined value. if (x.symbol.type.form == 'array' and isinstance(x.symbol.expr, ast.ExprSingle) and not x.symbol.expr.elem in live and not x.symbol.expr.elem in formal_set and x.symbol.value == None): p = ast.Param(x.symbol.expr.elem.name, T_VAL_SINGLE, None) formals.append(p) formal_set.add(x.symbol.expr.elem) actuals.append(x.symbol.expr) # Create a unique name name = self.sig.unique_process_name() # Create the local declarations (excluding values) [decls.append(self.create_decl(x)) for x in local_decls] # Create the new process definition if isinstance(stmt, ast.StmtSeq) or isinstance(stmt, ast.StmtPar): stmt.decls += decls else: stmt = ast.StmtSeq(decls, [stmt]) d = ast.ProcDef(name, T_PROC, formals, stmt) # perform semantic analysis to update symbol bindings. if self.debug: Printer().defn(d, 0) debug(self.debug, '==========================================') self.sem.defn(d) # Create the corresponding call. c = ast.StmtPcall(name, actuals) return (d, c)
def gen_array_conn(self, tab, scope, chan): """ Generate a conncection for an array channel declaration. We must analyse the subscript by generating nested conditional statements. 'chan' is a ChanElemSet with multiple elements. """ def target_loc(chan, elem, scope): master = tab.lookup_is_master(chan, elem, scope) return (tab.lookup_slave_location(chan.name, elem.index, scope) if master else tab.lookup_master_location( chan.name, elem.index, scope)) def create_single_conn(s, chan, scope, i_elem, elem): debug(self.debug, 'New connection for index {}'.format(elem.index)) master = tab.lookup_is_master(chan, elem, scope) if master: location = ast.ExprSingle( ast.ElemNumber( tab.lookup_slave_location(chan.name, elem.index, scope))) else: location = ast.ExprSingle( ast.ElemNumber( tab.lookup_master_location(chan.name, elem.index, scope))) chanend = ast.ElemId(chan.chanend) chanend.symbol = Symbol(chan.chanend, self.chanend_type(chan), None, scope=T_SCOPE_PROC) connid = tab.lookup_connid(chan.name, elem.index, scope) chanid = ast.ExprSingle(ast.ElemNumber(connid)) cond = ast.ExprBinop( '=', i_elem, ast.ExprSingle(ast.ElemNumber(elem.indices_value))) conn = ast.StmtConnect(chanend, chanid, location, self.connect_type(chan, master)) return ast.StmtIf(cond, conn, s) if s != None else conn def create_range_conn(s, chan, i_elem, group): diff2 = group[0] elem0 = group[1][0][0] offset = target_loc(chan, elem0, scope) # Form the location expression if elem0.indices_value > 0: location = ast.ElemGroup( ast.ExprBinop( '-', i_elem, ast.ExprSingle(ast.ElemNumber(elem0.indices_value)))) else: location = i_elem location = ast.ExprBinop('*', ast.ElemNumber(diff2 + 1), ast.ExprSingle(location)) location = ast.ExprBinop('+', ast.ElemGroup(location), ast.ExprSingle(ast.ElemNumber(offset))) chanend = ast.ElemId(chan.chanend) chanend.symbol = Symbol(chan.chanend, self.chanend_type(chan), None, scope=T_SCOPE_PROC) connid = tab.lookup_connid(chan.name, elem0.index, scope) chanid = ast.ExprSingle(ast.ElemNumber(connid)) begin = elem0.indices_value end = group[1][-1][0].indices_value cond = ast.ExprBinop( '>=', i_elem, ast.ExprSingle(ast.ElemNumber(min(begin, end)))) master = tab.lookup_is_master(chan, elem0, scope) conn = ast.StmtConnect(chanend, chanid, location, self.connect_type(chan, master)) return ast.StmtIf(cond, conn, s) if s else conn def create_tree_conn(tab, scope, chan, phase, group_size, base_indices_value, loc_base, loc_diff, connid_min, connid_offset, connid_diff, i_elem): location = ast.ExprBinop( '-', i_elem, ast.ExprSingle(ast.ElemNumber(base_indices_value))) location = ast.ExprBinop( '/', ast.ElemGroup(location), ast.ExprSingle(ast.ElemNumber(group_size))) location = ast.ExprBinop('*', ast.ElemNumber(loc_diff), ast.ExprSingle(ast.ElemGroup(location))) location = ast.ExprBinop('+', ast.ElemNumber(loc_base), ast.ExprSingle(ast.ElemGroup(location))) chanend = ast.ElemId(chan.chanend) chanend.symbol = Symbol(chan.chanend, self.chanend_type(chan), None, scope=T_SCOPE_PROC) elem0 = chan.elems[phase] #connid = ast.ExprBinop('+', i_elem, # ast.ExprSingle(ast.ElemNumber(connid_offset))) connid = ast.ExprBinop('-', i_elem, ast.ExprSingle(ast.ElemNumber(phase))) connid = ast.ExprBinop('rem', ast.ElemGroup(connid), ast.ExprSingle(ast.ElemNumber(group_size))) connid = ast.ExprBinop('*', ast.ElemGroup(connid), ast.ExprSingle(ast.ElemNumber(connid_diff))) connid = ast.ExprBinop('+', ast.ElemGroup(connid), ast.ExprSingle(ast.ElemNumber(connid_min))) master = tab.lookup_is_master(chan, elem0, scope) return ast.StmtConnect(chanend, connid, location, self.connect_type(chan, master)) def conn_diff_groups(chan, s, i_elem, d=DEBUG_COMPRESSION): """ Compress connecitons based on the difference between differences of indices value and destination. """ # Build a list of channel elements and index-dest differences diffs = [] for x in chan.elems: diff = target_loc(chan, x, scope) - x.indices_value diffs.append((x, diff)) debug(d, 'Differences for chan {}:'.format(chan.name)) for (elem, diff) in diffs: connid = tab.lookup_connid(chan.name, elem.index, scope) debug( d, ' {:>3}: [{}]:{} - {}'.format(elem.indices_value, elem.index, connid, diff)) # Group consecutive elements with a constant second difference groups = [] newgroup = True for ((elemA, diffA), (elemB, diffB)) in zip(diffs[:-1], diffs[1:]): diff2 = diffB - diffA connid = tab.lookup_connid(chan.name, elemB.index, scope) master = tab.lookup_is_master(chan, elemB, scope) if newgroup: groups.append((diff2, [(elemA, diffA)])) groupdiff2 = diff2 groupconnid = tab.lookup_connid(chan.name, elemA.index, scope) groupmaster = tab.lookup_is_master(chan, elemA, scope) newgroup = False if (groupdiff2 == diff2 and groupconnid == connid and groupmaster == master): groups[-1][1].append((elemB, diffB)) else: newgroup = True if newgroup: groups.append((None, [diffs[-1]])) debug(d, 'Groups:') for x in groups: diff2 = x[0] elem0 = x[1][0][0] offset = target_loc(chan, elem0, scope) debug(d, ' diff2: {}'.format(diff2)) debug(d, ' offset: {}'.format(offset)) debug(d, ' base: {}'.format(elem0.indices_value)) if len(x[1]) > 1: for (i, (elem, diff)) in enumerate(x[1]): loc = target_loc(chan, elem, scope) computed = ((diff2 + 1) * (elem.indices_value - elem0.indices_value)) + offset assert computed == loc debug( d, ' {:>3}: [{:>3}]->{:>3}, diff: {:>3}, computed: {}' .format(elem.indices_value, elem.index, loc, diff, computed)) else: debug( d, ' {:>3}: [{:>3}]->{:>3}'.format( elem0.indices_value, elem0.index, offset)) # If compression was inneffective then abort if len(groups) == len(chan.elems): debug(d, 'Aborting group diff compression.') return None debug(d, 'Diff compression successful.') # Construct connection syntax s = None for x in groups: elem0 = x[1][0][0] if len(x[1]) == 1: s = create_single_conn(s, chan, scope, i_elem, elem0) else: s = create_range_conn(s, chan, i_elem, x) return s def conn_tree_groups(chan, s, i_elem, d=DEBUG_COMPRESSION): """ Compress connections based on monotonically increasing or decreasing sets with the same target destination. Within a set, connection IDs can be monotonically increasing or decreasing. """ locs = [] debug(d, 'Locations:') for x in chan.elems: loc = target_loc(chan, x, scope) locs.append((x, loc)) debug( d, ' {:>4} : {}[{}] -> {}'.format(x.indices_value, chan.name, x.index, loc)) # Separate the first odd element if there is one phase = 0 if locs[0][1] != locs[1][1]: odd_elem = locs[0][0] locs = locs[1:] phase = 1 # Count the group size group_size = 1 while group_size < len(locs) and locs[group_size - 1][1] == locs[group_size][1]: group_size += 1 # Only consider connections with more than one group if len(locs) <= group_size + 1: debug(d, 'Aborting tree compression.') return None # Set parameters loc_diff = locs[group_size][1] - locs[group_size - 1][1] loc_base = locs[0][1] base_indices_value = locs[0][0].indices_value connidA = tab.lookup_connid(chan.name, locs[0][0].index, scope) connidB = tab.lookup_connid(chan.name, locs[1][0].index, scope) connid_min = min(connidA, connidB) connid_diff = max(connidA, connidB) - connid_min connid_offset = (phase + (1 if connidA > connidB else 0)) % 2 # Print some debug info debug(d, 'Attempting tree compression.') debug(d, ' Group size: {}'.format(group_size)) debug(d, ' Location base: {}'.format(loc_base)) debug(d, ' Location diff: {}'.format(loc_diff)) debug(d, ' Base ival: {}'.format(base_indices_value)) debug(d, ' ConnID base: {}'.format(connidA)) debug(d, ' ConnID diff: {}'.format(connid_diff)) # Check each group contains the same location debug(d, 'Checking groups...') i = 0 while i * group_size < len(locs): debug(d, ' Group {}'.format(i)) for j in range(1, group_size): if locs[i * group_size][1] != locs[(i * group_size) + j][1]: debug(d, 'Aborting tree compression.') return None i += 1 # Check each step between groups has same location and connection diffs debug(d, 'Checking diffs...') for (i, (x, y)) in enumerate( zip(locs[1::group_size], locs[group_size::group_size])): debug( d, ' Group {} and {}: {} and {}'.format( i, i + 1, y[1], x[1])) connidX = tab.lookup_connid(chan.name, x[0].index, scope) connidY = tab.lookup_connid(chan.name, y[0].index, scope) connid_diff_ = connidX - connidY if y[1] - x[1] != loc_diff or connid_diff != connid_diff_: debug(d, 'Aborting tree compression.') return None # Check matching computed location debug(d, 'Checking computed...') debug(d, 'connid_min = {}'.format(connid_min)) debug(d, 'connid_off = {}'.format(connid_offset)) if phase == 1: connid = tab.lookup_connid(chan.name, odd_elem.index, scope) debug(d, ' {}: connid={}'.format(odd_elem.indices_value, connid)) for (elem, loc) in locs: computed_loc = loc_base + (loc_diff * (math.floor( ((elem.indices_value - base_indices_value)) / group_size))) connid = tab.lookup_connid(chan.name, elem.index, scope) #computed_connid = (connid_min + # ((elem.indices_value + connid_offset) % group_size) * connid_diff) computed_connid = (connid_min + ( (elem.indices_value - phase) % group_size) * connid_diff) debug( d, ' {}: connid={}, loc={} computed({}, {})'.format( elem.indices_value, connid, loc, computed_connid, computed_loc)) assert computed_loc == loc assert computed_connid == connid debug(d, 'Tree compression successful.') # Construct connection syntax if phase == 0: return create_tree_conn(tab, scope, chan, phase, group_size, base_indices_value, loc_base, loc_diff, connid_min, connid_offset, connid_diff, i_elem) else: s = create_tree_conn(tab, scope, chan, phase, group_size, base_indices_value, loc_base, loc_diff, connid_min, connid_offset, connid_diff, i_elem) return create_single_conn(s, chan, scope, i_elem, odd_elem) def conn_singles(chan, s, i_elem, d=DEBUG_COMPRESSION): """ Create (uncompressed) connections for each case. """ debug(d, 'Creating uncompressed connection range.') for x in chan.elems: s = create_single_conn(s, chan, scope, i_elem, x) debug( d, ' {}: {}[{}]'.format(x.indices_value, chan.name, x.index)) return s # Sort the channel elements into increasing order of indices chan.elems = sorted(chan.elems, key=lambda x: x.indices_value) # Compress conditional connections and return the AST construction i_expr = indices_expr(chan.indices) i_elem = ast.ElemId('_i') i_elem.symbol = Symbol(i_elem.name, T_VAR_SINGLE, scope=T_SCOPE_BLOCK) s = None if COMPRESS: s = conn_tree_groups(chan, s, i_elem) s = conn_diff_groups(chan, s, i_elem) if s == None else s s = conn_singles(chan, s, i_elem) if s == None else s s = [ast.StmtAss(i_elem, i_expr), s] s = ast.StmtSeq([ast.VarDecl(i_elem.name, T_VAR_SINGLE, None)], s) return s