Mercurial > lcfOS
changeset 277:046017431c6a
Started register allocator
author | Windel Bouwman |
---|---|
date | Thu, 26 Sep 2013 21:14:25 +0200 |
parents | 56d37ed4b4d2 |
children | 9fca39eebe50 |
files | python/codeedit.py python/codegenarm.py python/cortexm3.py python/cortexm3desc.py python/flowgraph.py python/graph.py python/ide.py python/interferencegraph.py python/ir.py python/ir/__init__.py python/ir/builder.py python/ir/module.py python/irmach.py python/registerallocator.py python/target.py python/testasm.py python/testgraph.py python/testhexfile.py python/testir.py |
diffstat | 19 files changed, 854 insertions(+), 703 deletions(-) [+] |
line wrap: on
line diff
--- a/python/codeedit.py Mon Sep 16 21:51:17 2013 +0200 +++ b/python/codeedit.py Thu Sep 26 21:14:25 2013 +0200 @@ -170,7 +170,7 @@ painter.drawText(xpos, ypos, self.getRow(row)) if self.arrow and self.arrow.row == row: painter.drawPixmap(self.xposERR, ypos + ydt, self.arrowPixmap) - curErrors = [e for e in self.errorlist if e.loc.row == row] + curErrors = [e for e in self.errorlist if e.loc and e.loc.row == row] for e in curErrors: painter.drawPixmap(self.xposERR, ypos + ydt, self.errorPixmap) painter.setPen(errorPen)
--- a/python/codegenarm.py Mon Sep 16 21:51:17 2013 +0200 +++ b/python/codegenarm.py Thu Sep 26 21:14:25 2013 +0200 @@ -3,7 +3,6 @@ from target import Label, Comment, Alignment, LabelRef, Imm32, DebugInfo import cortexm3 as arm from ppci import CompilerError -import flowgraph import registerallocator from instructionselector import InstructionSelector import irmach @@ -210,59 +209,26 @@ # TODO: schedule traces in better order. # This is optional! self.ins_sel = ArmInstructionSelector() + self.ra = registerallocator.RegisterAllocator() self.outs = outs self.outs.getSection('code').address = 0x08000000 self.outs.getSection('data').address = 0x20000000 - def useUnused(self, inslist): - # Use unused temporaries at the end of the list - defTemps = [] - useTemps = [] - for i in inslist: - for d in iter(i.dst): - defTemps.append(d) - for s in iter(i.src): - useTemps.append(s) - defTemps = set(defTemps) - useTemps = set(useTemps) - unUsed = defTemps - useTemps - assert not unUsed - for uu in unUsed: - inslist.append(irmach.AbstractInstruction('use %s0', src=[uu])) - #print(useTemps) - - def allocFrame(self, f): - """ - Do register allocation for a single stack frame. - """ - ilist = f.instructions - self.useUnused(ilist) - cfg = flowgraph.FlowGraph(ilist) - f.cfg = cfg - ig = registerallocator.InterferenceGraph(cfg) - f.ig = ig - - ra = registerallocator.RegisterAllocator() - regMap = ra.registerAllocate(ig, f.regs, f.tempMap) - # Use allocated registers: - for i in ilist: - i.src = tuple(regMap[t] for t in i.src) - i.dst = tuple(regMap[t] for t in i.dst) - def generateFunc(self, irfunc): # Create a frame for this function: frame = ArmFrame(irfunc.name) + # Canonicalize the intermediate language: canon.make(irfunc, frame) - # print('after canonicalize:') - # irfunc.dump() + print('after canonicalize:') + irfunc.dump() self.ins_sel.munchFunction(irfunc, frame) - # print('Selected instructions:') - #for i in frame.instructions: - # print(i) + print('Selected instructions:') + for i in frame.instructions: + print(i) # Do register allocation: - self.allocFrame(frame) + self.ra.allocFrame(frame) # TODO: Peep-hole here? # Add label and return and stack adjustment:
--- a/python/cortexm3.py Mon Sep 16 21:51:17 2013 +0200 +++ b/python/cortexm3.py Thu Sep 26 21:14:25 2013 +0200 @@ -16,18 +16,14 @@ armtarget = Target('arm') -class ArmReg(Register): +class ArmRegister(Register): def __init__(self, num, name): super().__init__(name) self.num = num + def __repr__(self): return self.name -class RegOp: - def __init__(self, num): - assert num < 16 - self.num = num - @classmethod def Create(cls, vop): if type(vop) is ASymbol: @@ -37,41 +33,17 @@ regs[r.name] = r if name in regs: r = regs[name] - return cls(r.num) + if isinstance(r, cls): + return r -class Reg8Op: - def __init__(self, num): - assert num < 8 - self.num = num - @classmethod - def Create(cls, vop): - if type(vop) is ASymbol: - name = vop.name - regs = {} - for r in armtarget.registers: - regs[r.name] = r - if name in regs: - r = regs[name] - if r.num < 8: - return cls(r.num) +class Reg8Op(ArmRegister): + pass + -class Reg16Op: - def __init__(self, num): - assert num < 16 - self.num = num +class Reg16Op(ArmRegister): + pass - @classmethod - def Create(cls, vop): - if type(vop) is ASymbol: - name = vop.name - regs = {} - for r in armtarget.registers: - regs[r.name] = r - if name in regs: - r = regs[name] - if r.num < 16: - return cls(r.num) class RegSpOp: @classmethod @@ -128,7 +100,7 @@ class MemR8Rel: def __init__(self, basereg, offset): - assert type(basereg) is ArmReg + assert type(basereg) is Reg8Op self.basereg = basereg self.offset = offset @@ -173,13 +145,13 @@ regs = set() for arg in vop.arg: if type(arg) is ASymbol: - reg = RegOp.Create(arg) + reg = ArmRegister.Create(arg) if not reg: return regs.add(reg) elif type(arg) is ABinop and arg.op == '-': - reg1 = RegOp.Create(arg.arg1) - reg2 = RegOp.Create(arg.arg2) + reg1 = ArmRegister.Create(arg.arg1) + reg2 = ArmRegister.Create(arg.arg2) if not reg1: return if not reg2: @@ -193,32 +165,30 @@ def registerNumbers(self): return [r.num for r in self.regs] +def makeReg(cls, num, name): + r = cls(num, name) + armtarget.registers.append(r) + return r + # 8 bit registers: -r0 = ArmReg(0, 'r0') -armtarget.registers.append(r0) -r1 = ArmReg(1, 'r1') -armtarget.registers.append(r1) -r2 = ArmReg(2, 'r2') -armtarget.registers.append(r2) -r3 = ArmReg(3, 'r3') -armtarget.registers.append(r3) -r4 = ArmReg(4, 'r4') -armtarget.registers.append(r4) -r5 = ArmReg(5, 'r5') -armtarget.registers.append(r5) -r6 = ArmReg(6, 'r6') -armtarget.registers.append(r6) -r7 = ArmReg(7, 'r7') -armtarget.registers.append(r7) +r0 = makeReg(Reg8Op, 0, 'r0') +r1 = makeReg(Reg8Op, 1, 'r1') +r2 = makeReg(Reg8Op, 2, 'r2') +r3 = makeReg(Reg8Op, 3, 'r3') +r4 = makeReg(Reg8Op, 4, 'r4') +r5 = makeReg(Reg8Op, 5, 'r5') +r6 = makeReg(Reg8Op, 6, 'r6') +r7 = makeReg(Reg8Op, 7, 'r7') # Other registers: # TODO -sp = ArmReg(13, 'sp') -armtarget.registers.append(sp) -lr = ArmReg(14, 'lr') -armtarget.registers.append(lr) -pc = ArmReg(15, 'pc') -armtarget.registers.append(pc) +sp = makeReg(ArmRegister, 13, 'sp') +lr = makeReg(ArmRegister, 14, 'lr') +pc = makeReg(ArmRegister, 15, 'pc') +assert isinstance(sp, ArmRegister) +assert isinstance(r3, ArmRegister) +assert ArmRegister.Create(ASymbol('r3')) is r3 +assert ArmRegister.Create(ASymbol('sp')) is sp class ArmInstruction(Instruction): pass @@ -307,11 +277,12 @@ x = x + 1 return x + @armtarget.instruction class ldr_pcrel(ArmInstruction): """ ldr Rt, LABEL, load value from pc relative position """ mnemonic = 'ldr' - operands = (RegOp, LabelRef) + operands = (Reg8Op, LabelRef) def __init__(self, rt, label): assert isinstance(label, LabelRef) self.rt = rt @@ -337,38 +308,40 @@ def __repr__(self): return 'LDR {}, {}'.format(self.rt, self.label.name) + @armtarget.instruction class ldr_sprel(ls_sp_base_imm8): """ ldr Rt, [SP, imm8] """ mnemonic = 'LDR' opcode = 0x98 + @armtarget.instruction class str_sprel(ls_sp_base_imm8): """ str Rt, [SP, imm8] """ mnemonic = 'STR' opcode = 0x90 + @armtarget.instruction class mov_ins(ArmInstruction): """ mov Rd, imm8, move immediate value into register """ mnemonic = 'mov' opcode = 4 # 00100 Rd(3) imm8 - operands = (RegOp, Imm8) + operands = (Reg8Op, Imm8) def __init__(self, rd, imm): self.imm = imm.imm - self.r = rd.num + self.rd = rd def encode(self): - rd = self.r + rd = self.rd.num opcode = self.opcode imm8 = self.imm h = (opcode << 11) | (rd << 8) | imm8 return u16(h) + def __repr__(self): - return 'MOV {0}, xx?'.format(self.r) - - + return 'MOV {}, {}'.format(self.rd, self.imm) @@ -382,6 +355,7 @@ self.rd = rd self.rn = rn self.imm3 = imm3 + def encode(self): rd = self.rd.num rn = self.rn.num @@ -390,6 +364,8 @@ h = (self.opcode << 9) | (imm3 << 6) | (rn << 3) | rd return u16(h) + def __repr__(self): + return '{} {}, {}, {}'.format(self.mnemonic, self.rd, self.rn, self.imm3.imm) @armtarget.instruction class addregregimm3_ins(regregimm3_base): @@ -421,30 +397,35 @@ def __repr__(self): return '{} {}, {}, {}'.format(self.mnemonic, self.rd, self.rn, self.rm) + @armtarget.instruction class addregs_ins(regregreg_base): mnemonic = 'ADD' opcode = 0b0001100 + @armtarget.instruction class subregs_ins(regregreg_base): mnemonic = 'SUB' opcode = 0b0001101 + @armtarget.instruction class movregreg_ext_ins(ArmInstruction): - operands = (Reg16Op, Reg16Op) + operands = (ArmRegister, ArmRegister) mnemonic = 'MOV' def __init__(self, rd, rm): self.rd = rd self.rm = rm + def encode(self): Rd = self.rd.num & 0x7 D = (self.rd.num >> 3) & 0x1 Rm = self.rm.num opcode = 0b01000110 return u16((opcode << 8) | (D << 7) |(Rm << 3) | Rd) + def __repr__(self): return '{} {}, {}'.format(self.mnemonic, self.rd, self.rm) @@ -457,50 +438,65 @@ def __init__(self, rn, rdm): self.rn = rn self.rdm = rdm + def encode(self): rn = self.rn.num rdm = self.rdm.num opcode = 0b0100001101 h = (opcode << 6) | (rn << 3) | rdm return u16(h) + def __repr__(self): - return '{} {}, {}'.format(self.mnemonic, self.rdn, self.rm) + return '{} {}, {}'.format(self.mnemonic, self.rn, self.rdm) + class regreg_base(ArmInstruction): """ ??? Rdn, Rm """ operands = (Reg8Op, Reg8Op) + # TODO: integrate with the code gen interface: + src = (0, 1) + dst = (0,) def __init__(self, rdn, rm): self.rdn = rdn self.rm = rm + def encode(self): rdn = self.rdn.num rm = self.rm.num h = (self.opcode << 6) | (rm << 3) | rdn return u16(h) + def __repr__(self): return '{} {}, {}'.format(self.mnemonic, self.rdn, self.rm) + @armtarget.instruction class movregreg_ins(regreg_base): + # TODO: match this: + pattern = ir.Move(ir.Temp, ir.Temp) """ mov Rd, Rm """ mnemonic = 'mov' opcode = 0 + @armtarget.instruction class andregs_ins(regreg_base): mnemonic = 'AND' opcode = 0b0100000000 + @armtarget.instruction class orrregs_ins(regreg_base): mnemonic = 'ORR' opcode = 0b0100001100 + @armtarget.instruction class cmp_ins(regreg_base): mnemonic = 'CMP' opcode = 0b0100001010 + @armtarget.instruction class lslregs_ins(regreg_base): mnemonic = 'LSL' @@ -511,7 +507,7 @@ """ cmp Rn, imm8 """ mnemonic = 'cmp' opcode = 5 # 00101 - operands = (RegOp, Imm8) + operands = (Reg8Op, Imm8) def __init__(self, rn, imm): self.rn = rn self.imm = imm @@ -522,6 +518,7 @@ h = (opcode << 11) | (rn << 8) | imm return u16(h) + # Jumping: def wrap_negative(x, bits): @@ -653,29 +650,29 @@ # misc: # add/sub SP: -@armtarget.instruction -class addspsp_ins(ArmInstruction): +class addspsp_base(ArmInstruction): operands = (RegSpOp, RegSpOp, Imm7) - mnemonic = 'add' def __init__(self, _sp, _sp2, imm7): self.imm7 = imm7.imm assert self.imm7 % 4 == 0 self.imm7 >>= 2 def encode(self): - return u16((0b101100000 << 7) |self.imm7) + return u16((self.opcode << 7) |self.imm7) + + def __repr__(self): + return '{} sp, sp, {}'.format(self.mnemonic, self.imm7 << 2) @armtarget.instruction -class subspsp_ins(ArmInstruction): - operands = (RegSpOp, RegSpOp, Imm7) +class addspsp_ins(addspsp_base): + mnemonic = 'add' + opcode = 0b101100000 + + +@armtarget.instruction +class subspsp_ins(addspsp_base): mnemonic = 'sub' - def __init__(self, _sp, _sp2, imm7): - self.imm7 = imm7.imm - assert self.imm7 % 4 == 0 - self.imm7 >>= 2 - - def encode(self): - return u16((0b101100001 << 7) |self.imm7) + opcode = 0b101100001 armtarget.check()
--- a/python/cortexm3desc.py Mon Sep 16 21:51:17 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,17 +0,0 @@ - -# Patterns - -import cortexm3 -import ir - - -{ -""" -v3 = const1 -v1 = v2 + v3 -[v1] = v3 -""": cortexm3. -} - - -
--- a/python/flowgraph.py Mon Sep 16 21:51:17 2013 +0200 +++ b/python/flowgraph.py Thu Sep 26 21:14:25 2013 +0200 @@ -1,6 +1,5 @@ - +import graph -import graph class FlowGraphNode(graph.Node): """ A node in the flow graph """ @@ -24,12 +23,11 @@ class FlowGraph(graph.Graph): def __init__(self, instrs): """ Create a flowgraph from a list of abstract instructions """ - super().__init__() + super().__init__(True) self._map = {} # Add nodes: for ins in instrs: n = self.newNode(ins) - self._map[ins] = n # Make edges: prev = None @@ -48,8 +46,8 @@ def newNode(self, ins): """ Override new node to make flow graph node """ n = FlowGraphNode(self, ins) - self.nodes.append(n) + self._map[ins] = n + self.addNode(n) return n -
--- a/python/graph.py Mon Sep 16 21:51:17 2013 +0200 +++ b/python/graph.py Thu Sep 26 21:14:25 2013 +0200 @@ -4,25 +4,45 @@ Generic graph base class. Can dump to graphiz dot format for example! """ - def __init__(self): - self.nodes = [] - self.edges = [] + def __init__(self, directed): + self.directed = directed + self.nodes = set() + self.edges = set() def newNode(self): n = Node(self) - self.nodes.append(n) + self.addNode(n) return n + def addNode(self, n): + self.nodes.add(n) + + def delNode(self, n): + self.nodes.remove(n) + def addEdge(self, n, m): """ Add an edge from n to m """ if (n, m) not in self.edges and (m, n) not in self.edges: - self.edges.append((n, m)) - n.succ.append(m) - m.pred.append(n) + self.edges.add((n, m)) + + def hasEdge(self, n, m): + # This can be directional or not :) + if self.directed: + return (n, m) in self.edges + else: + return (n, m) in self.edges or (m, n) in self.edges - def delEdge(self): - # TODO - pass + def adjecent(self, n): + """ Return all nodes with edges to n """ + return set(m for m in self.nodes if self.hasEdge(n, m)) + + def preceeding(self, n): + assert self.directed + return set(m for m in self.nodes if self.hasEdge(m, n)) + + def succeeding(self, n): + assert self.directed + return set(m for m in self.nodes if self.hasEdge(n, m)) def to_dot(self, f): """ Generate graphviz dot representation """ @@ -40,11 +60,22 @@ """ def __init__(self, g): self.g = g - self.succ = [] - self.pred = [] + + @property + def Succ(self): + return self.g.succeeding(self) + + @property + def Pred(self): + """ Return a set of predecessors """ + return self.g.preceeding(self) @property def Adj(self): - return set(self.succ) | set(self.pred) + return self.g.adjecent(self) + + @property + def Degree(self): + return len(self.Adj)
--- a/python/ide.py Mon Sep 16 21:51:17 2013 +0200 +++ b/python/ide.py Thu Sep 26 21:14:25 2013 +0200 @@ -52,9 +52,11 @@ for e in errorlist: item = QStandardItem(self.errorIcon, str(e.msg)) item.setData(e) - irow = QStandardItem(str(e.loc.row)) + row = str(e.loc.row) if e.loc else '' + irow = QStandardItem(row) irow.setData(e) - icol = QStandardItem(str(e.loc.col)) + col = str(e.loc.col) if e.loc else '' + icol = QStandardItem(col) icol.setData(e) self.model.appendRow([item, irow, icol]) @@ -223,7 +225,8 @@ self.restoreGeometry(self.settings.value('mainwindowgeometry')) if self.settings.contains('lastfiles'): lfs = self.settings.value('lastfiles') - self.to_open_files.extend(lfs) + if lfs: + self.to_open_files.extend(lfs) def showEvent(self, ev): super().showEvent(ev)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/python/interferencegraph.py Thu Sep 26 21:14:25 2013 +0200 @@ -0,0 +1,69 @@ +import graph + + +class InterferenceGraphNode(graph.Node): + def __init__(self, g, varname): + super().__init__(g) + self.varname = varname + + def __repr__(self): + return '{}'.format(self.varname) + + +class InterferenceGraph(graph.Graph): + """ + Interference graph. + """ + def __init__(self, flowgraph): + """ + Create a new interference graph from a flowgraph + """ + super().__init__(False) + # Mapping from node to live set + self.liveMap = {} + self.tempMap = {} + # Calculate liveness in CFG: + ### + # Liveness: + # in[n] = use[n] UNION (out[n] - def[n]) + # out[n] = for s in n.succ in union in[s] + ### + for n in flowgraph.nodes: + n.live_in = set() + n.live_out = set() + + # Dataflow fixed point iteration: + change = True + while change: + change = False + for n in flowgraph.nodes: + _in = n.live_in + _out = n.live_out + n.live_in = n.uses | (n.live_out - n.defs) + if n.Succ: + n.live_out = set.union(*(s.live_in for s in n.Succ)) + else: + n.live_out = set() + change = change or (_in != n.live_in) or (_out != n.live_out) + + # Construct interference graph: + for n in flowgraph.nodes: + for tmp in n.live_out: + n1 = self.getNode(tmp) + for tmp2 in (n.live_out - {tmp}): + n2 = self.getNode(tmp2) + self.addEdge(n1, n2) + + def getNode(self, tmp): + if tmp not in self.tempMap: + self.tempMap[tmp] = self.newNode(tmp) + return self.tempMap[tmp] + + def newNode(self, varname): + n = InterferenceGraphNode(self, varname) + self.addNode(n) + return n + + def combine(self, n, m): + self.nodes.remove(m) + n.varnames.extend(m.varnames)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/python/ir.py Thu Sep 26 21:14:25 2013 +0200 @@ -0,0 +1,456 @@ +""" +Intermediate representation (IR) code classes. +""" + + +def dumpgv(m, outf): + print('digraph G ', file=outf) + print('{', file=outf) + for f in m.Functions: + print('{} [label="{}" shape=box3d]'.format(id(f), f),file=outf) + for bb in f.Blocks: + contents = str(bb) + '\n' + contents += '\n'.join([str(i) for i in bb.Instructions]) + outf.write('{0} [shape=note label="{1}"];\n'.format(id(bb), contents)) + for successor in bb.Successors: + print('"{}" -> "{}"\n'.format(id(bb), id(successor)), file=outf) + + outf.write('"{}" -> "{}" [label="entry"]\n'.format(id(f), id(f.entry))) + print('}', file=outf) + + +class Module: + """ Main container of variables and functions. """ + def __init__(self, name): + self.name = name + self.funcs = [] + self.variables = [] + + def __repr__(self): + return 'IR-module [{0}]'.format(self.name) + + def addFunc(self, f): + self.funcs.append(f) + + addFunction = addFunc + + def addVariable(self, v): + self.variables.append(v) + + def getVariables(self): + return self.variables + + Variables = property(getVariables) + + def getFunctions(self): + return self.funcs + + Functions = property(getFunctions) + + def findFunction(self, name): + for f in self.funcs: + if f.name == name: + return f + raise KeyError(name) + + getFunction = findFunction + + def dump(self, indent=' '): + print(self) + for v in self.Variables: + print(indent, v) + for fn in self.Functions: + fn.dump(indent=indent+' ') + + # Analysis functions: + def check(self): + """ Perform sanity check on module """ + for f in self.Functions: + f.check() + + +class Function: + """ + Function definition. Contains an entry block. + """ + def __init__(self, name): + self.name = name + self.entry = Block('{}_entry'.format(name)) + self.epiloog = Block('{}_epilog'.format(name)) + self.epiloog.addInstruction(Terminator()) + self.return_value = Temp('{}_retval'.format(name)) + self.arguments = [] + self.localvars = [] + + def __repr__(self): + args = ','.join(str(a) for a in self.arguments) + return 'Function {}({})'.format(self.name, args) + + def addBB(self, bb): + self.bbs.append(bb) + bb.parent = self + addBlock = addBB + + def removeBasicBlock(self, bb): + self.bbs.remove(bb) + bb.parent = None + + def getBBs(self): + bbs = [self.entry] + worklist = [self.entry] + while worklist: + b = worklist.pop() + for sb in b.Successors: + if sb not in bbs: + bbs.append(sb) + worklist.append(sb) + bbs.remove(self.entry) + if self.epiloog in bbs: + bbs.remove(self.epiloog) + bbs.insert(0, self.entry) + bbs.append(self.epiloog) + return bbs + + def findBasicBlock(self, name): + for bb in self.bbs: + if bb.name == name: + return bb + raise KeyError(name) + + Blocks = property(getBBs) + + @property + def Entry(self): + return self.entry + + def check(self): + for b in self.Blocks: + b.check() + + def addParameter(self, p): + assert type(p) is Parameter + p.num = len(self.arguments) + self.arguments.append(p) + + def addLocal(self, l): + assert type(l) is LocalVariable + self.localvars.append(l) + + def dump(self, indent=''): + print(indent+str(self)) + for bb in self.Blocks: + print(indent+' '+str(bb)) + for ins in bb.Instructions: + print(indent +' '*2 + str(ins)) + + +class Block: + """ + Uninterrupted sequence of instructions with a label at the start. + """ + def __init__(self, name): + self.name = name + self.instructions = [] + + def __repr__(self): + return 'Block {0}'.format(self.name) + + def addInstruction(self, i): + i.parent = self + assert not isinstance(self.LastInstruction, LastStatement) + self.instructions.append(i) + + def replaceInstruction(self, i1, i2): + idx = self.instructions.index(i1) + i1.parent = None + i1.delete() + i2.parent = self + self.instructions[idx] = i2 + + def removeInstruction(self, i): + i.parent = None + i.delete() + self.instructions.remove(i) + + def getInstructions(self): + return self.instructions + Instructions = property(getInstructions) + + def getLastIns(self): + if not self.Empty: + return self.instructions[-1] + LastInstruction = property(getLastIns) + + @property + def Empty(self): + return len(self.instructions) == 0 + + @property + def FirstInstruction(self): + return self.instructions[0] + + def getSuccessors(self): + if not self.Empty: + return self.LastInstruction.Targets + return [] + Successors = property(getSuccessors) + + def getPredecessors(self): + preds = [] + for bb in self.parent.BasicBlocks: + if self in bb.Successors: + preds.append(bb) + return preds + Predecessors = property(getPredecessors) + + def precedes(self, other): + raise NotImplementedError() + + def check(self): + assert isinstance(self.LastInstruction, LastStatement) + for i in self.instructions[:-1]: + assert not isinstance(i, LastStatement) + + +# Instructions: +class Term: + def __init__(self, x): + self.x = x + +def match_tree(tree, pattern): + if type(pattern) is Term: + return True, {pattern: tree} + elif type(pattern) is Binop and type(tree) is Binop and tree.operation == pattern.operation: + res_a, mp_a = match_tree(tree.a, pattern.a) + res_b, mp_b = match_tree(tree.b, pattern.b) + assert not (mp_a.keys() & mp_b.keys()) + mp_a.update(mp_b) + return res_a and res_b, mp_a + elif type(pattern) is Const and type(tree) is Const and pattern.value == tree.value: + return True, {} + else: + return False, {} + + +class Expression: + pass + + +class Const(Expression): + def __init__(self, value): + self.value = value + + def __repr__(self): + return 'Const {}'.format(self.value) + + +# Function calling: +class Call(Expression): + def __init__(self, f, arguments): + self.f = f + assert type(f) is Function + self.arguments = arguments + + def __repr__(self): + args = ', '.join([str(arg) for arg in self.arguments]) + return '{}({})'.format(self.f.name, args) + + +# Data operations +class Binop(Expression): + ops = ['+', '-', '*', '/', '|', '&', '<<', '>>'] + def __init__(self, value1, operation, value2): + assert operation in Binop.ops + self.a = value1 + self.b = value2 + self.operation = operation + + def __repr__(self): + a, b = self.a, self.b + return '({} {} {})'.format(a, self.operation, b) + + +def Add(a, b): + """ Convenience call """ + return Binop(a, '+', b) + + +class Eseq(Expression): + """ Sequence of instructions where the last is an expression """ + def __init__(self, stmt, e): + self.stmt = stmt + self.e = e + + def __repr__(self): + return '({}, {})'.format(self.stmt, self.e) + + +class Alloc(Expression): + """ Allocates space on the stack """ + def __init__(self): + super().__init__() + + def __repr__(self): + return 'Alloc' + + +class Variable(Expression): + def __init__(self, name): + self.name = name + + def __repr__(self): + return 'Var {}'.format(self.name) + + +class LocalVariable(Variable): + def __repr__(self): + return 'Local {}'.format(self.name) + + +class Parameter(Variable): + def __repr__(self): + return 'Param {}'.format(self.name) + + +class Temp(Expression): + """ Temporary storage, same as register """ + def __init__(self, name): + self.name = name + + def __repr__(self): + return 'TMP_{}'.format(self.name) + + +class Mem(Expression): + def __init__(self, e): + self.e = e + + def __repr__(self): + return '[{}]'.format(self.e) + + +class Statement: + """ Base class for all instructions. """ + pass + + +class Move(Statement): + def __init__(self, dst, src): + self.dst = dst + self.src = src + + def __repr__(self): + return '{} = {}'.format(self.dst, self.src) + + +class Exp(Statement): + def __init__(self, e): + self.e = e + + def __repr__(self): + return '{}'.format(self.e) + + +class Label(Statement): + # TODO: is this duplicate with block? + def __init__(self, name): + self.name = name + self.statements = [] + + def __repr__(self): + return 'LABEL {}:'.format(self.name) + + +# Branching: +class LastStatement(Statement): + pass + + +class Terminator(LastStatement): + """ Instruction that terminates the terminal block """ + Targets = [] + def __repr__(self): + return 'Terminator' + + +class Jump(LastStatement): + def __init__(self, target): + self.target = target + self.Targets = [target] + + def __repr__(self): + return 'BRANCH {}'.format(self.target.name) + + +class CJump(LastStatement): + conditions = ['==', '<', '>', '>=', '<=', '!='] + def __init__(self, a, cond, b, lab_yes, lab_no): + assert cond in CJump.conditions + self.a = a + self.cond = cond + self.b = b + self.lab_yes = lab_yes + self.lab_no = lab_no + self.Targets = [lab_yes, lab_no] + + def __repr__(self): + return 'IF {} {} {} THEN {} ELSE {}'.format(self.a, self.cond, self.b, self.lab_yes, self.lab_no) + + +class NamedClassGenerator: + def __init__(self, prefix, cls): + self.prefix = prefix + self.cls = cls + def NumGen(): + a = 0 + while True: + yield a + a = a + 1 + self.nums = NumGen() + + def gen(self, prefix=None): + if not prefix: + prefix = self.prefix + return self.cls('{0}{1}'.format(prefix, self.nums.__next__())) + + +class Builder: + """ Base class for ir code generators """ + def __init__(self): + self.prepare() + + def prepare(self): + self.newTemp = NamedClassGenerator('reg', Temp).gen + self.newBlock = NamedClassGenerator('block', Block).gen + self.bb = None + self.m = None + self.fn = None + self.loc = None + + # Helpers: + def setModule(self, m): + self.m = m + + def newFunction(self, name): + f = Function(name) + self.m.addFunc(f) + return f + + def setFunction(self, f): + self.fn = f + self.bb = f.entry if f else None + + def setBlock(self, b): + self.bb = b + + def setLoc(self, l): + self.loc = l + + def emit(self, i): + assert isinstance(i, Statement) + i.debugLoc = self.loc + if not self.bb: + raise Exception('No basic block') + self.bb.addInstruction(i) + +
--- a/python/ir/__init__.py Mon Sep 16 21:51:17 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -from .module import * -from .builder import Builder, NamedClassGenerator -
--- a/python/ir/builder.py Mon Sep 16 21:51:17 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,58 +0,0 @@ -from . import Block, Function, Statement, Temp - -class NamedClassGenerator: - def __init__(self, prefix, cls): - self.prefix = prefix - self.cls = cls - def NumGen(): - a = 0 - while True: - yield a - a = a + 1 - self.nums = NumGen() - - def gen(self, prefix=None): - if not prefix: - prefix = self.prefix - return self.cls('{0}{1}'.format(prefix, self.nums.__next__())) - - -class Builder: - """ Base class for ir code generators """ - def __init__(self): - self.prepare() - - def prepare(self): - self.newTemp = NamedClassGenerator('reg', Temp).gen - self.newBlock = NamedClassGenerator('block', Block).gen - self.bb = None - self.m = None - self.fn = None - self.loc = None - - # Helpers: - def setModule(self, m): - self.m = m - - def newFunction(self, name): - f = Function(name) - self.m.addFunc(f) - return f - - def setFunction(self, f): - self.fn = f - self.bb = f.entry if f else None - - def setBlock(self, b): - self.bb = b - - def setLoc(self, l): - self.loc = l - - def emit(self, i): - assert isinstance(i, Statement) - i.debugLoc = self.loc - if not self.bb: - raise Exception('No basic block') - self.bb.addInstruction(i) -
--- a/python/ir/module.py Mon Sep 16 21:51:17 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,379 +0,0 @@ -# IR-Structures: - - -def dumpgv(m, outf): - print('digraph G ', file=outf) - print('{', file=outf) - for f in m.Functions: - print('{} [label="{}" shape=box3d]'.format(id(f), f),file=outf) - for bb in f.Blocks: - contents = str(bb) + '\n' - contents += '\n'.join([str(i) for i in bb.Instructions]) - outf.write('{0} [shape=note label="{1}"];\n'.format(id(bb), contents)) - for successor in bb.Successors: - print('"{}" -> "{}"\n'.format(id(bb), id(successor)), file=outf) - - outf.write('"{}" -> "{}" [label="entry"]\n'.format(id(f), id(f.entry))) - print('}', file=outf) - - -class Module: - """ Main container of variables and functions. """ - def __init__(self, name): - self.name = name - self.funcs = [] - self.variables = [] - - def __repr__(self): - return 'IR-module [{0}]'.format(self.name) - - def addFunc(self, f): - self.funcs.append(f) - - addFunction = addFunc - - def addVariable(self, v): - self.variables.append(v) - - def getVariables(self): - return self.variables - - Variables = property(getVariables) - - def getFunctions(self): - return self.funcs - - Functions = property(getFunctions) - - def findFunction(self, name): - for f in self.funcs: - if f.name == name: - return f - raise KeyError(name) - - getFunction = findFunction - - def dump(self, indent=' '): - print(self) - for v in self.Variables: - print(indent, v) - for fn in self.Functions: - fn.dump(indent=indent+' ') - - # Analysis functions: - def check(self): - """ Perform sanity check on module """ - for f in self.Functions: - f.check() - - -class Function: - """ - Function definition. Contains an entry block. - """ - def __init__(self, name): - self.name = name - self.entry = Block('{}_entry'.format(name)) - self.epiloog = Block('{}_epilog'.format(name)) - self.epiloog.addInstruction(Terminator()) - self.return_value = Temp('{}_retval'.format(name)) - self.arguments = [] - self.localvars = [] - - def __repr__(self): - args = ','.join(str(a) for a in self.arguments) - return 'Function {}({})'.format(self.name, args) - - def addBB(self, bb): - self.bbs.append(bb) - bb.parent = self - addBlock = addBB - - def removeBasicBlock(self, bb): - self.bbs.remove(bb) - bb.parent = None - - def getBBs(self): - bbs = [self.entry] - worklist = [self.entry] - while worklist: - b = worklist.pop() - for sb in b.Successors: - if sb not in bbs: - bbs.append(sb) - worklist.append(sb) - bbs.remove(self.entry) - if self.epiloog in bbs: - bbs.remove(self.epiloog) - bbs.insert(0, self.entry) - bbs.append(self.epiloog) - return bbs - - def findBasicBlock(self, name): - for bb in self.bbs: - if bb.name == name: - return bb - raise KeyError(name) - - Blocks = property(getBBs) - - @property - def Entry(self): - return self.entry - - def check(self): - for b in self.Blocks: - b.check() - - def addParameter(self, p): - assert type(p) is Parameter - p.num = len(self.arguments) - self.arguments.append(p) - - def addLocal(self, l): - assert type(l) is LocalVariable - self.localvars.append(l) - - def dump(self, indent=''): - print(indent+str(self)) - for bb in self.Blocks: - print(indent+' '+str(bb)) - for ins in bb.Instructions: - print(indent +' '*2 + str(ins)) - - -class Block: - """ - Uninterrupted sequence of instructions with a label at the start. - """ - def __init__(self, name): - self.name = name - self.instructions = [] - - def __repr__(self): - return 'Block {0}'.format(self.name) - - def addInstruction(self, i): - i.parent = self - assert not isinstance(self.LastInstruction, LastStatement) - self.instructions.append(i) - - def replaceInstruction(self, i1, i2): - idx = self.instructions.index(i1) - i1.parent = None - i1.delete() - i2.parent = self - self.instructions[idx] = i2 - - def removeInstruction(self, i): - i.parent = None - i.delete() - self.instructions.remove(i) - - def getInstructions(self): - return self.instructions - Instructions = property(getInstructions) - - def getLastIns(self): - if not self.Empty: - return self.instructions[-1] - LastInstruction = property(getLastIns) - - @property - def Empty(self): - return len(self.instructions) == 0 - - @property - def FirstInstruction(self): - return self.instructions[0] - - def getSuccessors(self): - if not self.Empty: - return self.LastInstruction.Targets - return [] - Successors = property(getSuccessors) - - def getPredecessors(self): - preds = [] - for bb in self.parent.BasicBlocks: - if self in bb.Successors: - preds.append(bb) - return preds - Predecessors = property(getPredecessors) - - def precedes(self, other): - raise NotImplementedError() - - def check(self): - assert isinstance(self.LastInstruction, LastStatement) - for i in self.instructions[:-1]: - assert not isinstance(i, LastStatement) - - -# Instructions: - -class Expression: - pass - - -class Const(Expression): - def __init__(self, value): - self.value = value - - def __repr__(self): - return 'Const {}'.format(self.value) - - -# Function calling: -class Call(Expression): - def __init__(self, f, arguments): - self.f = f - assert type(f) is Function - self.arguments = arguments - - def __repr__(self): - args = ', '.join([str(arg) for arg in self.arguments]) - return '{}({})'.format(self.f.name, args) - - -# Data operations -class Binop(Expression): - ops = ['+', '-', '*', '/', '|', '&', '<<', '>>'] - def __init__(self, value1, operation, value2): - assert operation in Binop.ops - self.a = value1 - self.b = value2 - self.operation = operation - - def __repr__(self): - a, b = self.a, self.b - return '({} {} {})'.format(a, self.operation, b) - - -def Add(a, b): - """ Convenience call """ - return Binop(a, '+', b) - - -class Eseq(Expression): - """ Sequence of instructions where the last is an expression """ - def __init__(self, stmt, e): - self.stmt = stmt - self.e = e - - def __repr__(self): - return '({}, {})'.format(self.stmt, self.e) - - -class Alloc(Expression): - """ Allocates space on the stack """ - def __init__(self): - super().__init__() - - def __repr__(self): - return 'Alloc' - - -class Variable(Expression): - def __init__(self, name): - self.name = name - - def __repr__(self): - return 'Var {}'.format(self.name) - - -class LocalVariable(Variable): - def __repr__(self): - return 'Local {}'.format(self.name) - - -class Parameter(Variable): - def __repr__(self): - return 'Param {}'.format(self.name) - - -class Temp(Expression): - """ Temporary storage, same as register """ - def __init__(self, name): - self.name = name - - def __repr__(self): - return 'TMP_{}'.format(self.name) - - -class Mem(Expression): - def __init__(self, e): - self.e = e - - def __repr__(self): - return '[{}]'.format(self.e) - - -class Statement: - """ Base class for all instructions. """ - pass - - -class Move(Statement): - def __init__(self, dst, src): - self.dst = dst - self.src = src - - def __repr__(self): - return '{} = {}'.format(self.dst, self.src) - - -class Exp(Statement): - def __init__(self, e): - self.e = e - - def __repr__(self): - return '{}'.format(self.e) - - -class Label(Statement): - # TODO: is this duplicate with block? - def __init__(self, name): - self.name = name - self.statements = [] - - def __repr__(self): - return 'LABEL {}:'.format(self.name) - - -# Branching: -class LastStatement(Statement): - pass - - -class Terminator(LastStatement): - """ Instruction that terminates the terminal block """ - Targets = [] - def __repr__(self): - return 'Terminator' - - -class Jump(LastStatement): - def __init__(self, target): - self.target = target - self.Targets = [target] - - def __repr__(self): - return 'BRANCH {}'.format(self.target.name) - - -class CJump(LastStatement): - conditions = ['==', '<', '>', '>=', '<=', '!='] - def __init__(self, a, cond, b, lab_yes, lab_no): - assert cond in CJump.conditions - self.a = a - self.cond = cond - self.b = b - self.lab_yes = lab_yes - self.lab_no = lab_no - self.Targets = [lab_yes, lab_no] - - def __repr__(self): - return 'IF {} {} {} THEN {} ELSE {}'.format(self.a, self.cond, self.b, self.lab_yes, self.lab_no) - -
--- a/python/irmach.py Mon Sep 16 21:51:17 2013 +0200 +++ b/python/irmach.py Thu Sep 26 21:14:25 2013 +0200 @@ -7,6 +7,8 @@ Instructions are selected and scheduled at this stage. """ +import ir + class Frame: """ Activation record abstraction. This class contains a flattened @@ -29,24 +31,23 @@ Abstract machine instruction class. This is a very simple abstraction of machine instructions. """ - def __init__(self, assem, src=(), dst=(), jumps=()): - self.assem = assem + def __init__(self, cls, ops=(), src=(), dst=(), jumps=()): + self.assem = cls + self.ops = ops self.src = tuple(src) self.dst = tuple(dst) self.jumps = tuple(jumps) + c = lambda s: tuple(map(type, s)) == (ir.Temp, ) + self.ismove = c(src) and c(dst) and cls.lower().startswith('mov') def __repr__(self): - s = str(self.src) if self.src else '' - d = str(self.dst) if self.dst else '' - l = str(self.jumps) if self.jumps else '' - #return self.assem + s + d + l return self.render() def render(self): """ Substitutes source, dst and labels in the string """ - x = self.assem + x = str(self.assem) for i, s in enumerate(self.src): p = '%s{}'.format(i) x = x.replace(p, str(s)) @@ -56,7 +57,6 @@ for i, j in enumerate(self.jumps): p = '%l{}'.format(i) x = x.replace(p, str(j)) - return x
--- a/python/registerallocator.py Mon Sep 16 21:51:17 2013 +0200 +++ b/python/registerallocator.py Thu Sep 26 21:14:25 2013 +0200 @@ -1,97 +1,125 @@ - -import graph - -class InterferenceGraphNode(graph.Node): - def __init__(self, g, varname): - super().__init__(g) - self.varname = varname - - def __repr__(self): - return '{}'.format(self.varname) - - -class InterferenceGraph(graph.Graph): - def __init__(self, flowgraph): - """ - Create a new interference graph from a flowgraph - """ - super().__init__() - # Mapping from node to live set - self.liveMap = {} - self.tempMap = {} - # Calculate liveness in CFG: - ### - # Liveness: - # in[n] = use[n] UNION (out[n] - def[n]) - # out[n] = for s in n.succ in union in[s] - ### - for n in flowgraph.nodes: - n.live_in = set() - n.live_out = set() - # Dataflow fixed point iteration: - change = True - while change: - change = False - for n in flowgraph.nodes: - _in = n.live_in - _out = n.live_out - n.live_in = n.uses | (n.live_out - n.defs) - if n.succ: - n.live_out = set.union(*(s.live_in for s in n.succ)) - else: - n.live_out = set() - change = change or (_in != n.live_in) or (_out != n.live_out) - # Construct interference graph: - for n in flowgraph.nodes: - for tmp in n.live_out: - n1 = self.getNode(tmp) - for tmp2 in (n.live_out - {tmp}): - n2 = self.getNode(tmp2) - self.addEdge(n1, n2) - - def getNode(self, tmp): - if tmp not in self.tempMap: - self.tempMap[tmp] = self.newNode(tmp) - return self.tempMap[tmp] - - def newNode(self, varname): - n = InterferenceGraphNode(self, varname) - self.nodes.append(n) - return n +from flowgraph import FlowGraph +from interferencegraph import InterferenceGraph class RegisterAllocator: - """ Target independent register allocator """ - def registerAllocate(self, ig, regs, precolored): - """ - Color the given interference graph with the provided registers - """ - regMap = dict(precolored) - regs = set(regs) - K = len(regs) - allvars = list(n for n in ig.nodes if n.varname not in regMap) + """ + Target independent register allocator. + + Algorithm is iterated .. (ITR) by Appel and George + + The process consists of the following steps: + - build interference graph from the instruction list + - (optional) coalesc registers to remove redundant moves + - (optional) spill registers + - select registers + """ + def __init__(self): + pass + + def useUnused(self, inslist): + # Use unused temporaries at the end of the list + defTemps = [] + useTemps = [] + for i in inslist: + for d in iter(i.dst): + defTemps.append(d) + for s in iter(i.src): + useTemps.append(s) + defTemps = set(defTemps) + useTemps = set(useTemps) + unUsed = defTemps - useTemps + assert not unUsed + def build(self, f): + """ 1. Construct interference graph from instruction list """ + self.useUnused(f.instructions) + f.cfg = FlowGraph(f.instructions) + f.ig = InterferenceGraph(f.cfg) + self.pre_colored = set(n for n in f.ig.nodes if n.varname in f.tempMap.keys()) + print('pre-colored:', self.pre_colored) + self.moves = [i for i in f.instructions if i.ismove] + print('move instructions:', self.moves) + self.nodestack = [] + + def getMoveRelated(self, f): + mr = set() + for i in self.moves: + mr.add(f.ig.getNode(i.src[0])) + mr.add(f.ig.getNode(i.dst[0])) + return mr + + def simplify(self, f): + """ 2. Remove nodes from the graph """ # Chaitin's algorithm: - # remove all nodes that have less than K neighbours: - worklist = [] - while allvars: - less_k = [n for n in allvars if len(n.Adj - set(worklist)) < K] + # remove all non move related nodes that have less than K neighbours: + # Also do not remove pre-colored nodes. + # move_related = self.getMoveRelated(f) + #print('move related', move_related) + # o_nodes = f.ig.nodes - (self.pre_colored | move_related) + o_nodes = f.ig.nodes - self.pre_colored + #print('o_nodes', o_nodes) + while o_nodes: + less_k = [n for n in o_nodes if n.Degree < self.K] if not less_k: raise NotImplementedError('Spilling not implemented') n = less_k[0] - worklist.append(n) - allvars.remove(n) + self.nodestack.append(n) + f.ig.delNode(n) + o_nodes.remove(n) + + def coalesc(self, f): + # for mov in ins: + # if mov.src is not connected to mov.dst + # and the node being coalesced has less than K nodes + # of significant degree, then the node can be coalesced. + # This is briggs algorithm. + for m in self.moves: + src = f.ig.getNode(m.src[0]) + dst = f.ig.getNode(m.dst[0]) + assert not f.ig.hasEdge(src, dst) + neighbours = src.Adj | dst.Adj + # neighbours with significant degree: + sd_nb = set(n for n in neighbours if n.Degree >= self.K) + print('neighbours with significant degree', sd_nb) + if len(sd_nb) < self.K: + print('Coalesc:', m) + if dst in self.pre_colored: + if src in self.pre_colored: + break + v2 = src + for i in f.instructions: + rf = lambda x: x + i.src = rf(i.src) + i.dst = rf(i.dst) + f.instructions.remove(m) + + def select(self, f): + """ Add nodes of less than K degree back to the graph to color it. """ + regMap = dict(f.tempMap) # start with pre-colored # Add nodes back to the graph: - while worklist: - n = worklist.pop(-1) - adj = n.Adj - set(worklist) - possibleregs = set(regs) - set(regMap[n.varname] for n in adj) + while self.nodestack: + n = self.nodestack.pop(-1) + f.ig.addNode(n) + takenregs = set(regMap[m.varname] for m in n.Adj) + possibleregs = set(self.regs) - takenregs regMap[n.varname] = list(possibleregs)[0] # We have a register mapping! - # print(regMap) + print(regMap) + # Use allocated registers: + lookup = lambda t: regMap[t] + for i in f.instructions: + i.src = tuple(map(lookup, i.src)) + i.dst = tuple(map(lookup, i.dst)) + + def allocFrame(self, f): + """ Do register allocation for a single stack frame. """ + self.regs = set(f.regs) + self.K = len(self.regs) + self.build(f) + self.simplify(f) # TODO: implement spilling - # TODO: implement coalescing - return regMap + #self.coalesc(f) # Optional! + self.select(f) -
--- a/python/target.py Mon Sep 16 21:51:17 2013 +0200 +++ b/python/target.py Thu Sep 26 21:14:25 2013 +0200 @@ -184,8 +184,7 @@ rops = [roptype.Create(vop) for roptype, vop in zip(ic.operands, vi.operands)] # Check if we succeeded: - optypes = tuple(map(type, rops)) - if ic.operands == optypes: + if all(isinstance(rop, optype) for rop, optype in zip(rops, ic.operands)): return ic(*rops) raise CompilerError('No suitable instruction found for "{0}"'.format(vi))
--- a/python/testasm.py Mon Sep 16 21:51:17 2013 +0200 +++ b/python/testasm.py Thu Sep 26 21:14:25 2013 +0200 @@ -183,6 +183,11 @@ self.feed('mov r4, 100') self.check('6424') + @unittest.skip + def testMovExt(self): + self.feed('mov r3, sp') + self.check('') + def testYield(self): self.feed('yield') self.check('10bf')
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/python/testgraph.py Thu Sep 26 21:14:25 2013 +0200 @@ -0,0 +1,35 @@ +#!/usr/bin/python + +import unittest +import graph + +class GraphTestCase(unittest.TestCase): + def testEdge(self): + g = graph.Graph(False) + n1 = g.newNode() + n2 = g.newNode() + g.addEdge(n1, n2) + self.assertTrue(g.hasEdge(n2, n1)) + self.assertTrue(g.hasEdge(n1, n2)) + g.delNode(n1) + g.delNode(n2) + + def testDegree(self): + g = graph.Graph(False) + n1 = g.newNode() + n2 = g.newNode() + n3 = g.newNode() + g.addEdge(n1, n2) + g.addEdge(n1, n3) + self.assertEqual(2, n1.Degree) + self.assertEqual(1, n2.Degree) + g.delNode(n2) + self.assertEqual(1, n1.Degree) + +class DigraphTestCase(unittest.TestCase): + pass + + +if __name__ == '__main__': + unittest.main() +
--- a/python/testhexfile.py Mon Sep 16 21:51:17 2013 +0200 +++ b/python/testhexfile.py Thu Sep 26 21:14:25 2013 +0200 @@ -28,13 +28,11 @@ hf.addRegion(0xFFFE, bytes.fromhex('aabbcc')) self.saveload(hf) - @unittest.skip def testSave4(self): hf = HexFile() hf.addRegion(0xF000, bytes.fromhex('ab')*0x10000) self.saveload(hf) - @unittest.skip def testSave5(self): hf = HexFile() hf.addRegion(0xF003, bytes.fromhex('ab')*0x10000)
--- a/python/testir.py Mon Sep 16 21:51:17 2013 +0200 +++ b/python/testir.py Thu Sep 26 21:14:25 2013 +0200 @@ -26,6 +26,29 @@ #self.assertEqual(3, r) +class PatternMatchTestCase(unittest.TestCase): + def testSimpleTree(self): + t = ir.Term('x') + pat = ir.Binop(ir.Const(2), '+', t) + res, mp = ir.match_tree(ir.Binop(ir.Const(2), '+', 3), pat) + self.assertTrue(res) + self.assertIn(t, mp) + self.assertEqual(3, mp[t]) + + def testSimpleTree2(self): + t = ir.Term('x') + t2 = ir.Term('y') + pat = ir.Binop(ir.Const(2), '+', ir.Binop(t, '-', t2)) + res, mp = ir.match_tree(ir.Binop(ir.Const(2), '+', ir.Binop(2,'-',3)), pat) + self.assertTrue(res) + self.assertIn(t, mp) + self.assertEqual(2, mp[t]) + self.assertIn(t2, mp) + self.assertEqual(3, mp[t2]) + res, mp = ir.match_tree(ir.Const(2), pat) + self.assertFalse(res) + + class ConstantFolderTestCase(unittest.TestCase): def setUp(self): self.b = ir.Builder()