예제 #1
0
    def transform(self, node):
        if not isinstance(node, Program):
            return node

        offsets = 0
        changed = set([None])  # for getting rid of None
        tr = Transformer(node)
        for i, cur in tr:
            refs = cur.prereferences().movepointer(offsets)
            updates = cur.preupdates().movepointer(offsets)

            zerorefs = set(refs.unsure) - changed
            if zerorefs:
                tr.prepend(*[SetMemory(j - offsets, 0) for j in zerorefs])
                changed.update(zerorefs)
            changed.update(updates.unsure)

            ioffsets = cur.offsets()
            if ioffsets is None: break
            offsets += ioffsets

        return node
예제 #2
0
    def transform(self, node):
        if not isinstance(node, Program):
            return node

        offsets = 0
        changed = set([None]) # for getting rid of None
        tr = Transformer(node)
        for i, cur in tr:
            refs = cur.prereferences().movepointer(offsets)
            updates = cur.preupdates().movepointer(offsets)

            zerorefs = set(refs.unsure) - changed
            if zerorefs:
                tr.prepend(*[SetMemory(j - offsets, 0) for j in zerorefs])
                changed.update(zerorefs)
            changed.update(updates.unsure)

            ioffsets = cur.offsets()
            if ioffsets is None: break
            offsets += ioffsets

        return node
예제 #3
0
    def _transform(self, node):
        laststr = []
        tr = Transformer(node)
        for i, cur in tr:
            if isinstance(cur, Output) and cur.expr.simple():
                tr.replace()
                laststr.append(chr(int(cur.expr) & 0xff))
            elif isinstance(cur, OutputConst):
                tr.replace()
                laststr.append(cur.str)
            elif not cur.pure(): # I/O cannot be reordered!
                if laststr:
                    tr.prepend(OutputConst(''.join(laststr)))
                laststr = []

        if laststr:
            node.append(OutputConst(''.join(laststr)))

        return node
예제 #4
0
    def _transform(self, node):
        laststr = []
        tr = Transformer(node)
        for i, cur in tr:
            if isinstance(cur, Output) and cur.expr.simple():
                tr.replace()
                laststr.append(chr(int(cur.expr) & 0xFF))
            elif isinstance(cur, OutputConst):
                tr.replace()
                laststr.append(cur.str)
            elif not cur.pure():  # I/O cannot be reordered!
                if laststr:
                    tr.prepend(OutputConst("".join(laststr)))
                laststr = []

        if laststr:
            node.append(OutputConst("".join(laststr)))

        return node
예제 #5
0
    def _transform(self, node):
        overflow = 1 << self.cellsize

        tr = Transformer(node)
        for i, cur in tr:
            if not isinstance(cur, While): continue
            if not isinstance(cur.cond, MemNotEqual): continue

            target = cur.cond.target
            value = cur.cond.value

            if len(cur) == 1 and isinstance(cur[0], MovePointer):
                tr.replace(SeekMemory(target, cur[0].offset, value))
                continue

            if cur.offsets() != 0: continue

            flag = True  # whether Repeat[] is applicable
            cell = Expr()
            mode = 0  # 0:adjust, 1:set, -1:unknown

            for inode in cur:
                if isinstance(inode, SetMemory):
                    if inode.offset == target:
                        if inode.delta.simple():
                            cell += inode.delta
                        else:
                            cell = inode.value
                            mode = 1
                else:
                    if not inode.pure():
                        flag = False
                    if inode.offsets() != 0:
                        flag = False
                        mode = -1

                    updates = inode.postupdates().unsure
                    if None in updates or target in updates:
                        flag = False
                        mode = -1

                refs = inode.postreferences().unsure - inode.postupdates().sure
                if None in refs or target in refs:
                    flag = False  # references target, cannot use Repeat[]

            if mode < 0 or not cell.simple(): continue
            delta = (value - int(cell)) % overflow

            if mode > 0:
                if delta == 0:
                    # XXX SetMemory is added temporarily; we should implement
                    # SSA-based optimizer and it will recognize them across basic blocks
                    tr.replace(If(cur.cond, cur[:]), SetMemory(target, value))
                else:
                    infloop = While(Always())
                    if not cur.pure():  # e.g. +[.[-]+]
                        infloop.extend(cur)
                    tr.replace(infloop)

            elif flag:
                # let w be the overflow value, which is 256 for char etc.
                # then there are three cases in the following code:
                #     i = 0; for (j = 0; i != x; ++j) i += m;
                #
                # 1. if m = 0, it loops forever.
                # 2. otherwise, the condition j * m = x (mod w) must hold.
                #    let u * m + v * w = gcd(m,w), and
                #    1) if x is not a multiple of gcd(m,w), it loops forever.
                #    2) otherwise it terminates and j = u * (x / gcd(m,w)).
                #
                # we can calculate u and gcd(m,w) in the compile time, but
                # x is not (yet). so we shall add simple check for now.

                if delta == 0:
                    tr.replace(While(Always()))
                    continue

                u, v, gcd = _gcdex(delta, overflow)
                diff = Expr[target] - value
                count = (u % overflow) * (diff // gcd)

                inodes = [
                    inode for inode in cur
                    if not (isinstance(inode, SetMemory)
                            and inode.offset == target)
                ]

                result = []
                if gcd > 1:
                    # no need to check if x is a multiple of gcd(m,w) (=1).
                    result.append(
                        If(NotEqual(diff % gcd, 0), [While(Always())]))
                if inodes:
                    # don't emit Repeat[] if [-] or [+] is given.
                    result.append(Repeat(count, inodes))
                result.append(SetMemory(target, value))
                tr.replace(*result)

        return cleanup(node)
예제 #6
0
def cleanup(node):
    # general node cleanup routine. it does the following jobs:
    # - removes every no-op nodes, including If[False; ...] and k+=0.
    # - flattens Repeat[num; ...] node with all memory ops to parent.
    # - flattens If[True; ...] node to parent.
    # - merges MovePointer[] nodes as much as possible, and adjusts
    #   other nodes accordingly.
    # - removes every (dead) nodes after non-returning node.
    #
    # this is not recursive, and intended to be called in the end of other
    # optimization passes.

    offsets = 0
    tr = Transformer(node)
    for i, cur in tr:
        if not cur: # remove no-op
            tr.replace()
            continue

        cur.movepointer(offsets)
        ioffsets = cur.offsets()
        if ioffsets is not None:
            offsets += ioffsets

        if isinstance(cur, MovePointer):
            tr.replace()
            continue

        if isinstance(cur, If):
            if isinstance(cur.cond, Always):
                tr.replace(*cur)

        elif isinstance(cur, Repeat):
            hasset = False
            for inode in cur:
                if isinstance(inode, SetMemory):
                    if inode.value.simple():
                        hasset = True
                    elif inode.delta.simple():
                        pass
                    else:
                        break
                else:
                    break

            else:
                for inode in cur:
                    if isinstance(inode, SetMemory) and inode.delta.simple():
                        inode.delta *= cur.count
                if hasset:
                    # cannot flatten, but we can turn it into If[]
                    tr.replace(If(NotEqual(cur.count, 0), cur[:]))
                else:
                    tr.replace(*cur)

        # if this command doesn't return, ignore all nodes after it.
        if not cur.returns():
            tr.truncate()
            offsets = 0

    if offsets != 0:
        node.append(MovePointer(offsets))

    return node
예제 #7
0
    def _transform(self, node):
        overflow = 1 << self.cellsize

        tr = Transformer(node)
        for i, cur in tr:
            if not isinstance(cur, While):
                continue
            if not isinstance(cur.cond, MemNotEqual):
                continue

            target = cur.cond.target
            value = cur.cond.value

            if len(cur) == 1 and isinstance(cur[0], MovePointer):
                tr.replace(SeekMemory(target, cur[0].offset, value))
                continue

            if cur.offsets() != 0:
                continue

            flag = True  # whether Repeat[] is applicable
            cell = Expr()
            mode = 0  # 0:adjust, 1:set, -1:unknown

            for inode in cur:
                if isinstance(inode, SetMemory):
                    if inode.offset == target:
                        if inode.delta.simple():
                            cell += inode.delta
                        else:
                            cell = inode.value
                            mode = 1
                else:
                    if not inode.pure():
                        flag = False
                    if inode.offsets() != 0:
                        flag = False
                        mode = -1

                    updates = inode.postupdates().unsure
                    if None in updates or target in updates:
                        flag = False
                        mode = -1

                refs = inode.postreferences().unsure - inode.postupdates().sure
                if None in refs or target in refs:
                    flag = False  # references target, cannot use Repeat[]

            if mode < 0 or not cell.simple():
                continue
            delta = (value - int(cell)) % overflow

            if mode > 0:
                if delta == 0:
                    # XXX SetMemory is added temporarily; we should implement
                    # SSA-based optimizer and it will recognize them across basic blocks
                    tr.replace(If(cur.cond, cur[:]), SetMemory(target, value))
                else:
                    infloop = While(Always())
                    if not cur.pure():  # e.g. +[.[-]+]
                        infloop.extend(cur)
                    tr.replace(infloop)

            elif flag:
                # let w be the overflow value, which is 256 for char etc.
                # then there are three cases in the following code:
                #     i = 0; for (j = 0; i != x; ++j) i += m;
                #
                # 1. if m = 0, it loops forever.
                # 2. otherwise, the condition j * m = x (mod w) must hold.
                #    let u * m + v * w = gcd(m,w), and
                #    1) if x is not a multiple of gcd(m,w), it loops forever.
                #    2) otherwise it terminates and j = u * (x / gcd(m,w)).
                #
                # we can calculate u and gcd(m,w) in the compile time, but
                # x is not (yet). so we shall add simple check for now.

                if delta == 0:
                    tr.replace(While(Always()))
                    continue

                u, v, gcd = _gcdex(delta, overflow)
                diff = Expr[target] - value
                count = (u % overflow) * (diff // gcd)

                inodes = [inode for inode in cur if not (isinstance(inode, SetMemory) and inode.offset == target)]

                result = []
                if gcd > 1:
                    # no need to check if x is a multiple of gcd(m,w) (=1).
                    result.append(If(NotEqual(diff % gcd, 0), [While(Always())]))
                if inodes:
                    # don't emit Repeat[] if [-] or [+] is given.
                    result.append(Repeat(count, inodes))
                result.append(SetMemory(target, value))
                tr.replace(*result)

        return cleanup(node)
예제 #8
0
    def _transform(self, node):
        unusedcells = {}  # cell -> node which last updated this cell
        unusednodes = set()
        unusedmoves = []

        offsets = 0
        tr = Transformer(node)
        for i, cur in tr:
            ioffsets = cur.offsets()
            if ioffsets is None:
                unusedcells.clear()
                unusednodes.clear()
            else:
                offsets += ioffsets

            pure = cur.pure() and cur.returns()
            if pure:  # to remove non-I/O nodes independent to memory cells
                unusedmoves.append(i)

            irefs = cur.postreferences().unsure
            iupdates = cur.postupdates().sure
            removable = pure and ioffsets == 0
            if irefs or iupdates:
                # any pointer moves before this cell cannot be removed
                del unusedmoves[:]

            # delete references to all nodes which updates the cell which
            # this node can reference, thus cannot be removed.
            if None in irefs:
                unusedcells.clear()
                unusednodes.clear()
            else:
                for j in irefs:
                    j += offsets
                    try:
                        unusednodes.discard(unusedcells.pop(j))
                    except:
                        pass

            # now removes all nodes which cell updates have been never
            # referenced, and is to be (certainly) updated by this node.
            iupdates.discard(None)
            for j in iupdates:
                j += offsets
                try:
                    oldi = unusedcells[j]
                    unusednodes.remove(oldi)  # will raise exception if none
                    node[oldi] = Nop()
                except:
                    pass

                if removable:
                    unusedcells[j] = i
                    unusednodes.add(i)

        if isinstance(node, Program):
            for i in unusednodes:
                node[i] = Nop()
            for i in unusedmoves:
                node[i] = Nop()

        return cleanup(node)
예제 #9
0
    def _transform(self, node):
        backrefs = {}
        usedrefs = {}
        substs = {}  # only for simple one, unless some vars are not current

        tr = Transformer(node)
        for i, cur in tr:
            cur.withmemory(substs)

            alters = mergable = False
            offset = None
            refs = []
            if isinstance(cur, Nop):
                pass
            elif isinstance(cur, SetMemory):
                alters = mergable = True
                offset = cur.offset
                if cur.value.simple():
                    substs[offset] = cur.value
                elif cur.delta.simple():
                    if offset in substs:
                        substs[offset] += cur.delta
                        if substs[offset].simple():
                            # replace with equivalent SetMemory node.
                            cur = SetMemory(offset, substs[offset])
                            tr.replace(cur)
                        else:
                            del substs[offset]
                else:
                    try:
                        del substs[offset]
                    except:
                        pass
            elif isinstance(cur, Input):
                alters = True
                offset = cur.offset
                try:
                    del substs[offset]
                except:
                    pass
            elif isinstance(cur, Output):
                pass
            else:  # MovePointer, While etc.
                backrefs.clear()
                usedrefs.clear()
                substs.clear()
                if isinstance(cur, (While, If)) and isinstance(
                        cur.cond, MemNotEqual):
                    substs[cur.cond.target] = cur.cond.value
                elif isinstance(cur, SeekMemory):
                    substs[cur.target] = cur.value

            refs = cur.postreferences().unsure
            merged = False
            if alters:
                if not mergable:
                    # prohibit next merging attempt.
                    try:
                        del backrefs[offset]
                    except:
                        pass
                else:
                    # we can merge node[target] and node[i] if:
                    # - no operation has changed cell k between them. (thus such target
                    #   is backrefs[offset], as it is updated after change)
                    # - no operation has referenced the target cell between them.
                    #   node[target] itself could reference that cell.
                    # - no operation has changed cell(s) referenced by value.
                    #   similar to above, node[target] is excluded from this rule.
                    if offset in backrefs:
                        target = backrefs[offset]
                        if target >= usedrefs.get(offset, -1) and \
                                all(target >= backrefs.get(ioffset, -1) for ioffset in refs):
                            assume = {offset: node[target].value}
                            node[target].value = cur.value.withmemory(assume)
                            tr.replace()
                            merged = True

                    if not merged:
                        backrefs[offset] = i

            if not merged:
                target = i
            for ioffset in refs:
                usedrefs[ioffset] = target

        return cleanup(node)