Example #1
0
 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))
Example #2
0
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)
Example #3
0
    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()
Example #4
0
    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()
Example #5
0
    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))])
Example #6
0
 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)
Example #7
0
    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
Example #8
0
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
Example #9
0
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)
Example #10
0
    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)
Example #11
0
    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)))
Example #12
0
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)
Example #13
0
 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")
Example #14
0
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)
    ])
Example #15
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)
Example #16
0
    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'))
Example #17
0
    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
Example #18
0
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)
    ])
Example #19
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)
Example #20
0
 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)
         ]
Example #21
0
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)
Example #22
0
    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()
Example #23
0
    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)
Example #24
0
    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)
Example #25
0
    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 = {}
Example #26
0
 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)
Example #27
0
    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)
Example #28
0
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
Example #29
0
 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
Example #30
0
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()])
Example #31
0
    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
Example #32
0
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)
Example #33
0
 def new_basic_block(self, name=''):
     self.builder = IRBuilder(self.block.instructions)
     return self.function.append_basic_block(name)