Mercurial > lcfOS
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'):