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 form_location(sym, base, offset, compression): """ Given the base id (ElemId), offset (Expr) and compression ratio (integer), produce an expression for a location:: location = (base + (off/comp)) rem NUM_CORES """ assert isinstance(base, ast.Elem) or isinstance(base, ast.Expr) assert isinstance(offset, ast.Expr) if isinstance(base, ast.Expr): base = ast.ElemGroup(base) # Offset loc = offset # Apply compression #if compression > 1: # loc = ast.ExprBinop('/', ast.ElemGroup(loc), # ast.ExprSingle(ast.ElemNumber(compression))) #elem_numcores = ast.ElemId(SYS_NUM_CORES_CONST) #elem_numcores.symbol = sym.lookup(SYS_NUM_CORES_CONST) # Apply 'rem NUM_CORES' to base + (off/comp) loc = ast.ExprSingle( ast.ElemGroup( ast.ExprBinop('+', base, ast.ExprSingle(ast.ElemGroup(loc))))) #ast.ExprBinop('rem', ast.ExprSingle(elem_numcores)) v = EvalExpr().expr(loc) loc = loc if v == None else ast.ExprSingle(ast.ElemNumber(v)) return loc
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 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 stmt_server(self, node, parent, d): """ For server statements the server and client processes are overlaid. """ if node.distribute: debug(self.debug, 'd before server = {}'.format(d)) e = self.stmt(node.server, parent, d) debug(self.debug, 'd after server = {}'.format(e)) node.client = ast.StmtOn(ast.ExprSingle(ast.ElemNumber(d)), node.client) e += self.stmt(node.client, parent, d) debug(self.debug, 'd after client = {}'.format(e)) node.distribute = False return e else: debug(self.debug, 'd before server = {}'.format(d)) x = self.stmt(node.server, parent, d) debug(self.debug, 'd after server = {}'.format(x)) y = self.stmt(node.client, parent, d) debug(self.debug, 'd after client = {}'.format(y)) return max(x, y)
def stmt_par(self, node, parent, d): """ For processes in parallel composition, add 'on' prefixes to provide simple compile-time distribution. If any process is already prefixed with an 'on', then do not add any (this is mainly for the test cases). """ if node.distribute: if any([isinstance(x, ast.StmtOn) for x in node.stmt]): self.errorlog.report_error( "parallel composition contains 'on's") return 0 e = self.stmt(node.stmt[0], parent, d) debug(self.debug, 'd before par = {}'.format(d)) for (i, x) in enumerate(node.stmt[1:]): node.stmt[i + 1] = ast.StmtOn( ast.ExprSingle(ast.ElemNumber(d + e)), x) e += self.stmt(x, parent, d + e) debug(self.debug, 'd after par = {}'.format(d)) node.distribute = False return e else: return 0
def stmt_on(self, node, l): node.location = l # Try and evaluate this 'target' expression v = EvalExpr().expr(node.expr) k = ast.ExprSingle(ast.ElemNumber(v)) if v != None else l self.stmt(node.stmt, k)
def defn(self, node): node.location = ast.ElemNumber(0) if node.name == 'main': self.stmt(node.stmt, ast.ExprSingle(node.location)) else: self.stmt(node.stmt, ast.ExprSingle(node.location))
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 p_elem_number(self, p): '''elem : HEXLITERAL | DECLITERAL | BINLITERAL''' p[0] = ast.ElemNumber(p[1], self.coord(p))