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
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
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
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)
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
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)
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)
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)