# HG changeset patch # User Windel Bouwman # Date 1376840598 -7200 # Node ID 5f8c04a8d26bb198f792d11e3b1aad730fa61c43 # Parent 5ec7580976d961a0b4350a15a39a0dfa446bead4 Towards better modularity diff -r 5ec7580976d9 -r 5f8c04a8d26b python/c3/codegenerator.py --- a/python/c3/codegenerator.py Wed Aug 14 20:12:40 2013 +0200 +++ b/python/c3/codegenerator.py Sun Aug 18 17:43:18 2013 +0200 @@ -40,7 +40,11 @@ def genFunction(self, fn): # TODO: handle arguments f = self.funcMap[fn] + # TODO reserve room for stack, this can be done at later point? self.setFunction(f) + l2 = self.newBlock() + self.emit(ir.Jump(l2)) + self.setBlock(l2) # generate room for locals: for sym in fn.innerScope: @@ -51,7 +55,8 @@ self.genCode(fn.body) # TODO handle return? - self.emit(ir.Return(ir.Const(0))) + self.emit(ir.Jump(f.epiloog)) + #self.emit(ir.Return(ir.Const(0))) self.setFunction(None) def genCode(self, code): diff -r 5ec7580976d9 -r 5f8c04a8d26b python/codegenarm.py --- a/python/codegenarm.py Wed Aug 14 20:12:40 2013 +0200 +++ b/python/codegenarm.py Sun Aug 18 17:43:18 2013 +0200 @@ -3,40 +3,14 @@ from target import Label, Comment, Alignment, LabelRef, Imm32, DebugInfo import cortexm3 as arm from ppci import CompilerError -import irmach - - -class InstructionSelector: - def newTmp(self): - return 't999' - - def munchProgram(self, p): - assert isinstance(p, ir.Module) - self.result = [] - for f in p.Functions: - for bb in f.BasicBlocks: - for i in bb.Instructions: - self.munchStm(i) - return self.result - - def emit(self, *args, **kwargs): - """ Abstract instruction emitter """ - i = irmach.AbstractInstruction(*args, **kwargs) - self.result.append(i) - - def munchStm(self, s): - raise NotImplementedError() - - def munchExpr(self, e): - raise NotImplementedError() - - -class RegisterAllocator: - """ Target independent register allocator """ - pass +import graph +import flowgraph +import registerallocator +from instructionselector import InstructionSelector class ArmInstructionSelector(InstructionSelector): + """ Instruction selector for the arm architecture """ def munchExpr(self, e): if isinstance(e, ir.Alloc): return 0 @@ -46,11 +20,17 @@ d = self.newTmp() self.emit('add %d0, %s0, %s1', dst=[d], src=[a, b]) return d + elif isinstance(e, ir.Binop) and e.operation == '-': + a = self.munchExpr(e.value1) + b = self.munchExpr(e.value2) + d = self.newTmp() + self.emit('sub %d0, %s0, %s1', dst=[d], src=[a, b]) + return d elif isinstance(e, ir.Binop) and e.operation == '|': a = self.munchExpr(e.value1) b = self.munchExpr(e.value2) d = self.newTmp() - self.emit('orrrr %d0, %s0, %s1', dst=[d], src=[a, b]) + self.emit('or %d0, %s0, %s1', dst=[d], src=[a, b]) return d elif isinstance(e, ir.Binop) and e.operation == '<<': a = self.munchExpr(e.value1) @@ -62,14 +42,14 @@ a = self.munchExpr(e.value1) b = self.munchExpr(e.value2) d = self.newTmp() - self.emit('mylll %d0, %s0, %s1', dst=[d], src=[a, b]) + self.emit('mul %d0, %s0, %s1', dst=[d], src=[a, b]) return d elif isinstance(e, ir.Const): d = self.newTmp() if e.value < 256: self.emit('ldr %d0, {}'.format(e.value), dst=[d]) else: - self.emit('ldrpcrel TODO') + self.emit('ldrpcrel TODO', dst=[d]) return d elif isinstance(e, ir.Mem): # Load from memory @@ -78,7 +58,7 @@ self.emit('ldr %d0, [%s0]', src=[loc], dst=[d]) return d elif isinstance(e, ir.Temp): - return e + return self.getTempReg(e) else: raise NotImplementedError('--> {}'.format(e)) @@ -89,217 +69,48 @@ self.emit('str [%s0], %s1') elif isinstance(s, ir.Move) and isinstance(s.dst, ir.Temp): val = self.munchExpr(s.src) - self.emit('str %d0, %s0', dst=[s.dst], src=[val]) + dreg = self.getTempReg(s.dst) + self.emit('mov %d0, %s0', dst=[dreg], src=[val]) elif isinstance(s, ir.Return): - self.emit('ret') + #etgt = self.targets[ + self.emit('jmp exit', jumps=[]) elif isinstance(s, ir.Jump): - self.emit('jmp {}'.format(s)) + tgt = self.targets[s.target] + self.emit('jmp {}'.format(s), jumps=[tgt]) elif isinstance(s, ir.CJump): - self.munchExpr(s.a) - self.munchExpr(s.b) - self.emit('jmp {}'.format(s)) + a = self.munchExpr(s.a) + b = self.munchExpr(s.b) + self.emit('cmp %s0, %s1', src=[a, b]) + ntgt = self.targets[s.lab_no] + ytgt = self.targets[s.lab_yes] + jmp_ins = self.makeIns('jmp {}'.format(s.lab_no), jumps=[ntgt]) + # Explicitely add fallthrough: + self.emit('jeq {}'.format(s.lab_yes), jumps=[ytgt, jmp_ins]) + self.emit2(jmp_ins) else: raise NotImplementedError('--> {}'.format(s)) class ArmCodeGenerator: def __init__(self, outs): + # TODO: schedule traces in better order. + # This is optional! self.ins_sel = ArmInstructionSelector() self.outs = outs self.outs.getSection('code').address = 0x08000000 self.outs.getSection('data').address = 0x20000000 - def generate(self, ircode): - self.ins_sel.munchProgram(ircode) + def generate(self, ircode, cfg_file=None, ig_file=None): + x = self.ins_sel.munchProgram(ircode) + cfg = flowgraph.FlowGraph(x) + if cfg_file: + cfg.to_dot(cfg_file) + ig = registerallocator.InterferenceGraph(cfg) + if ig_file: + ig.to_dot(ig_file) + + regs = ['r0', 'r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7'] + ra = registerallocator.RegisterAllocator() + ra.registerAllocate(ig, regs) -class ArmCodeGenerator_old: - """ - Simple code generator - Ad hoc implementation - """ - def __init__(self, out): - self.outs = out - self.logger = logging.getLogger('codegenarm') - - def emit(self, item): - self.outs.emit(item) - - def generate(self, ircode): - assert isinstance(ircode, ir.Module) - self.logger.info('Generating arm code for {}'.format(ircode.name)) - self.available_regs = {arm.r3, arm.r4, arm.r5, arm.r6, arm.r7} - self.regmap = {} - # TODO: get these from linker descriptor? - self.outs.getSection('code').address = 0x08000000 - self.outs.getSection('data').address = 0x20000000 - self.outs.selectSection('data') - - for gvar in ircode.Variables: - self.emit(Label(gvar.name)) - # TODO: use initial value: - self.dcd(0) - - self.imms = [] # list with immediates relative to PC. - self.outs.selectSection('code') - - # Manually inserted startup code: - self.dcd(0x20000678) # initial stack ptr - # TODO: use label here: - #self.emit(arm.dcd_ins(LabelRef('reset'))) # reset vector - self.dcd(0x08000009) # reset vector, lsb indicates thumb mode - self.emit(arm.bl_ins(LabelRef('main'))) - - self.emit(Label('reset')) - for f in ircode.Functions: - self.localVars = [] - # Add global variable addresses to immediate list: - for gvar in ircode.Variables: - pass #self.imms.append(( - - self.stack_frame = [] - self.emit(Label(f.name)) - # Save some registers: - self.emit(arm.push_ins(arm.RegisterSet({arm.r3, arm.r4, arm.r5, arm.r6,arm.r7,arm.lr}))) - for bb in f.BasicBlocks: - self.emit(Label(bb.name)) - for ins in bb.Instructions: - self.generateInstruction(ins) - - self.align() - while self.imms: - l, v = self.imms.pop() - self.emit(Label(l)) - self.dcd(v) - self.align() - self.outs.backpatch() - self.outs.backpatch() - codesize = self.outs.getSection('code').Size - self.logger.info('Generated {} bytes code'.format(codesize)) - - def dcd(self, x): - self.emit(arm.dcd_ins(Imm32(x))) - - def align(self): - self.outs.emit(Alignment(4)) - - # Helper functions: - def getStack(self, v): - off = self.stack_frame.index(v) - return off * 4 - - def addStack(self, v): - self.stack_frame.append(v) - return self.getStack(v) - - def getGlobal(self, r, g): - _global_address = g.name + '__global' - self.emit(arm.ldr_pcrel(r, LabelRef(_global_address))) - - def loadStack(self, reg, val): - self.emit(arm.ldr_sprel(reg, arm.MemSpRel(self.getStack(val)))) - - def getreg(self, v): - if not v in self.regmap: - self.regmap[v] = self.available_regs.pop() - return self.regmap[v] - - def freereg(self, v, ins): - if v.lastUse(ins): - r = self.regmap.pop(v) - assert r not in self.regmap.values() - self.available_regs.add(r) - - def comment(self, txt): - self.emit(Comment(txt)) - - def debugInfo(self, loc): - if loc: - self.emit(DebugInfo(loc)) - - def generateInstruction(self, ins): - self.comment(str(ins)) - if hasattr(ins, 'debugLoc'): - self.debugInfo(ins.debugLoc) - if type(ins) is ir.Branch: - tgt = LabelRef(ins.target.name) - self.emit(arm.b_ins(tgt)) - elif type(ins) is ir.ImmLoad: - lname = ins.target.name + '_ivalue' - r0 = self.getreg(ins.target) - self.emit(arm.ldr_pcrel(r0, LabelRef(lname))) - self.imms.append((lname, ins.value)) - elif type(ins) is ir.Store: - # Load value in r0: - r0 = self.getreg(ins.value) - # store in memory: - # TODO: split globals and locals?? - #self.getGlobal(arm.r1, ins.location) - # Horrible hack with localVars - if ins.location in self.localVars: - # The value was alloc'ed - self.emit(arm.str_sprel(r0, arm.MemSpRel(self.getStack(ins.location)))) - else: - r1 = self.getreg(ins.location) - self.emit(arm.storeimm5_ins(r0, arm.MemR8Rel(r1, 0))) - self.freereg(ins.location, ins) - self.freereg(ins.value, ins) - elif type(ins) is ir.Load: - # TODO: differ global and local?? - #self.getGlobal(arm.r0, ins.location) - r0 = self.getreg(ins.value) - if ins.location in self.localVars: - self.emit(arm.ldr_sprel(r0, arm.MemSpRel(self.getStack(ins.location)))) - else: - r2 = self.getreg(ins.location) - self.emit(arm.loadimm5_ins(r0, arm.MemR8Rel(r2, 0))) - self.freereg(ins.location, ins) - elif type(ins) is ir.BinaryOperator: - # Load operands: - r0 = self.getreg(ins.value1) - r1 = self.getreg(ins.value2) - r2 = self.getreg(ins.result) - # do operation: - if ins.operation == '+': - self.emit(arm.addregs_ins(r2, r0, r1)) - elif ins.operation == '<<': - self.emit(arm.movregreg_ins(r2, r0)) - self.emit(arm.lslregs_ins(r2, r1)) - elif ins.operation == '|': - self.emit(arm.movregreg_ins(r2, r0)) - self.emit(arm.orrregs_ins(r2, r1)) - else: - raise NotImplementedError('operation {} not implemented'.format(ins.operation)) - self.freereg(ins.value1, ins) - self.freereg(ins.value2, ins) - elif type(ins) is ir.Call: - # TODO: prep parameters: - self.emit(arm.bl_ins(LabelRef(ins.callee.name))) - elif type(ins) is ir.Return: - self.emit(arm.pop_ins(arm.RegisterSet({arm.r3, arm.r4, arm.r5, arm.r6, arm.r7, arm.pc}))) - elif type(ins) is ir.ConditionalBranch: - r0 = self.getreg(ins.a) - r1 = self.getreg(ins.b) - self.emit(arm.cmp_ins(r1, r0)) - tgt_yes = LabelRef(ins.lab1.name) - if ins.cond == '==': - self.emit(arm.beq_ins(tgt_yes)) - elif ins.cond == '<': - self.emit(arm.blt_ins(tgt_yes)) - elif ins.cond == '>': - self.emit(arm.bgt_ins(tgt_yes)) - else: - raise NotImplementedError('"{}" not covered'.format(ins.cond)) - tgt_no = LabelRef(ins.lab2.name) - self.emit(arm.b_ins(tgt_no)) - self.freereg(ins.a, ins) - self.freereg(ins.b, ins) - elif type(ins) is ir.Alloc: - # Local variables are added to stack - self.addStack(ins.value) - self.localVars.append(ins.value) - # load address into variable: - else: - raise NotImplementedError('IR "{}" not covered'.format(ins)) - - diff -r 5ec7580976d9 -r 5f8c04a8d26b python/flowgraph.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/python/flowgraph.py Sun Aug 18 17:43:18 2013 +0200 @@ -0,0 +1,50 @@ + + +import graph + +class FlowGraphNode(graph.Node): + """ A node in the flow graph """ + def __init__(self, g, ins): + super().__init__(g) + self.ins = ins + self.uses = set(ins.src) + self.defs = set(ins.dst) + self.live_in = set() + self.live_out = set() + + def __repr__(self): + return '{}, use={}, def={}'.format(self.ins, self.uses, self.defs) + + +class FlowGraph(graph.Graph): + def __init__(self, instrs): + """ Create a flowgraph from a list of abstract instructions """ + super().__init__() + self._map = {} + # Add nodes: + for ins in instrs: + n = self.newNode(ins) + self._map[ins] = n + + # Make edges: + prev = None + for ins in instrs: + n = self._map[ins] + if prev: + self.addEdge(prev, n) + if ins.jumps: + prev = None + for j in ins.jumps: + to_n = self._map[j] + self.addEdge(n, to_n) + else: + prev = n + + def newNode(self, ins): + """ Override new node to make flow graph node """ + n = FlowGraphNode(self, ins) + self.nodes.append(n) + return n + + + diff -r 5ec7580976d9 -r 5f8c04a8d26b python/graph.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/python/graph.py Sun Aug 18 17:43:18 2013 +0200 @@ -0,0 +1,45 @@ + +class Graph: + """ + Generic graph base class. + Can dump to graphiz dot format for example! + """ + def __init__(self): + self.nodes = [] + self.edges = [] + + def newNode(self): + n = Node(self) + self.nodes.append(n) + return 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) + + def delEdge(self): + # TODO + pass + + def to_dot(self, f): + """ Generate graphviz dot representation """ + print('digraph G {', file=f) + for node in self.nodes: + print('{} [label="{}" shape=box3d];'.format(id(node), node), file=f) + for n, m in self.edges: + print('{} -> {};'.format(id(n), id(m)), file=f) + print('}', file=f) + + +class Node: + """ + Node in a graph. + """ + def __init__(self, g): + self.g = g + self.succ = [] + self.pred = [] + diff -r 5ec7580976d9 -r 5f8c04a8d26b python/instructionselector.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/python/instructionselector.py Sun Aug 18 17:43:18 2013 +0200 @@ -0,0 +1,60 @@ + +import ir +import irmach + +def genTemps(): + n = 900 + while True: + yield 't{}'.format(n) + n = n + 1 + +class InstructionSelector: + """ + Base instruction selector. This class must be overridden by + backends. + """ + def newTmp(self): + return self.temps.__next__() + + def getTempReg(self, tmp): + if tmp not in self.tempMap: + self.tempMap[tmp] = self.newTmp() + return self.tempMap[tmp] + + def munchProgram(self, p): + # Entry point for instruction selection + self.temps = genTemps() + assert isinstance(p, ir.Module) + self.result = [] + self.targets = {} + self.tempMap = {} # Mapping from temporaries to infinite register + for f in p.Functions: + # First define labels: + for bb in f.BasicBlocks: + itgt = self.makeIns('{}:'.format(bb.name)) + self.targets[bb] = itgt + for bb in f.BasicBlocks: + self.emit2(self.targets[bb]) + for i in bb.Instructions: + self.munchStm(i) + bb.machIns = self.result + return self.result + + def makeIns(self, *args, **kwargs): + return irmach.AbstractInstruction(*args, **kwargs) + + def emit(self, *args, **kwargs): + """ Abstract instruction emitter """ + i = self.makeIns(*args, **kwargs) + return self.emit2(i) + + def emit2(self, i): + self.result.append(i) + return i + + def munchStm(self, s): + raise NotImplementedError() + + def munchExpr(self, e): + raise NotImplementedError() + diff -r 5ec7580976d9 -r 5f8c04a8d26b python/ir/basicblock.py --- a/python/ir/basicblock.py Wed Aug 14 20:12:40 2013 +0200 +++ b/python/ir/basicblock.py Sun Aug 18 17:43:18 2013 +0200 @@ -1,5 +1,3 @@ - -# from .instruction import Statement class Block: """ @@ -13,7 +11,6 @@ return 'Block {0}'.format(self.name) def addInstruction(self, i): - #assert isinstance(i, Statement) i.parent = self self.instructions.append(i) diff -r 5ec7580976d9 -r 5f8c04a8d26b python/ir/function.py --- a/python/ir/function.py Wed Aug 14 20:12:40 2013 +0200 +++ b/python/ir/function.py Sun Aug 18 17:43:18 2013 +0200 @@ -2,8 +2,9 @@ class Function: def __init__(self, name): - self.name = name - self.entry = Block('entry') + self.name = name + self.entry = Block('{}_entry'.format(name)) + self.epiloog = Block('{}_epilog'.format(name)) def __repr__(self): return 'Function {0}'.format(self.name) @@ -26,6 +27,10 @@ if sb not in bbs: bbs.append(sb) worklist.append(sb) + bbs.remove(self.entry) + bbs.remove(self.epiloog) + bbs.insert(0, self.entry) + bbs.append(self.epiloog) return bbs def findBasicBlock(self, name): @@ -38,7 +43,7 @@ @property def Entry(self): - return self.BasicBlocks[0] + return self.entry def check(self): for bb in self.BasicBlocks: diff -r 5ec7580976d9 -r 5f8c04a8d26b python/irmach.py --- a/python/irmach.py Wed Aug 14 20:12:40 2013 +0200 +++ b/python/irmach.py Sun Aug 18 17:43:18 2013 +0200 @@ -1,18 +1,7 @@ - -class MachProgram: - pass - - -class MachFunction: - def __init__(self, name): - self.name = name - self.entry = None - - -class MachBlock: - def __init__(self): - self.instructions = [] +""" + Abstract assembly language instructions +""" class AbstractInstruction: diff -r 5ec7580976d9 -r 5f8c04a8d26b python/registerallocator.py --- a/python/registerallocator.py Wed Aug 14 20:12:40 2013 +0200 +++ b/python/registerallocator.py Sun Aug 18 17:43:18 2013 +0200 @@ -1,33 +1,73 @@ + +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 + class RegisterAllocator: - def live_analyze(self): - # Determine liveness: - for i in self.Instructions: - i.live_in = set() - i.live_out = set() - for z in range(50): - # TODO iterate until converge - for i in self.Instructions: - lo_mk = i.live_out.difference(i.defs) - i.live_in = i.uses.union(lo_mk) - lo = set() - for s in i.succ: - lo = lo.union(s.live_in) - i.live_out = lo - def registerAllocate(self, ir, regs): - allVals = [] - # construct interference: - for i in ir.Instructions: - for v in i.live_in: - allVals.append(v) - for v2 in i.live_in: - if v != v2: - v.interferes.add(v2) - # assign random registers: - regs = set(regs) - for v in allVals: - takenregs = set([iv.reg for iv in v.interferes]) - r2 = list(regs.difference(takenregs)) - # Pick next available: - v.reg = r2[0] + """ Target independent register allocator """ + def registerAllocate(self, ig, regs): + """ Color the given interference graph with the provided registers """ + self.regMap = {} + # TODO: implement ;) + regs = set(regs) + for n in ig.nodes: + print(n) + # TODO: implement spilling + # TODO: implement coalescing + diff -r 5ec7580976d9 -r 5f8c04a8d26b python/tcodegen.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/python/tcodegen.py Sun Aug 18 17:43:18 2013 +0200 @@ -0,0 +1,49 @@ + +""" + Test individual parts of the code generation for arm using the c3 frontend. +""" + +import c3 +import ppci +import codegenarm +import outstream + +testsrc = """ +package test2; + +function void tesssst(int henkie) +{ + var int a, b, cee; + a = 2 * 33 - 12; + b = a * 2; + a = b + a; + cee = a; + cee = cee * 2 + cee; + if (cee + a > b and b - a+b== 3*6-b) + { + var int x = a; + x = b - a; + a = x * (x + a); + } + else + { + a = b + (a + b); + } + var int y; + y = a - b * 53; +} +""" + +if __name__ == '__main__': + diag = ppci.DiagnosticsManager() + builder = c3.Builder(diag) + ir = builder.build(testsrc) + ir.dump() + outs = outstream.TextOutputStream() + cga = codegenarm.ArmCodeGenerator(outs) + cfg_file = open('cfg.gv', 'w') + ig_file = open('ig.gv', 'w') + cga.generate(ir, cfg_file=cfg_file, ig_file=ig_file) + cfg_file.close() + ig_file.close() + diff -r 5ec7580976d9 -r 5f8c04a8d26b python/trunner.sh --- a/python/trunner.sh Wed Aug 14 20:12:40 2013 +0200 +++ b/python/trunner.sh Sun Aug 18 17:43:18 2013 +0200 @@ -2,9 +2,9 @@ DIR=. while :; do + python -m unittest echo "Awaiting changes in $DIR" inotifywait -r -e modify $DIR - python -m unittest done diff -r 5ec7580976d9 -r 5f8c04a8d26b python/trunner2.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/python/trunner2.sh Sun Aug 18 17:43:18 2013 +0200 @@ -0,0 +1,15 @@ +#!/usr/bin/bash + +DIR=. +while :; do + python tcodegen.py + + # Create svg from dot file: + dot -Tpdf -o cfg.pdf cfg.gv + dot -Tpdf -o ig.pdf ig.gv + echo "Awaiting changes in $DIR" + inotifywait -r -e modify $DIR +done + + +