changeset 335:582a1aaa3983

Added long branch format
author Windel Bouwman
date Mon, 17 Feb 2014 20:41:30 +0100
parents 6f4753202b9a
children d1ecc493384e
files python/outstream.py python/ppci/buildtasks.py python/ppci/linker.py python/ppci/objectfile.py python/target/arminstructions.py python/target/basetarget.py test/testarmasm.py test/testasm.py test/testcg.py test/testmsp430asm.py test/testzcc.py
diffstat 11 files changed, 233 insertions(+), 129 deletions(-) [+]
line wrap: on
line diff
--- a/python/outstream.py	Thu Feb 13 22:02:08 2014 +0100
+++ b/python/outstream.py	Mon Feb 17 20:41:30 2014 +0100
@@ -1,5 +1,6 @@
 import binascii
-from target import Instruction, Label, DebugInfo
+from target import Instruction, DebugInfo, Alignment
+from ppci.objectfile import ObjectFile
 
 """
  The output stream is a stream of instructions that can be output
@@ -7,76 +8,19 @@
 """
 
 
-class Section:
-    def __init__(self):
-        self.address = 0
-        self.instructions = []
-
+class OutputStream:
     def emit(self, item):
-        assert isinstance(item, Instruction)
-        self.instructions.append(item)
-
-    def to_bytes(self):
-        d = bytearray()
-        for i in self.instructions:
-            addr = i.address
-            insword = i.encode()
-            assert type(insword) is bytes
-            d.extend(insword)
-        return bytes(d)
-
-    @property
-    def Size(self):
-        return len(self.to_bytes())
-
-    def debugInfos(self):
-        di = [i for i in self.instructions if isinstance(i, DebugInfo)]
-        return di
-
-
-class OutputStream:
-    def __init__(self):
-        self.sections = {}
-        self.currentSection = None
-
-    def emit(self, item):
-        assert self.currentSection
-        self.currentSection.emit(item)
+        raise NotImplementedError('Abstract base class')
 
     def selectSection(self, sname):
-        self.currentSection = self.getSection(sname)
-
-    def getLabelAddress(self, lname):
-        assert isinstance(lname, str)
-        for s in self.sections.values():
-            for i in s.instructions:
-                if type(i) is Label:
-                    if i.name == lname:
-                        return i.address
-        return 0
+        raise NotImplementedError('Abstract base class')
 
-    def getSection(self, name):
-        if not name in self.sections:
-            self.sections[name] = Section()
-        return self.sections[name]
-
-    def backpatch(self):
-        """ Fixup references to other parts in the assembler """
-        for s in self.sections.values():
-            address = s.address
-            for i in s.instructions:
-                i.address = address
-                i.resolve(self.getLabelAddress)
-                bts = i.encode()
-                address += len(bts)
 
 class OutputStreamWriter:
     def __init__(self, extra_indent=''):
         self.extra_indent = extra_indent
 
     def dump(self, stream, f):
-        stream.backpatch()
-        stream.backpatch()
         for s in sorted(stream.sections.keys()):
             # print('.section '+ s)
             self.dumpSection(stream.sections[s], f)
@@ -96,19 +40,30 @@
                 print('    0x{0:08x} 0x{1} {2}'.format(addr, insword, asm), file=f)
 
 
-class TextOutputStream(OutputStream):
-    pass
-
-
-class BinOutputStream(OutputStream):
+class BinaryOutputStream(OutputStream):
+    """ Output stream that writes to object file """
+    def __init__(self, obj_file):
+        super().__init__()
+        self.obj_file = obj_file
 
-    @property
-    def Data(self):
-        d = self.dump()
-        return bytes(d)
+    def emit(self, item):
+        """ Encode instruction and add symbol and relocation information """
+        assert isinstance(item, Instruction)
+        assert self.currentSection
+        section = self.currentSection
+        address = self.currentSection.Size
+        b = item.encode()
+        syms = item.symbols()
+        relocs = item.relocations()
+        section.add_data(b)
+        for sym in syms:
+            self.obj_file.add_symbol(sym, address, section.name)
+        for sym, typ in relocs:
+            self.obj_file.add_relocation(sym, address, typ, section.name)
+        # Special case for align, TODO do this different?
+        if type(item) is Alignment:
+            while section.Size % item.align != 0:
+                section.add_data(bytes([0]))
 
-    def dump(self):
-        self.backpatch()
-        self.backpatch()
-        section = self.sections[list(self.sections.keys())[0]]
-        return section.to_bytes()
+    def selectSection(self, sname):
+        self.currentSection = self.obj_file.get_section(sname)
--- a/python/ppci/buildtasks.py	Thu Feb 13 22:02:08 2014 +0100
+++ b/python/ppci/buildtasks.py	Mon Feb 17 20:41:30 2014 +0100
@@ -17,6 +17,7 @@
 from .linker import Linker
 import outstream
 
+
 class BuildTask(Task):
     """ Base task for all kind of weird build tasks """
     def __init__(self, name):
@@ -71,7 +72,7 @@
             # Code generation:
             d = {'ircode':ircode}
             self.logger.debug('Starting code generation for {}'.format(ircode), extra=d)
-            o = outstream.TextOutputStream()
+            o = outstream.BinaryOutputStream(self.output)
             cg.generate(ircode, o)
 
         if not c3b.ok:
@@ -88,10 +89,6 @@
         self.duration = 0.1337
 
     def run(self):
-        print('LNK')
-        print('LNK', self.objects)
-        print('LNK')
-        print('LNK')
         self.linker.link(self.objects)
 
 
--- a/python/ppci/linker.py	Thu Feb 13 22:02:08 2014 +0100
+++ b/python/ppci/linker.py	Mon Feb 17 20:41:30 2014 +0100
@@ -1,19 +1,112 @@
 
+import struct
 from .objectfile import ObjectFile
+from . import CompilerError
+
+def align(x, m):
+    while ((x % m) != 0):
+        x = x + 1
+    return x
+
+def wrap_negative(x, bits):
+    b = struct.unpack('<I', struct.pack('<i', x))[0]
+    mask = (1 << bits) - 1
+    return b & mask
+
+reloc_map = {}
+
+def reloc(t):
+    def f(c):
+        reloc_map[t] = c
+    return f
+
+
+@reloc('lit_add_8')
+class LitAdd8:
+    def apply(self):
+        pass
+
 
 class Linker:
     def set_symbol(self, sym):
-        self.dst.add_symbol(sym.name, sym.value)
+        self.dst.add_symbol(sym.name, sym.value, sym.section)
 
     def link(self, objs):
+        # Create new object file to store output:
         self.dst = ObjectFile()
+        self.dst.get_section('code').address = 0x08000000
+
         # First copy all sections into output sections:
         for iobj in objs:
-            for sym in iobj.symbols:
-                print(sym)
+            offsets = {}
+            # Merge sections:
+            for in_s in iobj.sections.values():
+                out_s = self.dst.get_section(in_s.name)
+                offsets[in_s.name] = out_s.address + out_s.Size
+                out_s.add_data(in_s.data)
+                # TODO: align section
+
+            # Merge symbols:
+            for sym in iobj.symbols.values():
                 self.set_symbol(sym)
-        # Do relocations:
-        # TODO
+
+            # Merge relocations:
+            for reloc in iobj.relocations:
+                self.dst.add_relocation(reloc.sym, reloc.offset, reloc.typ, reloc.section)
+
         # Check that there are no more unresolved symbols:
-        # TODO
+        # Perform relocations:
+        for reloc in self.dst.relocations:
+            # Lookup symbol:
+            if reloc.sym not in self.dst.symbols:
+                raise CompilerError('Undefined reference "{}"'.format(reloc.sym))
+            sym = self.dst.symbols[reloc.sym]
+            # patch up:
+            section = self.dst.get_section(reloc.section)
+
+            if reloc.typ == 'lit_add_8':
+                assert sym.value % 4 == 0
+                offset = (sym.value - (align(reloc.offset + 2, 4)))
+                assert offset in range(0, 1024, 4)
+                rel8 = offset >> 2
+                section.data[reloc.offset] = rel8
+            elif reloc.typ == 'wrap_new11':
+                offset = sym.value - (align(reloc.offset, 2) + 4)
+                assert offset in range(-2048, 2046, 2)
+                imm11 = wrap_negative(offset >> 1, 11)
+                section.data[reloc.offset] = (imm11 & 0xff)
+                section.data[reloc.offset + 1] |= (imm11 >> 8) & 0x7
+            elif reloc.typ == 'rel8':
+                assert sym.value % 2 == 0
+                offset = sym.value - (align(reloc.offset, 2) + 4)
+                assert offset in range(-256, 254, 2), str(offset) + str(reloc)
+                imm8 = wrap_negative(offset >> 1, 8)
+                section.data[reloc.offset] = imm8
+            elif reloc.typ == 'bl_imm11_imm10':
+                assert sym.value % 2 == 0
+                offset = sym.value - (align(reloc.offset, 2) + 4)
+                assert offset in range(-16777216, 16777214, 2), str(offset)
+                imm32 = wrap_negative(offset >> 1, 32)
+                imm11 = imm32 & 0x7FF
+                imm10 = (imm32 >> 11) & 0x3FF
+                s = (imm32 >> 24) & 0x1
+                section.data[reloc.offset + 2] = imm11 & 0xFF
+                section.data[reloc.offset + 3] |= (imm11 >> 8) & 0x7
+                section.data[reloc.offset] = imm10 & 0xff
+                section.data[reloc.offset + 1] |= ((imm10 >> 8) & 0x3) | (s << 2)
+            elif reloc.typ == 'b_imm11_imm6':
+                assert sym.value % 2 == 0
+                offset = sym.value - (align(reloc.offset, 2) + 4)
+                assert offset in range(-1048576, 1048574, 2), str(offset)
+                imm32 = wrap_negative(offset >> 1, 32)
+                imm11 = imm32 & 0x7FF
+                imm6 = (imm32 >> 11) & 0x3F
+                s = (imm32 >> 24) & 0x1
+                section.data[reloc.offset + 2] = imm11 & 0xFF
+                section.data[reloc.offset + 3] |= (imm11 >> 8) & 0x7
+                section.data[reloc.offset] |= imm6
+                section.data[reloc.offset + 1] |= (s << 2)
+            else:
+                raise NotImplementedError('Unknown relocation type {}'.format(reloc.typ))
+
         return self.dst
--- a/python/ppci/objectfile.py	Thu Feb 13 22:02:08 2014 +0100
+++ b/python/ppci/objectfile.py	Mon Feb 17 20:41:30 2014 +0100
@@ -5,19 +5,41 @@
 """
 
 class Symbol:
-    def __init__(self, name, value):
+    def __init__(self, name, value, section):
         self.name = name
         self.value = value
+        self.section = section
+
+    def __repr__(self):
+        return 'SYM {}, val={} sec={}'.format(self.name, self.value, self.section)
 
 
 class Relocation:
-    def __init__(self, typ):
-        pass
+    def __init__(self, sym, offset, typ, section):
+        self.sym = sym
+        self.offset = offset
+        self.typ = typ
+        self.section = section
+
+    def __repr__(self):
+        return 'RELOC {} off={} t={} sec={}'.format(self.sym, self.offset, self.typ, self.section)
 
 
 class Section:
     def __init__(self, name):
         self.name = name
+        self.address = 0
+        self.data = bytearray()
+
+    def add_data(self, data):
+        self.data += data
+
+    @property
+    def Size(self):
+        return len(self.data)
+
+    def __repr__(self):
+        return 'SECTION {}'.format(self.name)
 
 
 class ObjectFile:
@@ -26,7 +48,17 @@
         self.sections = {}
         self.relocations = []
 
-    def add_symbol(self, name, value):
-        sym = Symbol(name, value)
+    def add_symbol(self, name, value, section):
+        sym = Symbol(name, value, section)
         self.symbols[name] = sym
         return sym
+
+    def add_relocation(self, sym_name, offset, typ, section):
+        reloc = Relocation(sym_name, offset, typ, section)
+        self.relocations.append(reloc)
+        return reloc
+
+    def get_section(self, name):
+        if not name in self.sections:
+            self.sections[name] = Section(name)
+        return self.sections[name]
--- a/python/target/arminstructions.py	Thu Feb 13 22:02:08 2014 +0100
+++ b/python/target/arminstructions.py	Mon Feb 17 20:41:30 2014 +0100
@@ -216,13 +216,13 @@
         else:
             raise NotImplementedError()
 
-    def resolve(self, f):
-        if self.label:
-            self.expr = f(self.label.name)
-
     def encode(self):
         return u32(self.expr)
 
+    def relocations(self):
+        assert not isinstance(self.expr, LabelRef)
+        return []
+
     def __repr__(self):
         return 'DCD 0x{0:X}'.format(self.expr)
 
@@ -325,12 +325,8 @@
     def fromim(cls, im):
         return cls(im.dst[0], im.others[0])
 
-    def resolve(self, f):
-        la = f(self.label.name)
-        sa = align(self.address + 2, 4)
-        self.offset = (la - sa)
-        if self.offset < 0:
-            self.offset = 0
+    def relocations(self):
+        return [(self.label.name, 'lit_add_8')]
 
     def encode(self):
         rt = self.rt.num
@@ -488,7 +484,7 @@
 
     def __repr__(self):
         return '{} {}, {}'.format(self.mnemonic, self.rd, self.rm)
-        
+
 
 @instruction
 class Mul(ArmInstruction):
@@ -602,15 +598,15 @@
         self.target = target_label
         self.offset = 0
 
-    def resolve(self, f):
-        la = f(self.target.name)
-        sa = self.address + 4
-        self.offset = (la - sa)
-
     def __repr__(self):
         return '{} {}'.format(self.mnemonic, self.target.name)
 
 
+class Imm11Reloc:
+    def apply(self, P, S):
+        pass
+
+
 @instruction
 class B(jumpBase_ins):
     mnemonic = 'B'
@@ -619,6 +615,8 @@
         h = (0b11100 << 11) | imm11 # | 1 # 1 to enable thumb mode
         return u16(h)
 
+    def relocations(self):
+        return [(self.target.name, 'wrap_new11')]
 
 @instruction
 class Bl(jumpBase_ins):
@@ -634,13 +632,32 @@
         h2 = (0b1101 << 12) | (j1 << 13) | (j2 << 11) | imm11
         return u16(h1) + u16(h2)
 
+    def relocations(self):
+        return [(self.target.name, 'bl_imm11_imm10')]
 
-class cond_base_ins(jumpBase_ins):
+
+class cond_base_ins_short(jumpBase_ins):
     def encode(self):
         imm8 = wrap_negative(self.offset >> 1, 8)
         h = (0b1101 << 12) | (self.cond << 8) | imm8
         return u16(h)
 
+    def relocations(self):
+        return [(self.target.name, 'rel8')]
+
+
+class cond_base_ins(jumpBase_ins):
+    """ Encoding T3 """
+    def encode(self):
+        j1 = 1 # TODO: what do these mean?
+        j2 = 1
+        h1 = (0b11110 << 11) | (self.cond << 6)
+        h2 = (0b1101 << 12) | (j1 << 13) | (j2 << 11)
+        return u16(h1) + u16(h2)
+
+    def relocations(self):
+        return [(self.target.name, 'b_imm11_imm6')]
+
 
 @instruction
 class Beq(cond_base_ins):
--- a/python/target/basetarget.py	Thu Feb 13 22:02:08 2014 +0100
+++ b/python/target/basetarget.py	Mon Feb 17 20:41:30 2014 +0100
@@ -58,10 +58,13 @@
 class Instruction:
     """ Base instruction class """
     def encode(self):
-        raise NotImplementedError('Instruction {0} has no encode yet, TODO'.format(type(self)))
+        return bytes()
 
-    def resolve(self, f):
-        pass
+    def relocations(self):
+        return []
+
+    def symbols(self):
+        return []
 
 
 class Nop(Instruction):
@@ -77,13 +80,12 @@
 class Label(PseudoInstruction):
     def __init__(self, name):
         self.name = name
-        self.address = 0
 
     def __repr__(self):
         return '{}:'.format(self.name)
 
-    def encode(self):
-        return bytes()
+    def symbols(self):
+        return [self.name]
 
     @classmethod
     def Create(cls, vop):
@@ -112,7 +114,8 @@
 
     def encode(self):
         pad = []
-        address = self.address
+        # TODO
+        address = 0
         while (address % self.align) != 0:
             address += 1
             pad.append(0)
@@ -126,9 +129,6 @@
     def __repr__(self):
         return 'DebugInfo: {}'.format(self.info)
 
-    def encode(self):
-        return bytes()
-
 
 class Register(Operand):
     def __init__(self, name):
--- a/test/testarmasm.py	Thu Feb 13 22:02:08 2014 +0100
+++ b/test/testarmasm.py	Mon Feb 17 20:41:30 2014 +0100
@@ -1,5 +1,6 @@
 import unittest
 import outstream
+from ppci.objectfile import ObjectFile
 from asm import Assembler
 from testasm import AsmTestCaseBase
 from target.target_list import armtarget
@@ -8,7 +9,8 @@
 class AssemblerARMTestCase(AsmTestCaseBase):
     def setUp(self):
         self.t = armtarget
-        self.o = outstream.BinOutputStream()
+        self.obj = ObjectFile()
+        self.o = outstream.BinaryOutputStream(self.obj)
         self.o.selectSection('.text')
         self.a = Assembler(target=self.t, stream=self.o)
 
--- a/test/testasm.py	Thu Feb 13 22:02:08 2014 +0100
+++ b/test/testasm.py	Mon Feb 17 20:41:30 2014 +0100
@@ -4,6 +4,8 @@
 from ppci import CompilerError
 from ppci.asmnodes import AInstruction, ABinop, AUnop, ASymbol, ALabel, ANumber
 from ppci.assembler import tokenize, Assembler
+from ppci.objectfile import ObjectFile
+from ppci.linker import Linker
 import outstream
 from target import Label
 
@@ -92,10 +94,11 @@
 
 class OustreamTestCase(unittest.TestCase):
     def test1(self):
-        o = outstream.BinOutputStream()
+        obj = ObjectFile()
+        o = outstream.BinaryOutputStream(obj)
         o.selectSection('.text')
         o.emit(Label('a'))
-        self.assertSequenceEqual(bytes(), o.Data)
+        self.assertSequenceEqual(bytes(), obj.get_section('.text').data)
 
 
 class AsmTestCaseBase(unittest.TestCase):
@@ -104,7 +107,10 @@
         self.a.assemble(line)
 
     def check(self, hexstr):
-        self.assertSequenceEqual(bytes.fromhex(hexstr), self.o.Data)
+        l = Linker()
+        self.obj = l.link([self.obj])
+        data = bytes(self.obj.get_section('.text').data)
+        self.assertSequenceEqual(bytes.fromhex(hexstr), data)
 
 
 if __name__ == '__main__':
--- a/test/testcg.py	Thu Feb 13 22:02:08 2014 +0100
+++ b/test/testcg.py	Mon Feb 17 20:41:30 2014 +0100
@@ -18,7 +18,7 @@
         self.cg = CodeGenerator(armtarget)
 
     def testFunction(self):
-        s = outstream.OutputStream()
+        s = outstream.BinaryOutputStream(ppci.objectfile.ObjectFile())
         m, f, bb = genTestFunction()
         bb.addInstruction(ir.Exp(ir.Const(123)))
         bb.addInstruction(ir.Jump(f.epiloog))
@@ -28,7 +28,7 @@
 
 class testArmCodeGeneration(unittest.TestCase):
     def testStack(self):
-        s = outstream.OutputStream()
+        s = outstream.BinaryOutputStream(ppci.objectfile.ObjectFile())
         cg = CodeGenerator(armtarget)
         m, f, bb = genTestFunction()
         bb.addInstruction(ir.Move(ir.Mem(ir.Const(1)), ir.Const(22)))
--- a/test/testmsp430asm.py	Thu Feb 13 22:02:08 2014 +0100
+++ b/test/testmsp430asm.py	Mon Feb 17 20:41:30 2014 +0100
@@ -3,6 +3,7 @@
 import unittest
 from ppci.asmnodes import AInstruction, ABinop, AUnop, ASymbol, ALabel, ANumber
 from ppci.assembler import tokenize, Assembler
+from ppci.objectfile import ObjectFile
 import outstream
 from target import Label
 from target.target_list import msp430target
@@ -12,7 +13,8 @@
 class AssemblerMSP430TestCase(AsmTestCaseBase):
     def setUp(self):
         self.t = msp430target
-        self.o = outstream.BinOutputStream()
+        self.obj = ObjectFile()
+        self.o = outstream.BinaryOutputStream(self.obj)
         self.o.selectSection('.text')
         self.a = Assembler(target=self.t, stream=self.o)
 
--- a/test/testzcc.py	Thu Feb 13 22:02:08 2014 +0100
+++ b/test/testzcc.py	Mon Feb 17 20:41:30 2014 +0100
@@ -3,7 +3,7 @@
 import sys
 
 import zcc
-import outstream
+from ppci.objectfile import ObjectFile
 import ppci
 import io
 import target
@@ -83,12 +83,12 @@
             function void t2() {var int t3; t3 = 2;}
         """
         f = io.StringIO(src)
-        outs = outstream.TextOutputStream()
+        out = ObjectFile()
         tg = target.target_list.armtarget
         tr = ppci.tasks.TaskRunner()
-        tr.add_task(ppci.buildtasks.Compile([f], [], tg, outs))
+        tr.add_task(ppci.buildtasks.Compile([f], [], tg, out))
         tr.run_tasks()
-        code = outs.getSection('code')
+        code = out.get_section('code')
         self.assertEqual(0x0, code.address)