def generate_function(self, funcname, argnames, ircode): # Generate code for a single Wabbit function. Each opcode # tuple (opcode, args) is dispatched to a method of the form # self.emit_opcode(args). Function should already be declared # using declare_function. self.function = self.globals[funcname] self.block = self.function.append_basic_block('entry') self.builder = IRBuilder(self.block) # Stack of LLVM temporaries self.stack = [] # Dictionary of local variables self.locals = { } for opcode, *opargs in ircode: if hasattr(self, 'emit_'+opcode): getattr(self, 'emit_'+opcode)(*opargs) else: print('Warning: No emit_'+opcode+'() method') # Add a return statement to void functions. if self.function.function_type.return_type == void_type: self.builder.ret_void()
def __init__(self): # Perform the basic LLVM initialization. You need the following parts: # # 1. A top-level Module object # 2. A Function instance in which to insert code # 3. A Builder instance to generate instructions # # Note: For project 5, we don't have any user-defined # functions so we're just going to emit all LLVM code into a top # level function void main() { ... }. This will get changed later. self.module = Module('module') self.function = Function(self.module, FunctionType(void_type, []), name='main') self.block = self.function.append_basic_block('entry') self.builder = IRBuilder(self.block) # Dictionary that holds all of the global variable/function declarations. # Any declaration in the Gone source code is going to get an entry here self.vars = {} # Dictionary that holds all of the temporary registers created in # the intermediate code. self.temps = {} # Initialize the runtime library functions (see below) self.declare_runtime_library()
def Codegen(ast: ProgramNode, printIR: bool = True): """Genera l'LLVM IR dall'AST in input. Args: ast: L'albero di sintassi astratto. printIR (bool): Un boolean che indica se fare un print dell'IR generato (default `False`) Returns: L'IR generato sotto forma di `str` Raises: StdlibNotFoundError: Se la standard lib non puo' essere trovata """ llvm.initialize() llvm.initialize_native_target() llvm.initialize_native_asmprinter() # Decide l'estensione della stdlib ext = None if platform.system() == 'Windows': ext = 'dll' elif platform.system() == 'Darwin': ext = 'dylib' elif platform.system() == 'Linux': ext = 'so' path = pathlib.Path(__file__).parent.absolute() try: llvm.load_library_permanently( str(path) + "/../../bin/tiny_hi_core.{}".format(ext)) except Exception: print("StdlibNotFoundError: Cannot find the standard library (tiny_hi_core.{})".format(ext)) return (None, None) module = Module() builder = IRBuilder() context = Context(module) entry = None llmod = None try: entry = ast.entry_point() ast.codegen(builder, context) strmod = str(module) if printIR: print(strmod) llmod = llvm.parse_assembly(strmod) except Exception as e: print("Codegen Failed") print("{}: {}".format(type(e).__name__, e)) finally: return (llmod, entry)
def visitFunction_definition(self, ctx: SmallCParser.Function_definitionContext): retType = self.getType(ctx.type_specifier().getText()) if ctx.identifier().ASTERIKS(): retType = retType.as_pointer() argsType = [] argsName = [] # args if ctx.param_decl_list(): args = ctx.param_decl_list() var_arg = False for t in args.getChildren(): if t.getText() != ',': if t.getText() == '...': var_arg = True break t_type = self.getType(t.type_specifier().getText()) if t.identifier().ASTERIKS(): t_type = t_type.as_pointer() argsType.append(t_type) argsName.append(t.identifier().IDENTIFIER().getText()) funcType = FunctionType(retType, tuple(argsType), var_arg=var_arg) # no args else: funcType = FunctionType(retType, ()) # function if ctx.identifier().IDENTIFIER().getText() in self.function_dict: func = self.function_dict[ctx.identifier().IDENTIFIER().getText()] else: func = Function(self.Module, funcType, name=ctx.identifier().IDENTIFIER().getText()) self.function_dict[ctx.identifier().IDENTIFIER().getText()] = func # blocks or ; if ctx.compound_stmt(): self.function = ctx.identifier().IDENTIFIER().getText() block = func.append_basic_block( ctx.identifier().IDENTIFIER().getText()) varDict = dict() self.Builder = IRBuilder(block) for i, arg in enumerate(func.args): arg.name = argsName[i] alloca = self.Builder.alloca(arg.type, name=arg.name) self.Builder.store(arg, alloca) varDict[arg.name] = { "id": arg.name, "type": arg.type, "value": None, "ptr": alloca } self.var_stack.append(varDict) self.visit(ctx.compound_stmt()) if isinstance(retType, VoidType): self.Builder.ret_void() self.var_stack.pop() self.function = None return
def generate_functions(self, functions): type_dict = { 'int': int_type, 'float': float_type, 'byte': byte_type, 'bool': int_type, 'void': void_type } for function in functions: # register function name = function.name if function.name != 'main' else '_gone_main' return_type = type_dict[function.return_type] param_types = [type_dict[t] for t in function.param_types] function_type = FunctionType(return_type, param_types) self.function = Function(self.module, function_type, name=name) self.globals[name] = self.function self.blocks = {} self.block = self.function.append_basic_block('entry') self.blocks['entry'] = self.block self.builder = IRBuilder(self.block) # local scope self.vars = self.vars.new_child() self.temps = {} for n, (param_name, param_type) in enumerate( zip(function.param_names, param_types)): var = self.builder.alloca(param_type, name=param_name) var.initializer = Constant(param_type, 0) self.vars[param_name] = var self.builder.store(self.function.args[n], self.vars[param_name]) # alloc return var / block if function.return_type != 'void': self.vars['return'] = self.builder.alloca(return_type, name='return') self.return_block = self.function.append_basic_block('return') # generate instructions self.generate_code(function) # return if not self.block.is_terminated: self.builder.branch(self.return_block) self.builder.position_at_end(self.return_block) if function.return_type != 'void': self.builder.ret( self.builder.load(self.vars['return'], 'return')) else: self.builder.ret_void() self.vars = self.vars.parents
def generate_code(self, ir_function): # Given a sequence of SSA intermediate code tuples, generate LLVM # instructions using the current builder (self.builder). Each # opcode tuple (opcode, args) is dispatched to a method of the # form self.emit_opcode(args) # print(ir_function) self.function = Function( self.module, FunctionType(LLVM_TYPE_MAPPING[ir_function.return_type], [ LLVM_TYPE_MAPPING[ptype] for _, ptype in ir_function.parameters ]), name=ir_function.name) self.block = self.function.append_basic_block('entry') self.builder = IRBuilder(self.block) # Save the function as a global to be referenced later on another # function self.globals[ir_function.name] = self.function # All local variables are stored here self.locals = {} self.vars = ChainMap(self.locals, self.globals) # Dictionary that holds all of the temporary registers created in # the intermediate code. self.temps = {} # Setup the function parameters for n, (pname, ptype) in enumerate(ir_function.parameters): self.vars[pname] = self.builder.alloca(LLVM_TYPE_MAPPING[ptype], name=pname) self.builder.store(self.function.args[n], self.vars[pname]) # Allocate the return value and return_block if ir_function.return_type: self.vars['return'] = self.builder.alloca( LLVM_TYPE_MAPPING[ir_function.return_type], name='return') self.return_block = self.function.append_basic_block('return') for opcode, *args in ir_function.code: if hasattr(self, 'emit_' + opcode): getattr(self, 'emit_' + opcode)(*args) else: print('Warning: No emit_' + opcode + '() method') if not self.block.is_terminated: self.builder.branch(self.return_block) self.builder.position_at_end(self.return_block) self.builder.ret(self.builder.load(self.vars['return'], 'return'))
def lower_finalize_func(self, lower): """ Lower the generator's finalizer. """ fnty = llvmlite.ir.FunctionType(llvmlite.ir.VoidType(), [self.context.get_value_type(self.gentype)]) function = cgutils.get_or_insert_function( lower.module, fnty, self.gendesc.llvm_finalizer_name) entry_block = function.append_basic_block('entry') builder = IRBuilder(entry_block) genptrty = self.context.get_value_type(self.gentype) genptr = builder.bitcast(function.args[0], genptrty) self.lower_finalize_func_body(builder, genptr)
def __init__(self): self.module = Module('hello') self._print_int = Function(self.module, FunctionType(void_type, [int_type]), name='_print_int') self.function = Function(self.module, FunctionType(int_type, []), name='main') self.block = self.function.append_basic_block('entry') self.builder = IRBuilder(self.block) self.stack = [] self.vars = {}
def generate_code(self, ir_function): self.function = Function( self.module, FunctionType(LLVM_TYPE_MAPPING[ir_function.return_type], [ LLVM_TYPE_MAPPING[ptype] for _, ptype in ir_function.parameters ]), name=ir_function.name) self.block = self.function.append_basic_block('entry') self.builder = IRBuilder(self.block) self.globals[ir_function.name] = self.function self.locals = {} self.vars = ChainMap(self.locals, self.globals) self.temps = {} for n, (pname, ptype) in enumerate(ir_function.parameters): self.vars[pname] = self.builder.alloca(LLVM_TYPE_MAPPING[ptype], name=pname) self.builder.store(self.function.args[n], self.vars[pname]) if ir_function.return_type: self.vars['return'] = self.builder.alloca( LLVM_TYPE_MAPPING[ir_function.return_type], name='return') self.return_block = self.function.append_basic_block('return') for opcode, *args in ir_function.code: if hasattr(self, 'emit_' + opcode): getattr(self, 'emit_' + opcode)(*args) else: print('Warning: No emit_' + opcode + '() method') if not self.block.is_terminated: self.builder.branch(self.return_block) self.builder.position_at_end(self.return_block) self.builder.ret(self.builder.load(self.vars['return'], 'return'))
def generate_function(self, name, return_type, arg_types, arg_names, ircode): self.function = Function(self.module, FunctionType(return_type, arg_types), name=name) self.globals[name] = self.function self.block = self.function.append_basic_block('entry') self.builder = IRBuilder(self.block) self.locals = {} self.vars = ChainMap(self.locals, self.globals) # Have to declare local variables for holding function arguments for n, (name, ty) in enumerate(zip(arg_names, arg_types)): self.locals[name] = self.builder.alloca(ty, name=name) self.builder.store(self.function.args[n], self.locals[name]) # Dictionary that holds all of the temporary registers created in # the intermediate code. self.temps = {} # Make the exit block for function return self.return_block = self.function.append_basic_block('return') if return_type != void_type: self.return_var = self.builder.alloca(return_type, name='return') self.generate_code(ircode) if not self.block.is_terminated: self.builder.branch(self.return_block) self.builder.position_at_end(self.return_block) if return_type != void_type: self.builder.ret(self.builder.load(self.return_var)) else: self.builder.ret_void()
def create_function_body(self, func: RIALFunction, rial_arg_types: List[str]): self.current_func = func # Create entry block bb = func.append_basic_block("entry") llvm_bb = create_llvm_block(bb) self.current_block = llvm_bb if self.builder is None: self.builder = IRBuilder(bb) self.builder.position_at_start(bb) # Allocate new variables for the passed arguments for i, arg in enumerate(func.args): # Don't copy variables that are a pointer if isinstance(arg.type, PointerType): variable = RIALVariable(arg.name, rial_arg_types[i], arg) else: allocated_arg = self.builder.alloca(arg.type) self.builder.store(arg, allocated_arg) variable = RIALVariable(arg.name, rial_arg_types[i], allocated_arg) self.current_block.add_named_value(arg.name, variable)
def build(self): wrapname = self.fndesc.llvm_cpython_wrapper_name # This is the signature of PyCFunctionWithKeywords # (see CPython's methodobject.h) pyobj = self.context.get_argument_type(types.pyobject) wrapty = llvmlite.ir.FunctionType(pyobj, [pyobj, pyobj, pyobj]) wrapper = llvmlite.ir.Function(self.module, wrapty, name=wrapname) builder = IRBuilder(wrapper.append_basic_block('entry')) # - `closure` will receive the `self` pointer stored in the # PyCFunction object (see _dynfunc.c) # - `args` and `kws` will receive the tuple and dict objects # of positional and keyword arguments, respectively. closure, args, kws = wrapper.args closure.name = 'py_closure' args.name = 'py_args' kws.name = 'py_kws' api = self.context.get_python_api(builder) self.build_wrapper(api, builder, closure, args, kws) return wrapper, api
def generate_function(self, funcname, argnames, ircode): # Generate code for a single Wabbit function. Each opcode # tuple (opcode, args) is dispatched to a method of the form # self.emit_opcode(args). Function should already be declared # using declare_function. self.function = self.globals[funcname] self.block = self.function.append_basic_block('entry') self.builder = IRBuilder(self.block) # Stack of LLVM temporaries self.stack = [] # Dictionary of local variables self.locals = { } # Combined symbol table self.symbols = ChainMap(self.locals, self.globals) # Have to declare local variables for holding function arguments for n, (name, ty) in enumerate(zip(argnames, self.function.function_type.args)): self.locals[name] = self.builder.alloca(ty, name=name) self.builder.store(self.function.args[n], self.locals[name]) # Stack of blocks self.blocks = [ ] for opcode, *opargs in ircode: if hasattr(self, 'emit_'+opcode): getattr(self, 'emit_'+opcode)(*opargs) else: print('Warning: No emit_'+opcode+'() method') # Add a return statement to void functions. if self.function.function_type.return_type == void_type: self.builder.ret_void()
def _create_instruct(self, typ: str, is_printf: bool = False) -> None: """Create a new Function instruction and attach it to a new Basic Block Entry. Args: typ (str): node type. is_printf (bool, optional): Defaults to False. """ if is_printf or typ in ["String", "ArrayType"]: self.str_counter += 1 self.func_name = f"_{typ}.{self.str_counter}" func_type = FunctionType(VoidType(), []) elif typ == "Boolean": self.bool_counter += 1 self.func_name = f"_{typ}.{self.bool_counter}" func_type = FunctionType(IntType(1), []) else: self.expr_counter += 1 self.func_name = f"_{typ}_Expr.{self.expr_counter}" func_type = FunctionType(DoubleType(), []) main_func = Function(self.module, func_type, self.func_name) bb_entry = main_func.append_basic_block("entry") self.builder = IRBuilder(bb_entry)
def _build_wrapper(self, library, name): """ The LLVM IRBuilder code to create the gufunc wrapper. The *library* arg is the CodeLibrary to which the wrapper should be added. The *name* arg is the name of the wrapper function being created. """ intp_t = self.context.get_value_type(types.intp) fnty = self._wrapper_function_type() wrapper_module = library.create_ir_module('_gufunc_wrapper') func_type = self.call_conv.get_function_type(self.fndesc.restype, self.fndesc.argtypes) fname = self.fndesc.llvm_func_name func = ir.Function(wrapper_module, func_type, name=fname) func.attributes.add("alwaysinline") wrapper = ir.Function(wrapper_module, fnty, name) # The use of weak_odr linkage avoids the function being dropped due # to the order in which the wrappers and the user function are linked. wrapper.linkage = 'weak_odr' arg_args, arg_dims, arg_steps, arg_data = wrapper.args arg_args.name = "args" arg_dims.name = "dims" arg_steps.name = "steps" arg_data.name = "data" builder = IRBuilder(wrapper.append_basic_block("entry")) loopcount = builder.load(arg_dims, name="loopcount") pyapi = self.context.get_python_api(builder) # Unpack shapes unique_syms = set() for grp in (self.sin, self.sout): for syms in grp: unique_syms |= set(syms) sym_map = {} for syms in self.sin: for s in syms: if s not in sym_map: sym_map[s] = len(sym_map) sym_dim = {} for s, i in sym_map.items(): sym_dim[s] = builder.load( builder.gep(arg_dims, [self.context.get_constant(types.intp, i + 1)])) # Prepare inputs arrays = [] step_offset = len(self.sin) + len(self.sout) for i, (typ, sym) in enumerate( zip(self.signature.args, self.sin + self.sout)): ary = GUArrayArg(self.context, builder, arg_args, arg_steps, i, step_offset, typ, sym, sym_dim) step_offset += len(sym) arrays.append(ary) bbreturn = builder.append_basic_block('.return') # Prologue self.gen_prologue(builder, pyapi) # Loop with cgutils.for_range(builder, loopcount, intp=intp_t) as loop: args = [a.get_array_at_offset(loop.index) for a in arrays] innercall, error = self.gen_loop_body(builder, pyapi, func, args) # If error, escape cgutils.cbranch_or_continue(builder, error, bbreturn) builder.branch(bbreturn) builder.position_at_end(bbreturn) # Epilogue self.gen_epilogue(builder, pyapi) builder.ret_void() # Link library.add_ir_module(wrapper_module) library.add_linking_library(self.library)
# hellollvm.py from llvmlite.ir import ( Module, Function, FunctionType, IntType, Constant, IRBuilder ) mod = Module('hello') hello_func = Function(mod, FunctionType(IntType(32), []), name='hello') block = hello_func.append_basic_block('entry') builder = IRBuilder(block) builder.ret(Constant(IntType(32), 37)) # A user-defined function from llvmlite.ir import DoubleType ty_double = DoubleType() dsquared_func = Function(mod, FunctionType(ty_double, [ty_double, ty_double]), name='dsquared') block = dsquared_func.append_basic_block('entry') builder = IRBuilder(block) # Get the function args x, y = dsquared_func.args # Compute temporary values for x*x and y*y xsquared = builder.fmul(x, x) ysquared = builder.fmul(y, y)
def build_ufunc_wrapper(library, context, fname, signature, objmode, cres): """ Wrap the scalar function with a loop that iterates over the arguments Returns ------- (library, env, name) """ assert isinstance(fname, str) byte_t = ir.IntType(8) byte_ptr_t = ir.PointerType(byte_t) byte_ptr_ptr_t = ir.PointerType(byte_ptr_t) intp_t = context.get_value_type(types.intp) intp_ptr_t = ir.PointerType(intp_t) fnty = ir.FunctionType( ir.VoidType(), [byte_ptr_ptr_t, intp_ptr_t, intp_ptr_t, byte_ptr_t]) wrapperlib = context.codegen().create_library('ufunc_wrapper') wrapper_module = wrapperlib.create_ir_module('') if objmode: func_type = context.call_conv.get_function_type( types.pyobject, [types.pyobject] * len(signature.args)) else: func_type = context.call_conv.get_function_type( signature.return_type, signature.args) func = ir.Function(wrapper_module, func_type, name=fname) func.attributes.add("alwaysinline") wrapper = ir.Function(wrapper_module, fnty, "__ufunc__." + func.name) arg_args, arg_dims, arg_steps, arg_data = wrapper.args arg_args.name = "args" arg_dims.name = "dims" arg_steps.name = "steps" arg_data.name = "data" builder = IRBuilder(wrapper.append_basic_block("entry")) # Prepare Environment envname = context.get_env_name(cres.fndesc) env = cres.environment envptr = builder.load(context.declare_env_global(builder.module, envname)) # Emit loop loopcount = builder.load(arg_dims, name="loopcount") # Prepare inputs arrays = [] for i, typ in enumerate(signature.args): arrays.append(UArrayArg(context, builder, arg_args, arg_steps, i, typ)) # Prepare output out = UArrayArg(context, builder, arg_args, arg_steps, len(arrays), signature.return_type) # Setup indices offsets = [] zero = context.get_constant(types.intp, 0) for _ in arrays: p = cgutils.alloca_once(builder, intp_t) offsets.append(p) builder.store(zero, p) store_offset = cgutils.alloca_once(builder, intp_t) builder.store(zero, store_offset) unit_strided = cgutils.true_bit for ary in arrays: unit_strided = builder.and_(unit_strided, ary.is_unit_strided) pyapi = context.get_python_api(builder) if objmode: # General loop gil = pyapi.gil_ensure() with cgutils.for_range(builder, loopcount, intp=intp_t): build_obj_loop_body( context, func, builder, arrays, out, offsets, store_offset, signature, pyapi, envptr, env, ) pyapi.gil_release(gil) builder.ret_void() else: with builder.if_else(unit_strided) as (is_unit_strided, is_strided): with is_unit_strided: with cgutils.for_range(builder, loopcount, intp=intp_t) as loop: build_fast_loop_body( context, func, builder, arrays, out, offsets, store_offset, signature, loop.index, pyapi, env=envptr, ) with is_strided: # General loop with cgutils.for_range(builder, loopcount, intp=intp_t): build_slow_loop_body( context, func, builder, arrays, out, offsets, store_offset, signature, pyapi, env=envptr, ) builder.ret_void() del builder # Link and finalize wrapperlib.add_ir_module(wrapper_module) wrapperlib.add_linking_library(library) return _wrapper_info(library=wrapperlib, env=env, name=wrapper.name)