diff --git a/ast.py b/ast.py index 49f712a572301ed16d04355c3609a14dc4214271..8c8fc9f8bda28a52b3af77eef2d087f459863266 100644 --- a/ast.py +++ b/ast.py @@ -45,7 +45,7 @@ class KernelFunction(Node): class Argument: def __init__(self, name, dtype): self.name = name - self.dtype = dtype + self.dtype = dtype # TODO ordentliche Klasse self.isFieldPtrArgument = False self.isFieldShapeArgument = False self.isFieldStrideArgument = False diff --git a/backends/llvm.py b/backends/llvm.py index 0d1eb1a86a3fde4636cf0ee901eb191d27ca0bb0..affc9987fdb2a8b753c3badfa1f45bd75c1ee11c 100644 --- a/backends/llvm.py +++ b/backends/llvm.py @@ -1,26 +1,19 @@ import llvmlite.ir as ir -import llvmlite.binding as llvm -import logging.config - -from sympy.utilities.codegen import CCodePrinter -from pystencils.ast import Node from sympy.printing.printer import Printer from sympy import S # S is numbers? -def generateLLVM(astNode): - return None - - class LLVMPrinter(Printer): """Convert expressions to LLVM IR""" - def __init__(self, module, builder, fn, *args, **kwargs): + def __init__(self, module, builder, fn=None, *args, **kwargs): self.func_arg_map = kwargs.pop("func_arg_map", {}) super(LLVMPrinter, self).__init__(*args, **kwargs) self.fp_type = ir.DoubleType() - #self.integer = ir.IntType(64) + self.fp_pointer = self.fp_type.as_pointer() + self.integer = ir.IntType(64) + self.void = ir.VoidType() self.module = module self.builder = builder self.fn = fn @@ -81,9 +74,32 @@ class LLVMPrinter(Printer): e = self.builder.fadd(e, node) return e + def _print_KernelFunction(self, function): + return_type = self.void + # TODO argument in their own call? + parameter_type = [] + for parameter in function.parameters: + # TODO what bout ptr shape and stride argument? + if parameter.isFieldArgument: + parameter_type.append(self.fp_pointer) + else: + parameter_type.append(self.fp_type) + # TODO need tuple()? + func_type = ir.FunctionType(return_type, tuple(parameter_type)) + self.fn = ir.Function(self.module, func_type, function.functionName) + + # func.attributes.add("inlinehint") + # func.attributes.add("argmemonly") + block = self.fn.append_basic_block(name="entry") + self.builder = ir.IRBuilder(block) + return self.fn + + + # TODO - assumes all called functions take one double precision argument. # Should have a list of math library functions to validate this. + # TODO delete this? def _print_Function(self, expr): name = expr.func.__name__ e0 = self._print(expr.args[0]) @@ -99,71 +115,7 @@ class LLVMPrinter(Printer): % type(expr)) -class Eval(object): - def __init__(self): - llvm.initialize() - llvm.initialize_all_targets() - llvm.initialize_native_target() - llvm.initialize_native_asmprinter() - self.target = llvm.Target.from_default_triple() - - def compile(self, module): - logger.debug('=============Preparse') - logger.debug(str(module)) - llvmmod = llvm.parse_assembly(str(module)) - llvmmod.verify() - logger.debug('=============Function in IR') - logger.debug(str(llvmmod)) - # TODO cpu, features, opt - cpu = llvm.get_host_cpu_name() - features = llvm.get_host_cpu_features() - logger.debug('=======Things') - logger.debug(cpu) - logger.debug(features.flatten()) - target_machine = self.target.create_target_machine(cpu=cpu, features=features.flatten(), opt=2) - - logger.debug('Machine = ' + str(target_machine.target_data)) - - with open('gen.ll', 'w') as f: - f.write(str(llvmmod)) - optimize = True - if optimize: - pmb = llvm.create_pass_manager_builder() - pmb.opt_level = 2 - pmb.disable_unit_at_a_time = False - pmb.loop_vectorize = True - pmb.slp_vectorize = True - # TODO possible to pass for functions - pm = llvm.create_module_pass_manager() - pm.add_instruction_combining_pass() - pm.add_function_attrs_pass() - pm.add_constant_merge_pass() - pm.add_licm_pass() - pmb.populate(pm) - pm.run(llvmmod) - logger.debug("==========Opt") - logger.debug(str(llvmmod)) - with open('gen_opt.ll', 'w') as f: - f.write(str(llvmmod)) - - with llvm.create_mcjit_compiler(llvmmod, target_machine) as ee: - ee.finalize_object() - - logger.debug('==========Machine code') - logger.debug(target_machine.emit_assembly(llvmmod)) - with open('gen.S', 'w') as f: - f.write(target_machine.emit_assembly(llvmmod)) - with open('gen.o', 'wb') as f: - f.write(target_machine.emit_object(llvmmod)) - - # fptr = CFUNCTYPE(c_double, c_double, c_double)(ee.get_function_address('add2')) - # result = fptr(2, 3) - # print(result) - return 0 - - -if __name__ == "__main__": - logger = logging.getLogger(__name__) -else: - logger = logging.getLogger(__name__) + +def generateLLVM(astNode): + return None diff --git a/llvm/jit.py b/llvm/jit.py new file mode 100644 index 0000000000000000000000000000000000000000..8e6fdb56f947ce6a98ba93aad5f8bb2ef9006014 --- /dev/null +++ b/llvm/jit.py @@ -0,0 +1,71 @@ +import llvmlite.binding as llvm +import logging.config + + +class Eval(object): + def __init__(self): + llvm.initialize() + llvm.initialize_all_targets() + llvm.initialize_native_target() + llvm.initialize_native_asmprinter() + self.target = llvm.Target.from_default_triple() + + def compile(self, module): + logger.debug('=============Preparse') + logger.debug(str(module)) + llvmmod = llvm.parse_assembly(str(module)) + llvmmod.verify() + logger.debug('=============Function in IR') + logger.debug(str(llvmmod)) + # TODO cpu, features, opt + cpu = llvm.get_host_cpu_name() + features = llvm.get_host_cpu_features() + logger.debug('=======Things') + logger.debug(cpu) + logger.debug(features.flatten()) + target_machine = self.target.create_target_machine(cpu=cpu, features=features.flatten(), opt=2) + + logger.debug('Machine = ' + str(target_machine.target_data)) + + with open('gen.ll', 'w') as f: + f.write(str(llvmmod)) + optimize = True + if optimize: + pmb = llvm.create_pass_manager_builder() + pmb.opt_level = 2 + pmb.disable_unit_at_a_time = False + pmb.loop_vectorize = True + pmb.slp_vectorize = True + # TODO possible to pass for functions + pm = llvm.create_module_pass_manager() + pm.add_instruction_combining_pass() + pm.add_function_attrs_pass() + pm.add_constant_merge_pass() + pm.add_licm_pass() + pmb.populate(pm) + pm.run(llvmmod) + logger.debug("==========Opt") + logger.debug(str(llvmmod)) + with open('gen_opt.ll', 'w') as f: + f.write(str(llvmmod)) + + with llvm.create_mcjit_compiler(llvmmod, target_machine) as ee: + ee.finalize_object() + + logger.debug('==========Machine code') + logger.debug(target_machine.emit_assembly(llvmmod)) + with open('gen.S', 'w') as f: + f.write(target_machine.emit_assembly(llvmmod)) + with open('gen.o', 'wb') as f: + f.write(target_machine.emit_object(llvmmod)) + + # fptr = CFUNCTYPE(c_double, c_double, c_double)(ee.get_function_address('add2')) + # result = fptr(2, 3) + # print(result) + return 0 + + +if __name__ == "__main__": + logger = logging.getLogger(__name__) +else: + logger = logging.getLogger(__name__) diff --git a/llvm/kernelcreation.py b/llvm/kernelcreation.py deleted file mode 100644 index 89eca80b86f18f9fdf4a3be208e0f32885539af2..0000000000000000000000000000000000000000 --- a/llvm/kernelcreation.py +++ /dev/null @@ -1,65 +0,0 @@ -import sympy as sp -from pystencils.transformations import resolveFieldAccesses, makeLoopOverDomain, typingFromSympyInspection, \ - typeAllEquations, getOptimalLoopOrdering, parseBasePointerInfo, moveConstantsBeforeLoop, splitInnerLoop -from pystencils.typedsymbol import TypedSymbol -from pystencils.field import Field -import pystencils.ast as ast - - -def createKernel(listOfEquations, functionName="kernel", typeForSymbol=None, splitGroups=(), - iterationSlice=None, ghostLayers=None): - """ - Creates an abstract syntax tree for a kernel function, by taking a list of update rules. - - Loops are created according to the field accesses in the equations. - - :param listOfEquations: list of sympy equations, containing accesses to :class:`pystencils.field.Field`. - Defining the update rules of the kernel - :param functionName: name of the generated function - only important if generated code is written out - :param typeForSymbol: a map from symbol name to a C type specifier. If not specified all symbols are assumed to - be of type 'double' except symbols which occur on the left hand side of equations where the - right hand side is a sympy Boolean which are assumed to be 'bool' . - :param splitGroups: Specification on how to split up inner loop into multiple loops. For details see - transformation :func:`pystencils.transformation.splitInnerLoop` - :param iterationSlice: if not None, iteration is done only over this slice of the field - :param ghostLayers: a sequence of pairs for each coordinate with lower and upper nr of ghost layers - if None, the number of ghost layers is determined automatically and assumed to be equal for a - all dimensions - - :return: :class:`pystencils.ast.KernelFunction` node - """ - if not typeForSymbol: - typeForSymbol = typingFromSympyInspection(listOfEquations, "double") - - def typeSymbol(term): - if isinstance(term, Field.Access) or isinstance(term, TypedSymbol): - return term - elif isinstance(term, sp.Symbol): - return TypedSymbol(term.name, typeForSymbol[term.name]) - else: - raise ValueError("Term has to be field access or symbol") - - fieldsRead, fieldsWritten, assignments = typeAllEquations(listOfEquations, typeForSymbol) - allFields = fieldsRead.union(fieldsWritten) - - for field in allFields: - field.setReadOnly(False) - for field in fieldsRead - fieldsWritten: - field.setReadOnly() - - body = ast.Block(assignments) - code = makeLoopOverDomain(body, functionName, iterationSlice=iterationSlice, ghostLayers=ghostLayers) - - if splitGroups: - typedSplitGroups = [[typeSymbol(s) for s in splitGroup] for splitGroup in splitGroups] - splitInnerLoop(code, typedSplitGroups) - - loopOrder = getOptimalLoopOrdering(allFields) - - basePointerInfo = [['spatialInner0'], ['spatialInner1']] - basePointerInfos = {field.name: parseBasePointerInfo(basePointerInfo, loopOrder, field) for field in allFields} - - resolveFieldAccesses(code, fieldToBasePointerInfo=basePointerInfos) - moveConstantsBeforeLoop(code) - - return code \ No newline at end of file