changeset 110:9e552d34bd60

Work on compiler
author Windel Bouwman
date Fri, 04 Jan 2013 15:25:58 +0100
parents 8267ba1dbce3
children d3068efdf045
files python/ppci/core/__init__.py python/ppci/core/asmwriter.py python/ppci/core/basicblock.py python/ppci/core/context.py python/ppci/core/function.py python/ppci/core/instruction.py python/ppci/core/llvmtype.py python/ppci/core/value.py python/ppci/frontends/ks/__init__.py python/ppci/frontends/ks/builtin.py python/ppci/frontends/ks/irgenerator.py python/ppci/frontends/ks/nodes.py python/ppci/frontends/ks/parser.py python/tests/code1.ks
diffstat 14 files changed, 176 insertions(+), 171 deletions(-) [+]
line wrap: on
line diff
--- a/python/ppci/core/__init__.py	Tue Jan 01 17:17:44 2013 +0100
+++ b/python/ppci/core/__init__.py	Fri Jan 04 15:25:58 2013 +0100
@@ -1,12 +1,14 @@
-from .instruction import *
+from .instruction import BinaryOperator
 from .function import Function
-from .value import Value
+from .value import Value, Constant
 from .bitreader import BitcodeReader, BitcodeWriter
 from .errors import CompilerException
 from .module import Module
-from .llvmtype import FunctionType
+from .llvmtype import FunctionType, i8, i16, i32, void
 from .context import Context
 from .asmwriter import AsmWriter
+from .basicblock import BasicBlock
+from .irbuilder import IRBuilder
 
 version='0.0.1'
 
--- a/python/ppci/core/asmwriter.py	Tue Jan 01 17:17:44 2013 +0100
+++ b/python/ppci/core/asmwriter.py	Fri Jan 04 15:25:58 2013 +0100
@@ -1,5 +1,6 @@
 
 from . import llvmtype
+from .instruction import BinaryOperator
 #typeNames[VoidType] = 'void'
 
 class AsmWriter:
@@ -9,7 +10,6 @@
    def printModule(self, module):
       if module.Identifier:
          print('; ModuleID = {0}'.format(module.Identifier))
-      # 
       # Print functions:
       for f in module.Functions:
          self.printFunction(f)
@@ -20,10 +20,9 @@
       args = '()'
       print('define {0} {1}{2}'.format(t, f.name, args))
       print('{')
-
       for bb in f.BasicBlocks:
-         print(bb)
-
+         print('basic block!')
+         self.printBasicBlock(bb)
       print('}')
 
    def strType(self, t):
@@ -36,5 +35,9 @@
       for instr in bb.Instructions:
          self.printInstruction(instr)
    def printInstruction(self, i):
-      pass
+      print('Instruction!')
+      if isinstance(i, BinaryOperator):
+         print(i.operation, i.value1.Name, i.value2.Name)
+      else:
+         print(i)
 
--- a/python/ppci/core/basicblock.py	Tue Jan 01 17:17:44 2013 +0100
+++ b/python/ppci/core/basicblock.py	Fri Jan 04 15:25:58 2013 +0100
@@ -1,4 +1,4 @@
-
+from .value import Value
 
 class BasicBlock(Value):
    """ 
@@ -6,5 +6,10 @@
      jumps and branches.
    """
    def __init__(self):
-      pass
+      super().__init__()
+      self.instructions = []
+      self.name = None
+   def getInstructions(self):
+      return self.instructions
+   Instructions = property(getInstructions)
 
--- a/python/ppci/core/context.py	Tue Jan 01 17:17:44 2013 +0100
+++ b/python/ppci/core/context.py	Fri Jan 04 15:25:58 2013 +0100
@@ -3,10 +3,10 @@
 class Context:
    """ Global context """
    def __init__(self):
-      self.Int8Type = IntegerType(self, 8)
-      self.Int16Type = IntegerType(self, 16)
-      self.Int32Type = IntegerType(self, 32)
-      self.Int64Type = IntegerType(self, 64)
-      self.VoidType = llvmType(self, typeID.Void)
-      self.DoubleType = llvmType(self, typeID.Double)
+      self.Int8Type = IntegerType(8)
+      self.Int16Type = IntegerType(16)
+      self.Int32Type = IntegerType(32)
+      self.Int64Type = IntegerType(64)
+      self.VoidType = llvmType(typeID.Void)
+      self.DoubleType = llvmType(typeID.Double)
 
--- a/python/ppci/core/function.py	Tue Jan 01 17:17:44 2013 +0100
+++ b/python/ppci/core/function.py	Fri Jan 04 15:25:58 2013 +0100
@@ -5,8 +5,9 @@
       self.name = name
       self.function = function
 
-class Function:
+class Function(GlobalValue):
    def __init__(self, functiontype, name, module):
+      super().__init__()
       self.functiontype = functiontype
       self.name = name
       self.module = module
--- a/python/ppci/core/instruction.py	Tue Jan 01 17:17:44 2013 +0100
+++ b/python/ppci/core/instruction.py	Fri Jan 04 15:25:58 2013 +0100
@@ -1,15 +1,26 @@
 
 from .value import Value
 
+def Enum(**enums):
+   return type('Enum', (), enums)
+
 class Instruction(Value):
    """ Base class for all instructions. """
    pass
 
 class CallInstruction(Instruction):
-   pass
+   def __init__(self, callee, arguments):
+      super().__init__()
+      self.callee = callee
+      self.arguments = arguments
+
+BinOps = Enum(Add=1, Sub=2, Mul=3)
 
 class BinaryOperator(Instruction):
    def __init__(self, operation, value1, value2):
+      assert value1
+      assert value2
+      print('operation is in binops:', operation in BinOps)
       # Check types of the two operands:
       self.value1 = value1
       self.value2 = value2
--- a/python/ppci/core/llvmtype.py	Tue Jan 01 17:17:44 2013 +0100
+++ b/python/ppci/core/llvmtype.py	Fri Jan 04 15:25:58 2013 +0100
@@ -5,19 +5,26 @@
 typeID = Enum(Void=0, Double=3, Integer=10, Function=11, Struct=12, Array=13, Pointer=14, Vector=15)
 
 class llvmType:
-   def __init__(self, context, tid):
-      self.context = context
+   def __init__(self, tid):
       self.tid = tid
       
 class IntegerType(llvmType):
-   def __init__(self, context, bits):
-      super().__init__(context, typeID.Integer)
+   def __init__(self, bits):
+      super().__init__(typeID.Integer)
       self.bits = bits
 
 class FunctionType(llvmType):
    def __init__(self, resultType, parameterTypes):
-      super().__init__(resultType.context, typeID.Function)
+      super().__init__(typeID.Function)
+      assert type(parameterTypes) is list
       self.resultType = resultType
       self.returnType = resultType
       self.parameterTypes = parameterTypes
 
+# Default types:
+i8 = IntegerType(8)
+i16 = IntegerType(16)
+i32 = IntegerType(32)
+void = llvmType(typeID.Void)
+
+
--- a/python/ppci/core/value.py	Tue Jan 01 17:17:44 2013 +0100
+++ b/python/ppci/core/value.py	Fri Jan 04 15:25:58 2013 +0100
@@ -1,13 +1,15 @@
 
 class Value:
-   def __init__(self, vty):
-      self.valueType = ty
+   def __init__(self, vty=None):
+      self.valueType = vty
       self.name = None
    def getContext(self):
       return self.valueType.context
    def dump(self):
       print(self)
 
+   def getName(self):
+      return self.name
    def setName(self, name):
       if not self.name and not name:
          return
@@ -17,4 +19,11 @@
          pass
       else:
          pass
+   Name = property(getName, setName)
 
+class Constant(Value):
+   def __init__(self, value, vty):
+      super().__init__(vty)
+      self.value = value
+      print('new constant value: ', value)
+
--- a/python/ppci/frontends/ks/__init__.py	Tue Jan 01 17:17:44 2013 +0100
+++ b/python/ppci/frontends/ks/__init__.py	Fri Jan 04 15:25:58 2013 +0100
@@ -16,6 +16,7 @@
       # Pass 1: parsing and type checking
       p = KsParser(src)
       ast = p.parseModule() # Parse source into an AST
+      print(ast)
 
       # Store ast:
       self.ast = ast
--- a/python/ppci/frontends/ks/builtin.py	Tue Jan 01 17:17:44 2013 +0100
+++ b/python/ppci/frontends/ks/builtin.py	Fri Jan 04 15:25:58 2013 +0100
@@ -1,6 +1,6 @@
 from .nodes import *
 
-boolean = BaseType('boolean', 8) # Choose: 1 or 8 bytes?
+boolean = BaseType('boolean', 8)
 integer = BaseType('integer', 8)
 real = BaseType('real', 8)
 char = BaseType('char', 1)
--- a/python/ppci/frontends/ks/irgenerator.py	Tue Jan 01 17:17:44 2013 +0100
+++ b/python/ppci/frontends/ks/irgenerator.py	Fri Jan 04 15:25:58 2013 +0100
@@ -5,106 +5,87 @@
 from .nodes import *
 from ...core.errors import Error
 from ... import core
-from .builtin import real, integer, boolean, char
+from .builtin import real, integer, boolean, char, void
+
+def coreType(typ):
+   """ Return the correct core type given a type """
+   if type(typ) is BaseType:
+      if typ is integer:
+         return core.i32
+      if typ is void:
+         return core.void
+   elif type(typ) is ProcedureType:
+      rType = coreType(typ.returntype)
+      fpTypes = [coreType(p.typ) for p in typ.parameters]
+      return core.FunctionType(rType, fpTypes)
+   print(typ)
+   raise NotImplementedError()
 
 class KsIrGenerator:
    def __init__(self):
-      pass
-
+      self.builder = core.IRBuilder()
    # Code generation functions:
    def genexprcode(self, node):
-      """ 
-         Generate code for expressions!
-         Recursively evaluates, and ensures a register contains the answer.
-         register is an integer register or a floating point reg
-      """
+      """ Generate code for expressions! """
       if isinstance(node, Binop):
          """ Handle a binary operation (two arguments) of some kind """
-         self.genexprcode(node.a)
-         self.genexprcode(node.b)
+         lhs = self.genexprcode(node.a)
+         rhs = self.genexprcode(node.b)
 
-         if node.op == 'mod':
-            assert(node.typ.isType(integer))
-         elif node.op == 'div':
-            assert(node.typ.isType(integer))
-         elif node.op == '*':
+         if node.op == '*':
             if node.typ.isType(integer):
-               pass
+               return self.builder.createMul(lhs, rhs)
          elif node.op == '+':
             if node.typ.isType(integer):
-               pass
+               return self.builder.createAdd(lhs, rhs)
          elif node.op == '-':
             if node.typ.isType(integer):
-               pass
-         else:
-            Error('Unknown Binop {0}'.format(node.op))
-
-      elif type(node) is Unop:
-         if node.op == 'INTTOREAL':
-            self.genexprcode(node.a)
-            code.append('Unop inttoreal TODO')
-         elif node.op == 'ABS':
-            if isType(node.typ, real):
-               Error('ABS error integer')
-            elif isType(node.typ, integer):
-               code = []
-               Error('ABS error integer')
-            else:
-               Error('ABS error')
-         else:
-            Error('Unknown Unop {0}'.format(node.op))
+               return self.builder.createSub(lhs, rhs)
+         Error('Unknown binop or type {0}'.format(node))
 
       elif isinstance(node, Designator):
          # dereference, array index. Make sure that the result comes into a register
          if len(node.selectors) > 0:
-            self.gencode(node) # Load the pointer into some register
-
-            # Now we can access the object at location '[node.reg]':
-            if node.typ.isType(integer):
-               self.addCode( mov(node.reg, [node.reg, 0x0]) )
-            else:
-               Error('Only integer types implemented')
+            Error('Only integer types implemented')
          else:
             # No selectors, load variable directly
-            if node.obj.typ.isType(integer):
-               if type(node.obj) is Constant:
-                  self.genexprcode(node.obj)
-                  node.reg = node.obj.reg
-               else:
-                  pass
-                  # Get a register to store the integer value
-            else:
-               Error('Cannot load variable type {0}'.format(node.typ))
+            print(node)
+            #Error('Cannot load variable type {0}'.format(node.typ))
       elif type(node) is Constant:
-         print('TODO: constant')
+         return core.Constant(node.value, coreType(node.typ))
       else:
          Error('Cannot generate expression code for: {0}'.format(node))
 
    def gencode(self, node):
       """ Code generation function for AST nodes """
       if isinstance(node, Module):
-         # TODO: recurse!
+         # Create module:
+         self.mod = core.Module(node.name) 
 
+         globs = node.symtable.getAllLocal(Variable)
+         for g in globs:
+            print('global:', g)
+         # Loop over all functions:
          print(node.symtable)
          node.symtable.printTable()
          funcs = node.symtable.getAllLocal(Procedure)
          for f in funcs:
-            print('function', f)
-            ftype = f.typ.coreType()
-            ftype = core.FunctionType(retType)
-         
-         self.mod = core.Module(node.name)
+            self.gencode(f)
          # Create a function called init for this module:
-         ftype = core.FunctionType(self.context.VoidType, [])
-         func = core.Function(ftype, "init", self.mod)
-         bb = self.gencode(node.initcode)
          self.mod.dump()
          return self.mod
 
       elif type(node) is Procedure:
-        # calculate offsets for local variables and parameters
-        # Variable location relative to 'rbp' register
-        variables = node.symtable.getAllLocal(Variable)
+            ftype = coreType(node.typ)
+            print('function', node, ftype)
+            func = core.Function(ftype, node.name, self.mod)
+            bb = core.BasicBlock()
+            func.basicblocks.append(bb)
+            self.builder.setInsertPoint(bb)
+            self.gencode(node.block)
+            self.builder.setInsertPoint(None)
+            variables = node.symtable.getAllLocal(Variable)
+            print(variables)
 
       elif isinstance(node, StatementSequence):
          for s in node.statements:
@@ -112,25 +93,17 @@
 
       elif type(node) is ProcedureCall:
          # Prepare parameters on the stack:
-         assert(len(node.args) == len(node.proc.typ.parameters))
-         for arg, param in zip(node.args, node.proc.typ.parameters):
-
-            if param.kind == 'value': 
-               self.genexprcode(arg)
-               self.addCode( push(arg.reg) )
-               self.freereg( arg )
-               stacksize += 8
-            else:
-               Error('Parameter kind other than value')
+         print("TODO")
 
       elif type(node) is Assignment:
          if node.lval.typ.isType(integer):
-           # TODO if node.rval is Constant of some datatype, move it to mem directly
-           self.genexprcode(node.rval) # Calculate the value that has to be stored.
-           print("TODO")
+           print('assign')
+           rhs = self.genexprcode(node.rval) # Calculate the value that has to be stored.
+           #self.gencode(node.lval)
+           print("TODO: assigment")
+           
          else:
             Error('Assignments of other types not implemented')
-            # TODO if left and right are designators, do some sort of memcpy.
 
       elif type(node) is IfStatement:
         self.genexprcode(node.condition)
@@ -152,54 +125,14 @@
          self.gencode(node.statements)
          Error('No implementation of FOR statement')
 
-      elif type(node) is AsmCode:
-         def processOperand(op):
-            if type(op) is list:
-               if type(op[0]) is Variable:
-                  var = op[0]
-                  if var.isLocal:
-                     return ['rbp', var.offset]
-                  else:
-                     Error('Can only use local variables in inline assembler')
-            return op
-         for asmline in node.asmcode:
-            opcode, operands = asmline
-            operands = [processOperand(opx) for opx in operands]
-            print('assembling', opcode, *operands)
-            func,nargs = opcodes[opcode]
-            code = func(*operands)
-            self.addCode(code)
-
       elif isinstance(node, EmptyStatement):
-         pass
+         pass # That was easy :)
 
       elif type(node) is StringConstant:
         self.strings.append(node)
-        self.data.append(node.value) # Add string to the data section
 
       elif type(node) is Designator:
-         if len(node.selectors) > 0:
-            self.getreg(node)
-            # Load starting address
-            if node.obj.isLocal:
-               self.addCode( leareg64(node.reg, ['rbp', node.obj.offset]) )
-            else:
-               # Global variables need to be relocated...
-               self.addCode(leareg64(node.reg, ['RIP', 0]))
-               self.fixCode(self.rip - 4, imm32(node.obj.offset - self.rip))
-            # Loop over all designators..
-            for selector in node.selectors:
-               if type(selector) is Index:
-                  # Deref an array index
-                  self.genexprcode(selector.index)
-                  self.freereg(selector)
-               elif type(selector) is Field:
-                  print('Field')
-                  Error('Field not implemented')
-               else:
-                  Error('Unknown selector')
-         else:
-            Error('Can only gencode for designator with selectors')
+         Error('Can only gencode for designator with selectors')
       else:
          print('not generating code for {0}'.format(node))
 
--- a/python/ppci/frontends/ks/nodes.py	Tue Jan 01 17:17:44 2013 +0100
+++ b/python/ppci/frontends/ks/nodes.py	Fri Jan 04 15:25:58 2013 +0100
@@ -129,8 +129,6 @@
       self.returntype = returntype
    def __repr__(self):
       return '[PROCTYPE {0} RET {1}]'.format(self.parameters, self.returntype)
-   def coreType(self):
-      return None
 
 class DefinedType(Type):
    def __init__(self, name, typ):
--- a/python/ppci/frontends/ks/parser.py	Tue Jan 01 17:17:44 2013 +0100
+++ b/python/ppci/frontends/ks/parser.py	Fri Jan 04 15:25:58 2013 +0100
@@ -95,6 +95,7 @@
    def parseImport(self):
       loc = self.getLocation()
       modname = self.Consume('ID')
+      # TODO: fix
       #mod = loadModule(modname)
       self.setLocation(mod, loc)
       self.cst.addSymbol(mod)
@@ -222,9 +223,10 @@
       else:
          self.Error('Cannot evaluate expression {0}'.format(expr))
 
-   def parseConstExpression(self):
+   def parseConstant(self):
       e = self.parseExpression()
-      return self.evalExpression(e), e.typ
+      val = self.evalExpression(e)
+      return Constant(val, e.typ)
 
    def parseConstantDeclarations(self):
       """ Parse const part of a module """
@@ -232,9 +234,10 @@
          while self.token.typ == 'ID':
             i = self.parseIdentDef()
             self.Consume('=')
-            constvalue, typ = self.parseConstExpression()
+            c = self.parseConstant()
             self.Consume(';')
-            c = Constant(constvalue, typ, name=i.name, public=i.ispublic)
+            c.name = i.name
+            c.public = i.ispublic
             self.setLocation(c, i.location)
             self.cst.addSymbol(c)
      
@@ -265,17 +268,17 @@
    def parseStructuredType(self):
       if self.hasConsumed('array'):
          dimensions = []
-         dimensions.append( self.parseConstExpression() )
+         dimensions.append( self.parseConstant() )
          while self.hasConsumed(','):
-             dimensions.append( self.parseConstExpression() )
+             dimensions.append( self.parseConstant() )
          self.Consume('of')
          arr = self.parseType()
-         for dimension, consttyp in reversed(dimensions):
-            if not isType(consttyp, integer):
+         for dimension in reversed(dimensions):
+            if not isType(dimension.typ, integer):
                self.Error('array dimension must be an integer type (not {0})'.format(consttyp))
-            if dimension < 2:
-               self.Error('array dimension must be bigger than 1 (not {0})'.format(dimension))
-            arr = ArrayType(dimension, arr)
+            if dimension.value < 2:
+               self.Error('array dimension must be bigger than 1 (not {0})'.format(dimension.value))
+            arr = ArrayType(dimension.value, arr)
          return arr
       elif self.hasConsumed('record'):
          fields = {}
@@ -436,6 +439,7 @@
       else:
          args = []
       self.Consume(')')
+      # Type checking:
       parameters = procedure.typ.parameters
       if len(args) != len(parameters):
          self.Error("Procedure requires {0} arguments, {1} given".format(len(parameters), len(args)))
@@ -500,6 +504,7 @@
       stmt = self.parseStatementSequence()
       self.Consume('until')
       cond = self.parseBoolExpression()
+      # TODO
 
    def parseForStatement(self):
       loc = self.getLocation()
@@ -517,9 +522,10 @@
       if not end.typ.isType(integer):
          self.Error('end expression of a for statement must have integer type')
       if self.hasConsumed('by'):
-         increment, typ = self.parseConstExpression()
-         if not typ.isType(integer):
+         increment = self.parseConstant()
+         if not increment.typ.isType(integer):
             self.Error('Increment must be integer')
+         increment = increment.value
       else:
          increment = 1
       assert(type(increment) is int)
@@ -530,6 +536,7 @@
 
    def parseAsmcode(self):
       # TODO: move this to seperate file
+      # TODO: determine what to do with inline asm?
       def parseOpcode():
          return self.Consume('ID')
       def parseOperand():
@@ -607,10 +614,10 @@
 
    def parseStatementSequence(self):
        """ Sequence of statements seperated by ';' """
-       statements = [ self.parseStatement() ]
+       statements = [self.parseStatement()]
        while self.hasConsumed(';'):
-         statements.append( self.parseStatement() )
-       return StatementSequence( statements )
+         statements.append(self.parseStatement())
+       return StatementSequence(statements)
 
    # Parsing expressions:
    """
@@ -624,8 +631,35 @@
      factor           = number | nil | true | false | "(" expression ")" | 
                         designator [ actualparameters ] | 'not' factor
    """
+   def getTokenPrecedence(self):
+      binopPrecs = {}
+      binopPrecs['and'] = 8
+      binopPrecs['or'] = 6
+      binopPrecs['<'] = 10
+      binopPrecs['>'] = 10
+      binopPrecs['='] = 10
+      binopPrecs['<='] = 10
+      binopPrecs['>='] = 10
+      binopPrecs['<>'] = 10
+      binopPrecs['+'] = 20
+      binopPrecs['-'] = 20
+      binopPrecs['*'] = 40
+      binopPrecs['/'] = 40
+      binopPrecs['div'] = 40
+      binopPrecs['mod'] = 40
+
+      typ = self.token.typ
+      if typ in binopPrecs:
+         return binopPrecs[typ]
+      return 0
+   def parsePrimary(self):
+      pass
    def parseExpression(self):
       """ The connector between the boolean and expression domain """
+      # TODO: implement precedence bindin
+      #lhs = self.parsePrimary()
+      #return self.parseBinopRhs(lhs)
+
       expr = self.parseSimpleExpression()
       if self.token.typ in ['>=','<=','<','>','<>','=']:
          relop = self.Consume()
@@ -640,7 +674,6 @@
             self.Error('Type mismatch in relop')
          if isType(expr.typ, real) and relop in ['<>', '=']:
             self.Error('Cannot check real values for equality')
-
          expr = Relop(expr, relop, expr2, boolean)
       return expr
 
@@ -717,10 +750,10 @@
       elif self.token.typ == 'STRING':
          txt = self.Consume('STRING')
          return StringConstant(txt)
-      elif self.token.typ in ['true', 'false']:
-         val = self.Consume()
-         val = True if val == 'true' else False
-         return Constant(val, boolean)
+      elif self.hasConsumed('true'):
+         return Constant(True, boolean)
+      elif self.hasConsumed('false'):
+         return Constant(False, boolean)
       elif self.hasConsumed('nil'):
          return Constant(0, NilType())
       elif self.hasConsumed('not'):
--- a/python/tests/code1.ks	Tue Jan 01 17:17:44 2013 +0100
+++ b/python/tests/code1.ks	Fri Jan 04 15:25:58 2013 +0100
@@ -1,9 +1,11 @@
 module s;
 var a, b, c:integer;
 
-procedure x();
+procedure test(x:integer);
 begin
-end x;
+   a := x * x + 1337;
+   b := a * x
+end test;
 
 begin
   a := 4 + 6;