def compile(self, module: ir.Module, builder: ir.IRBuilder, symbols: SymbolTable) -> ir.Value: expr = self.expr if expr is None: builder.ret_void() else: builder.ret(self.expr.compile(module, builder, symbols))
def call_raw_function_pointer(func_ptr, function_type, args, builder: ir.IRBuilder): val = ir.Constant(ir.IntType(64), func_ptr) ptr = builder.inttoptr(val, ir.PointerType(function_type)) # Due to limitations in llvmlite ptr cannot be a constant, so do the cast as an instruction to make the call # argument an instruction. return builder.call(ptr, args)
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 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 compile(self, module: ir.Module, builder: ir.IRBuilder, symbols: SymbolTable) -> ir.Value: if len(self.elsif_exprs) == 0: if self.else_block: with builder.if_else(self.if_expr.compile( module, builder, symbols)) as (then, otherwise): with then: self.if_block.compile(module, builder, symbols) with otherwise: self.else_block.compile(module, builder, symbols) return else: with builder.if_then( self.if_expr.compile(module, builder, symbols)): self.if_block.compile(module, builder, symbols) return def build_elsif(elsifs): expr, block = elsifs with builder.if_else(expr.compile( module, builder, symbols)) as (then, otherwise): with then: block.compile(module, builder, symbols) with otherwise: left = len(elsifs) if left > 2: build_elsif(elsifs[1:]) if left == 2: expr, block = elsifs[1] if self.else_block: with builder.if_else(expr.compile( module, builder, symbols)) as (then, otherwise): with then: block.compile(module, builder, symbols) with otherwise: self.else_block.compile( module, builder, symbols) else: with builder.if_then(expr.compile( module, builder, symbols)): block.compile(module, builder, symbols) with builder.if_else(self.if_expr.compile( module, builder, symbols)) as (then, otherwise): with then: self.if_block.compile(module, builder, symbols) with otherwise: build_elsif([(self.elsif_exprs[i], self.elsif_blocks[i]) for i in range(len(self.elsif_exprs))])
def codegen(self, builder: IRBuilder, ctx: Context): ptr = self.children[0].codegen(builder, ctx) if ptr is None: ReturnError() # La funzione crea una copia, la registra ad un livello inferiore e la restituisce fun = ctx.get_function(fn.return_and_collect_function) new_ptr = builder.call(fun, [ptr]) return builder.ret(new_ptr)
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 get_bool(scope_stack, builder: ir.IRBuilder, value): value = get_value(scope_stack, builder, value) if isinstance(value, ir.Constant): return bool_type(1 if value.constant else 0) if isinstance(value.type, ir.DoubleType): return builder.fcmp_ordered('!=', value, value.type(0)) if isinstance(value.type, ir.IntType) and value.type.width != 1: return builder.icmp_signed('!=', value, value.type(0)) return value
def get_struct_ptr_pointer(scope_stack, builder: ir.IRBuilder, struct, item, index=int_type(0)): identifier = get_identifier(scope_stack, struct) assert identifier['type'] == 'struct_ptr' item = struct_map[identifier['val_type']][item] value = builder.load(identifier['value']) return builder.gep(value, [index, item['index']], True)
def compile(self, module: ll.Module, builder: ll.IRBuilder): pin_in = self.get_pin('in') pin_out = self.get_pin('out') pin_in = module.get_global(pin_in) pin_out = module.get_global(pin_out) v = builder.load(pin_in) v = builder.not_(v) builder.store(v, pin_out)
def codegen(self, builder: IRBuilder, ctx: Context): ptr = builder.alloca(ir.ArrayType(ir.IntType(8), len(self.value) + 1)) mapped = list( map(lambda x: ir.Constant(ir.IntType(8), ord(x)), list(self.value))) # La trasformo in un array di char mapped.append(ir.Constant(ir.IntType(8), 0)) # Aggiungo il terminatore value = ir.Constant.literal_array(mapped) builder.store(value, ptr) return builder.bitcast(ptr, ir.PointerType(ir.IntType(8)))
def opt_store(builder: ir.IRBuilder, value, ptr): assert isinstance(ptr.type, ir.PointerType) if isinstance(value, ir.Constant) and type(value.type) != type(ptr.type.pointee): value = ptr.type(get_type_rank(ptr.type.pointee)[1](value.constant)) else: proper_type = get_proper_type(value.type, ptr.type.pointee) if type(value.type) != type(proper_type[0]): value = convert(builder, value, proper_type[0]) builder.store(value, ptr)
def allocate(self, builder: ir.IRBuilder) -> None: """Use IRBuilder to allocate memory of 'size' and use self.alloc as the pointer, Need twice of space for complex data. Should be called at the graph level. """ if self.dtype in (DType.Int, DType.Float, DType.Double): self.alloc = builder.alloca(type_map_llvm[self.dtype], self.size, self.name + "-data") else: # Complx or DComplx self.alloc = builder.alloca(type_map_llvm[self.dtype], self.size * 2, self.name + "-data")
def memcpy(module: ir.Module, scope_stack: list, builder: ir.IRBuilder, dst, src, len, size): size >>= 3 dst = get_pointer(scope_stack, builder, dst) insert_memcpy(module, scope_stack) builder.call(memcpy_func, [ builder.bitcast(dst, char_ptr_type), src, int_type(len), int_type(size), bool_type(0) ])
def compile(self, module: ll.Module, builder: ll.IRBuilder): for cname in self._toposort(): cdesc = self.children[cname] cdesc.compile(module, builder) for pin1 in self._conns.get(cname, dict()): for desc2, pin2 in self._conns[cname][pin1]: p1 = self.get_desc(cname).get_pin(pin1) p2 = self.get_desc(desc2).get_pin(pin2) p1 = module.get_global(p1) p2 = module.get_global(p2) v = builder.load(p1) builder.store(v, p2)
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 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 memzero(module: ir.Module, scope_stack: list, builder: ir.IRBuilder, dst, len): len *= get_type_size(dst.type.pointee.element) len >>= 3 dst = get_pointer(scope_stack, builder, dst) insert_memset(module, scope_stack) builder.call(memset_func, [ builder.bitcast(dst, char_ptr_type), char_type(0), int_type(len), int_type(4), bool_type(0) ])
def options(b: ir.IRBuilder, t: ir.Type) -> ir.Value: new_block = b.function.append_basic_block("block") with b.goto_block(new_block): phi_node = b.phi(t) def ret(value): if not b.block.is_terminated: phi_node.add_incoming(value, b.block) b.branch(new_block) yield ret, phi_node b.position_at_end(new_block)
def _load_from_src(self, ind: Union[ir.Constant, ir.Instruction], builder: ir.IRBuilder) -> List[ir.Instruction]: zero = ir.Constant(type_map_llvm[self.dtype], 0) if not self.dtype in (DType.Complx, DType.DComplx): if self.dtype == DType.Int: return [builder.add(zero, self.const)] else: return [builder.fadd(zero, self.const)] else: return [ builder.fadd(zero, self.const_real), builder.fadd(zero, self.const_imag) ]
def get_array_pointer(scope_stack, builder: ir.IRBuilder, array, index): if isinstance(array, tuple) and array[0] == 'struct': item = get_struct_pointer(scope_stack, builder, array[1], array[2]) elif isinstance(array, tuple) and array[0] == 'struct_ptr': item = get_struct_ptr_pointer(scope_stack, builder, array[1], array[2]) else: identifier = None if isinstance(array, tuple): identifier = get_identifier(scope_stack, array[1]) item = get_pointer(scope_stack, builder, array) if identifier and identifier['type'] == 'val_ptr': item = builder.load(item) return builder.gep(item, [index], True) return builder.gep(item, [int_type(0), index], True)
def codegen(self, builder: IRBuilder, ctx: Context): fnTy = ir.FunctionType(layout.ObjectPtrType(), [layout.ObjectPtrType() for arg in self.args]) fun = ir.Function(ctx.module, fnTy, name=self.name) ctx.parameters = dict(zip(self.args, fun.args)) ctx.start_function(fun) top = fun.append_basic_block() builder.position_at_end(top) ctx.label = top for child in self.children: child.codegen(builder, ctx) ctx.end_function()
def compile(self, module: ll.Module, builder: ll.IRBuilder): inputs = [] for i in range(self.num_inputs): path = self.get_pin(f'in{i}') inputs.append(module.get_global(path)) output = module.get_global(self.get_pin('out')) output_v = ll.Constant(INT_TYPE, 0) if self.kind == 'and': output_v = builder.not_(output_v) for inp in inputs: v = builder.load(inp) if self.kind == 'and': output_v = builder.and_(output_v, v) elif self.kind == 'or': output_v = builder.or_(output_v, v) elif self.kind == 'xor': output_v = builder.xor(output_v, v) if self.negated: output_v = builder.not_(output_v) builder.store(output_v, output)
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 _code_gen(self, builder: ir.IRBuilder) -> None: mod = builder.block.module # instr = ir.values.Function(mod, self.ftype, self.func_name) input_type = [] for in_node in self.SRC: input_type.append(type_map_llvm[in_node.dtype]) instr = mod.declare_intrinsic(self.func_name, input_type) params = [] with LoopCtx(self.name, builder, self.size) as loop: index = builder.load(loop.inc) data_ptr = builder.gep(self.alloc, [index]) for n in self.SRC: params.append(n.get_ele(index, builder)[0]) res = builder.call(instr, params) builder.store(res, data_ptr)
def _load_from_src(self, ind: Union[ir.Constant, ir.Instruction], builder: ir.IRBuilder) -> List[ir.Instruction]: if isinstance(self.ind, (ir.Constant, ir.Instruction)): src_index = self.ind if isinstance(self.ind, slice): start, _, step = self.ind.indices(self.src.size) muled = builder.mul(ind, ir.Constant(int_type, step)) src_index = builder.add(muled, ir.Constant(int_type, start)) elif isinstance(self.ind, Node): src_index = self.ind.get_ele(ind, builder)[0] else: src_index_ptr = builder.gep(self.src_inds, [ind]) src_index = builder.load(src_index_ptr) return self.src.get_ele(src_index, builder)
def convert(builder: ir.IRBuilder, value, type): if isinstance(value, ir.Constant): return type(get_type_rank(type)[1](value.constant)) elif isinstance(type, ir.IntType) and isinstance(value.type, ir.IntType): if type.width > value.type.width: return builder.zext(value, type) elif type.width < value.type.width: return builder.trunc(value, type) elif isinstance(type, ir.DoubleType) and isinstance( value.type, ir.IntType): return builder.sitofp(value, type) elif isinstance(type, ir.IntType) and isinstance(value.type, ir.DoubleType): return builder.fptosi(value, type) return value
def __compile_variable(self, builder: ir.IRBuilder, c: Union[SetVariable, Variable]): if isinstance(c, SetVariable): value = self.__compile_value(builder, c.value) if c.name not in self.__variables: self.__variables[c.name] = builder.alloca(value.type) align = value.type.get_abi_alignment(self.__target_data) # print(f"Alignment: {align}") ptr = self.__variables[c.name] if not c.deref else builder.load( self.__variables[c.name]) builder.store(value, ptr) elif isinstance(c, Variable): p = self.__variables[c.name] value = builder.load(p) if p.type.is_pointer else p return value
class LLVMGenerator: 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 emit(self, code): for op, *opargs in code: getattr(self, f'emit_{op}')(*opargs) self.builder.ret(Constant(int_type, 0)) return str(self.module) def push(self, item): self.stack.append(item) def pop(self): return self.stack.pop() def emit_VARI(self, name): self.vars[name] = self.builder.alloca(int_type, name=name) def emit_CONSTI(self, value): self.push(Constant(int_type, value)) def emit_STORE(self, name): self.builder.store(self.pop(), self.vars[name]) def emit_LOAD(self, name): self.push(self.builder.load(self.vars[name])) def emit_ADDI(self): self.push(self.builder.add(self.pop(), self.pop())) def emit_MULI(self): self.push(self.builder.mul(self.pop(), self.pop())) def emit_PRINTI(self): self.builder.call(self._print_int, [self.pop()])
def start_function(self, name, rettypename, parmtypenames): rettype = typemap[rettypename] parmtypes = [typemap[pname] for pname in parmtypenames] # Type.function(rettype, parmtypes, False) func_type = FunctionType(rettype, parmtypes) # Create the function for which we're generating code # Function.new(self.module, func_type, name) self.function = Function(self.module, func_type, name=name) # Make the builder and entry block self.block = self.function.append_basic_block("entry") self.builder = IRBuilder(self.block) # Make the exit block self.exit_block = self.function.append_basic_block("exit") # Clear the local vars and temps self.locals = {} self.temps = {} # Make the return variable if rettype is not void_type: self.locals['return'] = self.builder.alloca(rettype, name="return") # Put an entry in the globals self.globals[name] = self.function
class GenerateLLVM(object): def __init__(self, name='module'): # 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(name) # self.function = Function(self.module, # FunctionType(void_type, []), # name='main') # self.block = self.function.append_basic_block('entry') self.block = None # self.builder = IRBuilder(self.block) self.builder = None # 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 = {} self.globals = {} self.locals = {} # Dictionary that holds all of the temporary variables created in # the intermediate code. For example, if you had an expression # like this: # # a = b + c*d # # The corresponding intermediate code might look like this: # # ('load_int', 'b', 'int_1') # ('load_int', 'c', 'int_2') # ('load_int', 'd', 'int_3') # ('mul_int', 'int_2','int_3','int_4') # ('add_int', 'int_1','int_4','int_5') # ('store_int', 'int_5', 'a') # # The self.temp dictionary below is used to map names such as 'int_1', # 'int_2' to their corresponding LLVM values. Essentially, every time # you make anything in LLVM, it gets stored here. self.temps = {} # Initialize the runtime library functions (see below) self.declare_runtime_library() self.last_branch = None def start_function(self, name, rettypename, parmtypenames): rettype = typemap[rettypename] parmtypes = [typemap[pname] for pname in parmtypenames] # Type.function(rettype, parmtypes, False) func_type = FunctionType(rettype, parmtypes) # Create the function for which we're generating code # Function.new(self.module, func_type, name) self.function = Function(self.module, func_type, name=name) # Make the builder and entry block self.block = self.function.append_basic_block("entry") self.builder = IRBuilder(self.block) # Make the exit block self.exit_block = self.function.append_basic_block("exit") # Clear the local vars and temps self.locals = {} self.temps = {} # Make the return variable if rettype is not void_type: self.locals['return'] = self.builder.alloca(rettype, name="return") # Put an entry in the globals self.globals[name] = self.function def new_basic_block(self, name=''): self.builder = IRBuilder(self.block.instructions) return self.function.append_basic_block(name) def declare_runtime_library(self): # Certain functions such as I/O and string handling are often easier # to implement in an external C library. This method should make # the LLVM declarations for any runtime functions to be used # during code generation. Please note that runtime function # functions are implemented in C in a separate file gonert.c self.runtime = {} # Declare printing functions self.runtime['_print_int'] = Function(self.module, FunctionType( void_type, [int_type]), name="_print_int") self.runtime['_print_float'] = Function(self.module, FunctionType( void_type, [float_type]), name="_print_float") self.runtime['_print_bool'] = Function(self.module, FunctionType( void_type, [int_type]), name="_print_bool") def generate_code(self, ircode): # 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) for opcode, *args in ircode: if hasattr(self, 'emit_' + opcode): getattr(self, 'emit_' + opcode)(*args) else: print('Warning: No emit_' + opcode + '() method') # Add a return statement. Note, at this point, we don't really have # user-defined functions so this is a bit of hack--it may be removed # later. # self.builder.ret_void() def terminate(self): # Add a return statement. This connects the last block to the exit # block. # The return statement is then emitted if self.last_branch != self.block: self.builder.branch(self.exit_block) self.builder.position_at_end(self.exit_block) if 'return' in self.locals: self.builder.ret(self.builder.load(self.locals['return'])) else: self.builder.ret_void() def add_block(self, name): # Add a new block to the existing function return self.function.append_basic_block(name) def set_block(self, block): # Sets the current block for adding more code self.block = block self.builder.position_at_end(block) def cbranch(self, testvar, true_block, false_block): self.builder.cbranch(self.temps[testvar], true_block, false_block) def branch(self, next_block): if self.last_branch != self.block: self.builder.branch(next_block) self.last_branch = self.block # ---------------------------------------------------------------------- # Opcode implementation. You must implement the opcodes. A few # sample opcodes have been given to get you started. # ---------------------------------------------------------------------- # Creation of literal values. Simply define as LLVM constants. def emit_literal_int(self, value, target): self.temps[target] = Constant(int_type, value) def emit_literal_float(self, value, target): self.temps[target] = Constant(float_type, value) def emit_literal_bool(self, value, target): self.temps[target] = Constant(bool_type, value) # def emit_literal_string(self, value, target): # self.temps[target] = Constant(string_type, value) # Allocation of variables. Declare as global variables and set to # a sensible initial value. def emit_alloc_int(self, name): var = self.builder.alloca(int_type, name=name) var.initializer = Constant(int_type, 0) self.locals[name] = var def emit_alloc_float(self, name): var = self.builder.alloca(float_type, name=name) var.initializer = Constant(float_type, 0) self.locals[name] = var def emit_alloc_bool(self, name): var = self.builder.alloca(bool_type, name=name) var.initializer = Constant(bool_type, 0) self.locals[name] = var def emit_global_int(self, name): var = GlobalVariable(self.module, int_type, name=name) var.initializer = Constant(int_type, 0) self.globals[name] = var def emit_global_float(self, name): var = GlobalVariable(self.module, float_type, name=name) var.initializer = Constant(float_type, 0) self.globals[name] = var def emit_global_bool(self, name): var = GlobalVariable(self.module, bool_type, name=name) var.initializer = Constant(bool_type, 0) self.globals[name] = var # def emit_alloc_string(self, name): # var = GlobalVariable(self.module, string_type, name=name) # var.initializer = Constant(string_type, "") # self.vars[name] = var # Load/store instructions for variables. Load needs to pull a # value from a global variable and store in a temporary. Store # goes in the opposite direction. def lookup_var(self, name): if name in self.locals: return self.locals[name] else: return self.globals[name] def emit_load_int(self, name, target): # print('LOADINT %s, %s' % (name, target)) # print('GLOBALS %s' % self.globals) # print('LOCALS %s' % self.locals) self.temps[target] = self.builder.load(self.lookup_var(name), target) def emit_load_float(self, name, target): self.temps[target] = self.builder.load(self.lookup_var(name), target) def emit_load_bool(self, name, target): self.temps[target] = self.builder.load(self.lookup_var(name), target) def emit_store_int(self, source, target): self.builder.store(self.temps[source], self.lookup_var(target)) def emit_store_float(self, source, target): self.builder.store(self.temps[source], self.lookup_var(target)) def emit_store_bool(self, source, target): self.builder.store(self.temps[source], self.lookup_var(target)) # Binary + operator def emit_add_int(self, left, right, target): self.temps[target] = self.builder.add( self.temps[left], self.temps[right], target) def emit_add_float(self, left, right, target): self.temps[target] = self.builder.fadd( self.temps[left], self.temps[right], target) # Binary - operator def emit_sub_int(self, left, right, target): self.temps[target] = self.builder.sub( self.temps[left], self.temps[right], target) def emit_sub_float(self, left, right, target): self.temps[target] = self.builder.fsub( self.temps[left], self.temps[right], target) # Binary * operator def emit_mul_int(self, left, right, target): self.temps[target] = self.builder.mul( self.temps[left], self.temps[right], target) def emit_mul_float(self, left, right, target): self.temps[target] = self.builder.fmul( self.temps[left], self.temps[right], target) # Binary / operator def emit_div_int(self, left, right, target): self.temps[target] = self.builder.sdiv( self.temps[left], self.temps[right], target) def emit_div_float(self, left, right, target): self.temps[target] = self.builder.fdiv( self.temps[left], self.temps[right], target) # Unary + operator def emit_uadd_int(self, source, target): self.temps[target] = self.builder.add( Constant(int_type, 0), self.temps[source], target) def emit_uadd_float(self, source, target): self.temps[target] = self.builder.fadd( Constant(float_type, 0.0), self.temps[source], target) # Unary - operator def emit_usub_int(self, source, target): self.temps[target] = self.builder.sub( Constant(int_type, 0), self.temps[source], target) def emit_usub_float(self, source, target): self.temps[target] = self.builder.fsub( Constant(float_type, 0.0), self.temps[source], target) # Binary < operator def emit_lt_int(self, left, right, target): self.temps[target] = self.builder.icmp_signed( '<', self.temps[left], self.temps[right], target) def emit_lt_float(self, left, right, target): self.temps[target] = self.builder.fcmp_ordered( '<', self.temps[left], self.temps[right], target) # Binary <= operator def emit_le_int(self, left, right, target): self.temps[target] = self.builder.icmp_signed( '<=', self.temps[left], self.temps[right], target) def emit_le_float(self, left, right, target): self.temps[target] = self.builder.fcmp_ordered( '<=', self.temps[left], self.temps[right], target) # Binary > operator def emit_gt_int(self, left, right, target): self.temps[target] = self.builder.icmp_signed( '>', self.temps[left], self.temps[right], target) def emit_gt_float(self, left, right, target): self.temps[target] = self.builder.fcmp_ordered( '>', self.temps[left], self.temps[right], target) # Binary >= operator def emit_ge_int(self, left, right, target): self.temps[target] = self.builder.icmp_signed( '>=', self.temps[left], self.temps[right], target) def emit_ge_float(self, left, right, target): self.temps[target] = self.builder.fcmp_ordered( '>=', self.temps[left], self.temps[right], target) # Binary == operator def emit_eq_int(self, left, right, target): self.temps[target] = self.builder.icmp_signed( '==', self.temps[left], self.temps[right], target) def emit_eq_bool(self, left, right, target): self.temps[target] = self.builder.icmp_signed( '==', self.temps[left], self.temps[right], target) def emit_eq_float(self, left, right, target): self.temps[target] = self.builder.fcmp_ordered( '==', self.temps[left], self.temps[right], target) # Binary != operator def emit_ne_int(self, left, right, target): self.temps[target] = self.builder.icmp_signed( '!=', self.temps[left], self.temps[right], target) def emit_ne_bool(self, left, right, target): self.temps[target] = self.builder.icmp_signed( '!=', self.temps[left], self.temps[right], target) def emit_ne_float(self, left, right, target): self.temps[target] = self.builder.fcmp_ordered( '!=', self.temps[left], self.temps[right], target) # Binary && operator def emit_and_bool(self, left, right, target): self.temps[target] = self.builder.and_( self.temps[left], self.temps[right], target) # Binary || operator def emit_or_bool(self, left, right, target): self.temps[target] = self.builder.or_( self.temps[left], self.temps[right], target) # Unary ! operator def emit_not_bool(self, source, target): self.temps[target] = self.builder.icmp_signed( '==', self.temps[source], Constant(bool_type, 0), target) # Print statements def emit_print_int(self, source): self.builder.call(self.runtime['_print_int'], [self.temps[source]]) def emit_print_float(self, source): self.builder.call(self.runtime['_print_float'], [self.temps[source]]) def emit_print_bool(self, source): self.builder.call(self.runtime['_print_bool'], [ self.builder.zext(self.temps[source], int_type)]) # Extern function declaration. def emit_extern_func(self, name, rettypename, *parmtypenames): rettype = typemap[rettypename] parmtypes = [typemap[pname] for pname in parmtypenames] func_type = FunctionType(rettype, parmtypes) self.globals[name] = Function(self.module, func_type, name=name) # Call an external function. def emit_call_func(self, funcname, *args): target = args[-1] func = self.globals[funcname] argvals = [self.temps[name] for name in args[:-1]] self.temps[target] = self.builder.call(func, argvals) # Function parameter declarations. Must create as local variables def emit_parm_int(self, name, num): var = self.builder.alloca(int_type, name=name) self.builder.store(self.function.args[num], var) self.locals[name] = var def emit_parm_float(self, name, num): var = self.builder.alloca(float_type, name=name) self.builder.store(self.function.args[num], var) self.locals[name] = var def emit_parm_bool(self, name, num): var = self.builder.alloca(bool_type, name=name) self.builder.store(self.function.args[num], var) self.locals[name] = var # Return statements def emit_return_int(self, source): self.builder.store(self.temps[source], self.locals['return']) self.branch(self.exit_block) def emit_return_float(self, source): self.builder.store(self.temps[source], self.locals['return']) self.branch(self.exit_block) def emit_return_bool(self, source): self.builder.store(self.temps[source], self.locals['return']) self.branch(self.exit_block) def emit_return_void(self): self.branch(self.exit_block)
def new_basic_block(self, name=''): self.builder = IRBuilder(self.block.instructions) return self.function.append_basic_block(name)