예제 #1
0
 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
예제 #2
0
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
예제 #3
0
        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
예제 #4
0
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
예제 #5
0
 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))
예제 #6
0
    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])
예제 #7
0
 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))
예제 #8
0
    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)
예제 #9
0
 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)
예제 #10
0
 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
예제 #11
0
 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))
예제 #12
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)
예제 #13
0
 def p_expr_single(self, p):
   'expr : elem' 
   p[0] = ast.ExprSingle(p[1])
예제 #14
0
    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
예제 #15
0
    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)
예제 #16
0
 def p_right_single(self, p):
   'right : elem'
   p[0] = ast.ExprSingle(p[1])
예제 #17
0
    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)