예제 #1
0
  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
예제 #2
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
예제 #3
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))
예제 #4
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
예제 #5
0
    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
예제 #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 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)]
예제 #8
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))
예제 #9
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
예제 #10
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)
예제 #11
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
예제 #12
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)
예제 #13
0
 def remove_bound(self, c, decls):
   decl_elems = set()
   for x in decls:
     decl_elems.add(ast.ElemId(x.name))
   return c - decl_elems
예제 #14
0
 def p_left_name(self, p):
   'left : name'
   p[0] = ast.ElemId(p[1], self.coord(p)) 
예제 #15
0
 def p_elem_name(self, p):
   'elem : name'
   p[0] = ast.ElemId(p[1], self.coord(p)) 
예제 #16
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)
예제 #17
0
    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