Exemplo n.º 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))
Exemplo n.º 2
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()])
Exemplo n.º 3
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)
Exemplo n.º 4
0
class GenerateLLVM(object):
    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')

        # All globals variables and function definitions go here
        self.globals = {}

        self.blocks = {}

        # Initialize the runtime library functions (see below)
        self.declare_runtime_library()

    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_byte'] = Function(self.module,
                                               FunctionType(
                                                   void_type, [byte_type]),
                                               name="_print_byte")

    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 get_block(self, block_name):
        block = self.blocks.get(block_name)
        if block is None:
            block = self.function.append_basic_block(block_name)
            self.blocks[block_name] = block

        return 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_MOV(self, value, target, val_type):
        self.temps[target] = Constant(val_type, value)

    emit_MOVI = partialmethod(emit_MOV, val_type=int_type)
    emit_MOVF = partialmethod(emit_MOV, val_type=float_type)
    emit_MOVB = partialmethod(emit_MOV, val_type=byte_type)

    # Allocation of GLOBAL variables.  Declare as global variables and set to
    # a sensible initial value.
    def emit_VAR(self, name, var_type):
        var = GlobalVariable(self.module, var_type, name=name)
        var.initializer = Constant(var_type, 0)
        self.globals[name] = var

    emit_VARI = partialmethod(emit_VAR, var_type=int_type)
    emit_VARF = partialmethod(emit_VAR, var_type=float_type)
    emit_VARB = partialmethod(emit_VAR, var_type=byte_type)

    # Allocation of LOCAL variables.  Declare as local variables and set to
    # a sensible initial value.
    def emit_ALLOC(self, name, var_type):
        self.locals[name] = self.builder.alloca(var_type, name=name)

    emit_ALLOCI = partialmethod(emit_ALLOC, var_type=int_type)
    emit_ALLOCF = partialmethod(emit_ALLOC, var_type=float_type)
    emit_ALLOCB = partialmethod(emit_ALLOC, var_type=byte_type)

    # 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 emit_LOADI(self, name, target):
        self.temps[target] = self.builder.load(self.vars[name], name=target)

    emit_LOADF = emit_LOADI
    emit_LOADB = emit_LOADI

    def emit_STOREI(self, source, target):
        self.builder.store(self.temps[source], self.vars[target])

    emit_STOREF = emit_STOREI
    emit_STOREB = emit_STOREI

    # Binary + operator
    def emit_ADDI(self, left, right, target):
        self.temps[target] = self.builder.add(self.temps[left],
                                              self.temps[right],
                                              name=target)

    def emit_ADDF(self, left, right, target):
        self.temps[target] = self.builder.fadd(self.temps[left],
                                               self.temps[right],
                                               name=target)

    # Binary - operator
    def emit_SUBI(self, left, right, target):
        self.temps[target] = self.builder.sub(self.temps[left],
                                              self.temps[right],
                                              name=target)

    def emit_SUBF(self, left, right, target):
        self.temps[target] = self.builder.fsub(self.temps[left],
                                               self.temps[right],
                                               name=target)

    # Binary * operator
    def emit_MULI(self, left, right, target):
        self.temps[target] = self.builder.mul(self.temps[left],
                                              self.temps[right],
                                              name=target)

    def emit_MULF(self, left, right, target):
        self.temps[target] = self.builder.fmul(self.temps[left],
                                               self.temps[right],
                                               name=target)

    # Binary / operator
    def emit_DIVI(self, left, right, target):
        self.temps[target] = self.builder.sdiv(self.temps[left],
                                               self.temps[right],
                                               name=target)

    def emit_DIVF(self, left, right, target):
        self.temps[target] = self.builder.fdiv(self.temps[left],
                                               self.temps[right],
                                               name=target)

    # Print statements
    def emit_PRINT(self, source, runtime_name):
        self.builder.call(self.runtime[runtime_name], [self.temps[source]])

    emit_PRINTI = partialmethod(emit_PRINT, runtime_name="_print_int")
    emit_PRINTF = partialmethod(emit_PRINT, runtime_name="_print_float")
    emit_PRINTB = partialmethod(emit_PRINT, runtime_name="_print_byte")

    def emit_CMPI(self, operator, left, right, target):
        tmp = self.builder.icmp_signed(operator, self.temps[left],
                                       self.temps[right], 'tmp')
        # LLVM compares produce a 1-bit integer as a result.  Since our IRcode using integers
        # for bools, need to sign-extend the result up to the normal int_type to continue
        # with further processing (otherwise you'll get a LLVM type error).
        self.temps[target] = self.builder.zext(tmp, int_type, target)

    def emit_CMPF(self, operator, left, right, target):
        tmp = self.builder.fcmp_ordered(operator, self.temps[left],
                                        self.temps[right], 'tmp')
        self.temps[target] = self.builder.zext(tmp, int_type, target)

    emit_CMPB = emit_CMPI

    # Logical ops
    def emit_AND(self, left, right, target):
        self.temps[target] = self.builder.and_(self.temps[left],
                                               self.temps[right], target)

    def emit_OR(self, left, right, target):
        self.temps[target] = self.builder.or_(self.temps[left],
                                              self.temps[right], target)

    def emit_XOR(self, left, right, target):
        self.temps[target] = self.builder.xor(self.temps[left],
                                              self.temps[right], target)

    def emit_LABEL(self, lbl_name):
        self.block = self.get_block(lbl_name)
        self.builder.position_at_end(self.block)

    def emit_BRANCH(self, dst_label):
        if not self.block.is_terminated:
            self.builder.branch(self.get_block(dst_label))

    def emit_CBRANCH(self, test_target, true_label, false_label):
        true_block = self.get_block(true_label)
        false_block = self.get_block(false_label)
        testvar = self.temps[test_target]
        self.builder.cbranch(self.builder.trunc(testvar, IntType(1)),
                             true_block, false_block)

    def emit_RET(self, register):
        self.builder.store(self.temps[register], self.vars['return'])
        self.builder.branch(self.return_block)

    def emit_CALL(self, func_name, *registers):
        # print(self.globals)
        args = [self.temps[r] for r in registers[:-1]]
        target = registers[-1]
        self.temps[target] = self.builder.call(self.globals[func_name], args)
Exemplo n.º 5
0
class GenerateLLVM(object):
    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.globals = {}

        # Initialize the runtime library functions (see below)
        self.declare_runtime_library()

    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 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_byte'] = Function(self.module,
                                               FunctionType(
                                                   void_type, [byte_type]),
                                               name="_print_byte")

    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)

        # Collect and create all basic blocks
        labels = [instr[1] for instr in ircode if instr[0] == 'LABEL']
        self.basicblocks = {
            name: self.function.append_basic_block(name)
            for name in labels
        }

        for opcode, *args in ircode:
            if hasattr(self, 'emit_' + opcode):
                getattr(self, 'emit_' + opcode)(*args)
            else:
                print('Warning: No emit_' + opcode + '() method')

    # ----------------------------------------------------------------------
    # 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_MOVI(self, value, target):
        self.temps[target] = Constant(int_type, value)

    def emit_MOVF(self, value, target):
        self.temps[target] = Constant(float_type, value)

        pass  # You must implement

    def emit_MOVB(self, value, target):
        self.temps[target] = Constant(byte_type, value)

    # Allocation of variables.  Declare as global variables and set to
    # a sensible initial value.
    def emit_VARI(self, name):
        var = GlobalVariable(self.module, int_type, name=name)
        var.initializer = Constant(int_type, 0)
        self.globals[name] = var

    def emit_VARF(self, name):
        var = GlobalVariable(self.module, float_type, name=name)
        var.initializer = Constant(float_type, 0.0)
        self.globals[name] = var

    def emit_VARB(self, name):
        var = GlobalVariable(self.module, byte_type, name=name)
        var.initializer = Constant(byte_type, 0)
        self.globals[name] = var

    def emit_ALLOCI(self, name):
        self.locals[name] = self.builder.alloca(int_type, name=name)

    def emit_ALLOCF(self, name):
        self.locals[name] = self.builder.alloca(float_type, name=name)

    def emit_ALLOCB(self, name):
        self.locals[name] = self.builder.alloca(byte_type, name=name)

    # 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 emit_LOADI(self, name, target):
        self.temps[target] = self.builder.load(self.vars[name], target)

    def emit_LOADF(self, name, target):
        self.temps[target] = self.builder.load(self.vars[name], target)

    def emit_LOADB(self, name, target):
        self.temps[target] = self.builder.load(self.vars[name], target)
        pass  # You must implement

    def emit_STOREI(self, source, target):
        self.builder.store(self.temps[source], self.vars[target])

    def emit_STOREF(self, source, target):
        self.builder.store(self.temps[source], self.vars[target])

    def emit_STOREB(self, source, target):
        self.builder.store(self.temps[source], self.vars[target])

    # Binary + operator
    def emit_ADDI(self, left, right, target):
        self.temps[target] = self.builder.add(self.temps[left],
                                              self.temps[right], target)

    def emit_ADDF(self, left, right, target):
        self.temps[target] = self.builder.fadd(self.temps[left],
                                               self.temps[right], target)
        pass  # You must implement

    # Binary - operator
    def emit_SUBI(self, left, right, target):
        self.temps[target] = self.builder.sub(self.temps[left],
                                              self.temps[right], target)
        pass  # You must implement

    def emit_SUBF(self, left, right, target):
        self.temps[target] = self.builder.fsub(self.temps[left],
                                               self.temps[right], target)
        pass  # You must implement

    # Binary * operator
    def emit_MULI(self, left, right, target):
        self.temps[target] = self.builder.mul(self.temps[left],
                                              self.temps[right], target)
        pass  # You must implement

    def emit_MULF(self, left, right, target):
        self.temps[target] = self.builder.fmul(self.temps[left],
                                               self.temps[right], target)
        pass  # You must implement

    # Binary / operator
    def emit_DIVI(self, left, right, target):
        self.temps[target] = self.builder.sdiv(self.temps[left],
                                               self.temps[right], target)
        pass  # You must implement

    def emit_DIVF(self, left, right, target):
        self.temps[target] = self.builder.fdiv(self.temps[left],
                                               self.temps[right], target)
        pass  # You must implement

    def emit_CMPI(self, op, left, right, target):
        result = self.builder.icmp_signed(op, self.temps[left],
                                          self.temps[right], '_temp')
        self.temps[target] = self.builder.zext(result, int_type, target)

    def emit_CMPF(self, op, left, right, target):
        result = self.builder.fcmp_ordered(op, self.temps[left],
                                           self.temps[right], '_temp')
        self.temps[target] = self.builder.zext(result, int_type, target)

    def emit_CMPB(self, op, left, right, target):
        result = self.builder.icmp_signed(op, self.temps[left],
                                          self.temps[right], '_temp')
        self.temps[target] = self.builder.zext(result, int_type, target)

    def emit_AND(self, left, right, target):
        self.temps[target] = self.builder.and_(self.temps[left],
                                               self.temps[right], target)

    def emit_OR(self, left, right, target):
        self.temps[target] = self.builder.or_(self.temps[left],
                                              self.temps[right], target)

    def emit_XOR(self, left, right, target):
        self.temps[target] = self.builder.xor(self.temps[left],
                                              self.temps[right], target)

    # Print statements
    def emit_PRINTI(self, source):
        self.builder.call(self.runtime['_print_int'], [self.temps[source]])

    def emit_PRINTF(self, source):
        self.builder.call(self.runtime['_print_float'], [self.temps[source]])
        pass  # You must implement

    def emit_PRINTB(self, source):
        self.builder.call(self.runtime['_print_byte'], [self.temps[source]])
        pass  # You must implement

    def emit_LABEL(self, name):
        self.block = self.basicblocks[name]
        self.builder.position_at_end(self.block)

    def emit_CBRANCH(self, test, true_label, false_label):
        true_block = self.basicblocks[true_label]
        false_block = self.basicblocks[false_label]
        self.builder.cbranch(self.builder.trunc(self.temps[test], IntType(1)),
                             true_block, false_block)

    def emit_BRANCH(self, next_label):
        target = self.basicblocks[next_label]
        if not self.block.is_terminated:
            self.builder.branch(target)

    def emit_CALL(self, name, *extra):
        *args, target = extra
        args = [self.temps[a] for a in args]
        func = self.vars[name]
        self.temps[target] = self.builder.call(func, args)

    def emit_RET(self, source):
        self.builder.store(self.temps[source], self.return_var)
        self.builder.branch(self.return_block)
Exemplo n.º 6
0
class GeneratorVisitor(SmallCVisitor):
    def __init__(self, output_file=None):
        super(SmallCVisitor, self).__init__()
        self.Module = Module(name=__file__)
        self.Module.triple = "x86_64-pc-linux-gnu"
        self.Builder = None
        self.function = None
        self.NamedValues = dict()
        self.counter = 0
        self.loop_stack = []
        self.signal_stack = []
        self.cond_stack = []
        self.var_stack = []
        self.cur_decl_type = None

        self.indentation = 0
        self.function_dict = dict()
        self.error_queue = list()
        self.output_file = output_file

    def print(self):
        if not self.output_file:
            print(self.Module)
        else:
            f = open(self.output_file, "w+")
            f.write(self.Module.__str__())
            f.close()

    def error(self, info):
        print("Error: ", info)
        return 0

    def toBool(self, value):
        zero = Constant(self.getType('bool'), 0)
        return self.Builder.icmp_signed('!=', value, zero)

    def getVal_of_expr(self, expr):
        temp = self.visit(expr)
        if isinstance(temp, Constant) or isinstance(
                temp, CallInstr) or isinstance(temp, LoadInstr) or isinstance(
                    temp, Instruction) or isinstance(temp, GlobalVariable):
            value = temp
        else:
            temp_val = self.getVal_local(temp.IDENTIFIER().getText())
            temp_ptr = temp_val['ptr']
            if temp.array_indexing():
                index = self.getVal_of_expr(temp.array_indexing().expr())
                temp_ptr = self.Builder.gep(temp_ptr,
                                            [Constant(IntType(32), 0), index],
                                            inbounds=True)
            # if isinstance(temp_val['type'],ArrayType):
            #     if temp.array_indexing():
            #         index = self.getVal_of_expr(temp.array_indexing().expr())
            #         temp_ptr = self.Builder.gep(temp_ptr, [Constant(IntType(32), 0), index], inbounds=True)
            #     elif temp.AMPERSAND():
            #         Constant(PointerType(IntType(8)), temp_ptr.getText())
            #     elif temp.ASTERIKS():
            #         pass
            #     else: #返回数组地址
            #         temp_ptr = self.Builder.gep(temp_ptr, [Constant(IntType(32), 0), Constant(IntType(32), 0)], inbounds=True)
            #         return temp_ptr
            value = self.Builder.load(temp_ptr)
        return value

    def getType(self, type):
        if type == 'int':
            return IntType(32)
        elif type == 'char':
            return IntType(8)
        elif type == 'float':
            return FloatType()
        elif type == 'bool':
            return IntType(1)
        elif type == 'void':
            return VoidType()
        else:
            self.error("type error in <getType>")

    def getVal_local(self, id):
        temp_maps = self.var_stack[::-1]
        for map in temp_maps:
            if id in map.keys():
                return map[id]
        self.error("value error in <getVal_local>")
        return None

    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 visitFunctioncall(self, ctx: SmallCParser.FunctioncallContext):
        var_map = self.var_stack[-1]
        function = self.function_dict[ctx.identifier().getText()]
        arg_types = function.args
        index = 0

        args = []
        if ctx.param_list():
            for param in ctx.param_list().getChildren():
                if (param.getText() == ','):
                    continue

                temp = self.getVal_of_expr(param)

                arg_type = None
                if index < len(arg_types):
                    arg_type = arg_types[index]
                ptr_flag = False
                if arg_type:
                    if isinstance(arg_type.type, PointerType):
                        ptr_flag = True
                elif self.getVal_local(temp.name):
                    temp_type = self.getVal_local(temp.name)['type']
                    if isinstance(temp_type, PointerType):
                        ptr_flag = True
                if not ptr_flag and not isinstance(temp, Constant):
                    temp = self.Builder.load(temp)
                args.append(temp)

                index += 1

        return self.Builder.call(function, args)

    # def visitVar_decl(self, ctx: SmallCParser.Var_declContext):
    #     type = self.getType(ctx.type_specifier())
    #     list = ctx.var_decl_list()
    #     for var in list.getChildren():
    #         if var.getText() != ',':
    #             if self.builder:
    #                 alloca = self.builder.alloca(type, name=var.identifier().getText())
    #                 self.builder.store(Constant(type, None), alloca)
    #                 self.var_stack[-1][var.identifier().getText()] = alloca
    #             else:
    #                 g_var = GlobalVariable(self.Module, type, var.identifier().getText())
    #                 g_var.initializer = Constant(type, None)
    #     return

    def visitStmt(self, ctx: SmallCParser.StmtContext):
        if ctx.RETURN():
            value = self.getVal_of_expr(ctx.expr())
            if isinstance(value.type, PointerType):
                value = self.Builder.load(value)
            return self.Builder.ret(value)
        elif ctx.CONTINUE():
            self.signal_stack[-1] = 1
            loop_blocks = self.loop_stack[-1]
            self.Builder.branch(loop_blocks['continue'])
            self.Builder.position_at_start(loop_blocks['buf'])
            return None
        elif ctx.BREAK():
            self.signal_stack[-1] = -1
            loop_blocks = self.loop_stack[-1]
            self.Builder.branch(loop_blocks['break'])
            self.Builder.position_at_start(loop_blocks['buf'])
        else:
            return self.visitChildren(ctx)

    def visitCompound_stmt(self, ctx: SmallCParser.Compound_stmtContext):
        # builder = IRBuilder(self.block_stack[-1])
        # block = self.Builder.append_basic_block()
        # self.block_stack.append(block)
        # with self.Builder.goto_block(block):
        result = self.visitChildren(ctx)
        # self.block_stack.pop()
        return result

    def visitAssignment(self, ctx: SmallCParser.AssignmentContext):
        value = self.getVal_of_expr(ctx.expr())
        identifier = ctx.identifier()
        identifier = self.getVal_local(identifier.IDENTIFIER().getText())
        if isinstance(identifier['type'], ArrayType):
            if ctx.identifier().array_indexing():
                index = self.getVal_of_expr(
                    ctx.identifier().array_indexing().expr())
                if isinstance(index.type, PointerType):
                    index = self.Builder.load(index)
            else:
                index = Constant(IntType(32), 0)
            tempPtr = self.Builder.gep(identifier['ptr'],
                                       [Constant(IntType(32), 0), index],
                                       inbounds=True)
            if isinstance(value.type, PointerType):
                value = self.Builder.load(value)
            return self.Builder.store(value, tempPtr)
        if isinstance(value.type, PointerType):
            value = self.Builder.load(value)
        return self.Builder.store(value, identifier['ptr'])

    def visitExpr(self, ctx: SmallCParser.ExprContext):
        if ctx.condition():
            return self.visit(ctx.condition())
        elif ctx.assignment():
            return self.visit(ctx.assignment())
        elif ctx.functioncall():
            return self.visit(ctx.functioncall())

    def visitCondition(self, ctx: SmallCParser.ConditionContext):
        if ctx.expr():
            disjunction = self.getVal_of_expr(ctx.disjunction())
            if isinstance(disjunction.type, PointerType):
                disjunction = self.Builder.load(disjunction)
            cond = self.Builder.icmp_signed('!=', disjunction,
                                            Constant(disjunction.type, 0))
            expr = self.getVal_of_expr(ctx.expr())
            if isinstance(expr.type, PointerType):
                expr = self.Builder.load(expr)
            condition = self.getVal_of_expr(ctx.condition())
            return self.Builder.select(cond, expr, condition)
        else:
            return self.getVal_of_expr(ctx.disjunction())

    def visitDisjunction(self, ctx: SmallCParser.DisjunctionContext):
        if ctx.disjunction():
            disjunction = self.getVal_of_expr(ctx.disjunction())
            if isinstance(disjunction.type, PointerType):
                disjunction = self.Builder.load(disjunction)
            conjunction = self.getVal_of_expr(ctx.conjunction())
            if isinstance(conjunction.type, PointerType):
                conjunction = self.Builder.load(conjunction)
            left = self.Builder.icmp_signed('!=', disjunction,
                                            Constant(disjunction.type, 0))
            right = self.Builder.icmp_signed('!=', conjunction,
                                             Constant(conjunction.type, 0))
            return self.Builder.or_(left, right)
        else:
            return self.getVal_of_expr(ctx.conjunction())

    def visitConjunction(self, ctx: SmallCParser.ConjunctionContext):
        if ctx.conjunction():
            conjunction = self.getVal_of_expr(ctx.conjunction())
            if isinstance(conjunction.type, PointerType):
                conjunction = self.Builder.load(conjunction)
            comparison = self.getVal_of_expr(ctx.comparison())
            if isinstance(comparison.type, PointerType):
                comparison = self.Builder.load(comparison)
            left = self.Builder.icmp_signed('!=', conjunction,
                                            Constant(conjunction.type, 0))
            right = self.Builder.icmp_signed('!=', comparison,
                                             Constant(comparison.type, 0))
            return self.Builder.and_(left, right)
        else:
            return self.getVal_of_expr(ctx.comparison())

    def visitComparison(self, ctx: SmallCParser.ComparisonContext):
        if ctx.EQUALITY():
            relation1 = self.getVal_of_expr(ctx.relation(0))
            if isinstance(relation1.type, PointerType):
                relation1 = self.Builder.load(relation1)
            relation2 = self.getVal_of_expr(ctx.relation(1))
            if isinstance(relation2.type, PointerType):
                relation2 = self.Builder.load(relation2)
            return self.Builder.icmp_signed('==', relation1, relation2)
        elif ctx.NEQUALITY():
            relation1 = self.getVal_of_expr(ctx.relation(0))
            if isinstance(relation1.type, PointerType):
                relation1 = self.Builder.load(relation1)
            relation2 = self.getVal_of_expr(ctx.relation(1))
            if isinstance(relation2.type, PointerType):
                relation2 = self.Builder.load(relation2)
            return self.Builder.icmp_signed('!=', relation1, relation2)
        else:
            return self.getVal_of_expr(ctx.relation(0))

    def visitRelation(self, ctx: SmallCParser.RelationContext):
        if len(ctx.equation()) > 1:
            equation1 = self.getVal_of_expr(ctx.equation(0))
            if isinstance(equation1.type, PointerType):
                equation1 = self.Builder.load(equation1)
            equation2 = self.getVal_of_expr(ctx.equation(1))
            if isinstance(equation2.type, PointerType):
                equation2 = self.Builder.load(equation2)
            if ctx.LEFTANGLE():
                value = self.Builder.icmp_signed('<', equation1, equation2)
            elif ctx.RIGHTANGLE():
                value = self.Builder.icmp_signed('>', equation1, equation2)
            elif ctx.LEFTANGLEEQUAL():
                value = self.Builder.icmp_signed('<=', equation1, equation2)
            elif ctx.RIGHTANGLEEQUAL():
                value = self.Builder.icmp_signed('>=', equation1, equation2)
            return value
        else:
            return self.getVal_of_expr(ctx.equation(0))

    def visitFor_stmt(self, ctx: SmallCParser.For_stmtContext):
        func = self.function_dict[self.function]

        end_block = func.append_basic_block()
        self.var_stack.append({})
        decl_block = func.append_basic_block()
        self.var_stack.append({})
        cond_block = func.append_basic_block()
        self.var_stack.append({})
        stmt_block = func.append_basic_block()
        self.var_stack.append({})
        loop_block = func.append_basic_block()
        # 1 -> continue, -1 -> break
        self.signal_stack.append(0)

        self.loop_stack.append({
            'continue': cond_block,
            'break': end_block,
            'buf': loop_block
        })

        with self.Builder.goto_block(decl_block):
            # self.Builder.position_at_start(end_block)
            if ctx.var_decl():
                self.visit(ctx.var_decl())
            elif ctx.var_decl_list():
                self.visit(ctx.var_decl_list())
            else:
                self.error("for error in <visitFor_stmt>")
            self.Builder.branch(cond_block)

        self.Builder.branch(decl_block)
        with self.Builder.goto_block(cond_block):
            # cond_expr
            cond_expr = ctx.expr(0)
            cond_expr = self.visit(cond_expr)
            cond_expr = self.toBool(cond_expr)
            self.Builder.cbranch(cond_expr, stmt_block, end_block)

        with self.Builder.goto_block(stmt_block):
            # expr
            self.visit(ctx.stmt())
            expr = ctx.expr(1)
            self.visit(expr)
            self.Builder.branch(cond_block)
            if self.signal_stack[-1] == 0:
                loop_blocks = self.loop_stack[-1]
                self.Builder.position_at_start(loop_blocks['buf'])
                self.Builder.branch(end_block)

        self.Builder.position_at_start(end_block)

        self.loop_stack.pop()
        self.signal_stack.pop()

        self.var_stack.pop()
        self.var_stack.pop()
        self.var_stack.pop()
        self.var_stack.pop()

    def visitWhile_stmt(self, ctx: SmallCParser.While_stmtContext):
        func = self.function_dict[self.function]

        end_block = func.append_basic_block()
        self.var_stack.append({})
        cond_block = func.append_basic_block()
        self.var_stack.append({})
        stmt_block = func.append_basic_block()
        self.var_stack.append({})
        loop_block = func.append_basic_block()
        # 1 -> continue, -1 -> break
        self.signal_stack.append(0)

        self.loop_stack.append({
            'continue': cond_block,
            'break': end_block,
            'buf': loop_block
        })

        self.Builder.branch(cond_block)

        with self.Builder.goto_block(cond_block):
            expr = self.getVal_of_expr(ctx.expr())
            cond_expr = self.toBool(expr)
            self.Builder.cbranch(cond_expr, stmt_block, end_block)

        with self.Builder.goto_block(stmt_block):
            self.visit(ctx.stmt())
            self.Builder.branch(cond_block)
            if self.signal_stack[-1] == 0:
                loop_blocks = self.loop_stack[-1]
                self.Builder.position_at_start(loop_blocks['buf'])
                self.Builder.branch(end_block)

        self.Builder.position_at_start(end_block)

        self.loop_stack.pop()
        self.signal_stack.pop()

        self.var_stack.pop()
        self.var_stack.pop()
        self.var_stack.pop()

    def visitCond_stmt(self, ctx: SmallCParser.Cond_stmtContext):

        expr = self.getVal_of_expr(ctx.expr())

        cond_expr = self.toBool(expr)
        else_expr = ctx.ELSE()

        if else_expr:
            with self.Builder.if_else(cond_expr) as (then, otherwise):
                with then:
                    self.var_stack.append({})
                    true_stmt = ctx.stmt(0)
                    self.visit(true_stmt)
                    self.var_stack.pop()
                with otherwise:
                    self.var_stack.append({})
                    else_stmt = ctx.stmt(1)
                    self.visit(else_stmt)
                    self.var_stack.pop()
        else:
            with self.Builder.if_then(cond_expr):
                self.var_stack.append({})
                true_stmt = ctx.stmt(0)
                self.visit(true_stmt)
                self.var_stack.pop()
        return None

    def visitVar_decl(self, ctx: SmallCParser.Var_declContext):
        self.cur_decl_type = self.getType(ctx.type_specifier().getText())
        return self.visitChildren(ctx)

    def visitVar_decl_list(self, ctx: SmallCParser.Var_decl_listContext):
        ans = []
        decls = ctx.variable_id()
        for decl in decls:
            ans.append(self.visit(decl))
        return ans

    def visitVariable_id(self, ctx: SmallCParser.Variable_idContext):
        identifier = ctx.identifier()
        type = self.cur_decl_type

        if not self.function:
            if identifier.array_indexing():
                length = self.getVal_of_expr(
                    identifier.array_indexing().expr())
                type = ArrayType(type, length.constant)
                g_var = GlobalVariable(self.Module, type,
                                       identifier.IDENTIFIER().getText())
            else:
                g_var = GlobalVariable(self.Module, type,
                                       identifier.IDENTIFIER().getText())

            g_var.initializer = Constant(type, None)
            atomic = {
                "id": identifier.IDENTIFIER().getText(),
                "type": type,
                "value": None,
                "ptr": g_var
            }
            if not len(self.var_stack):
                self.var_stack.append({})
            self.var_stack[0][identifier.IDENTIFIER().getText()] = atomic
            return g_var

        var_map = self.var_stack[-1]

        if identifier.array_indexing():
            length = self.getVal_of_expr(identifier.array_indexing().expr())
            type = ArrayType(type, length.constant)
            ptr = self.Builder.alloca(typ=type,
                                      name=identifier.IDENTIFIER().getText())
        else:
            ptr = self.Builder.alloca(typ=type,
                                      name=identifier.IDENTIFIER().getText())

        expr = ctx.expr()
        if expr:
            value = self.getVal_of_expr(expr)
        else:
            value = Constant(type, None)
        if isinstance(value.type, PointerType):
            value = self.Builder.load(value)
        self.Builder.store(value, ptr)
        var_map[identifier.IDENTIFIER().getText()] = {
            "id": identifier.IDENTIFIER().getText(),
            "type": type,
            "value": value,
            "ptr": ptr
        }
        return ptr

    def visitPrimary(self, ctx: SmallCParser.PrimaryContext):
        if ctx.BOOLEAN():
            return Constant(IntType(1), bool(ctx.getText()))
        elif ctx.INTEGER():
            return Constant(IntType(32), int(ctx.getText()))
        elif ctx.REAL():
            return Constant(FloatType, float(ctx.getText()))
        elif ctx.CHARCONST():
            tempStr = ctx.getText()[1:-1]
            tempStr = tempStr.replace('\\n', '\n')
            tempStr = tempStr.replace('\\0', '\0')
            if ctx.getText()[0] == '"':
                tempStr += '\0'
                temp = GlobalVariable(self.Module,
                                      ArrayType(IntType(8), len(tempStr)),
                                      name="str_" + tempStr[:-1] +
                                      str(self.counter))
                self.counter += 1
                temp.initializer = Constant(
                    ArrayType(IntType(8), len(tempStr)),
                    bytearray(tempStr, encoding='utf-8'))
                temp.global_constant = True
                return self.Builder.gep(
                    temp, [Constant(IntType(32), 0),
                           Constant(IntType(32), 0)],
                    inbounds=True)
            return Constant(IntType(8), ord(tempStr[0]))
        elif ctx.identifier():
            return self.visit(ctx.identifier())
        elif ctx.functioncall():
            return self.visit(ctx.functioncall())
        elif ctx.expr():
            return self.visit(ctx.expr())
        else:
            return self.error("type error in <visitPrimary>")

    def visitFactor(self, ctx: SmallCParser.FactorContext):
        if (ctx.MINUS()):
            factor = self.getVal_of_expr(ctx.factor())
            if isinstance(factor.type, PointerType):
                factor = self.Builder.load(factor)
            factor = self.Builder.neg(factor)
            return factor
        return self.visitChildren(ctx)

    def visitTerm(self, ctx: SmallCParser.TermContext):
        if (ctx.ASTERIKS()):
            term = self.getVal_of_expr(ctx.term())
            if isinstance(term.type, PointerType):
                term = self.Builder.load(term)
            factor = self.getVal_of_expr(ctx.factor())
            if isinstance(factor.type, PointerType):
                factor = self.Builder.load(factor)
            return self.Builder.mul(term, factor)
        if (ctx.SLASH()):
            term = self.getVal_of_expr(ctx.term())
            if isinstance(term.type, PointerType):
                term = self.Builder.load(term)
            factor = self.getVal_of_expr(ctx.factor())
            if isinstance(factor.type, PointerType):
                factor = self.Builder.load(factor)
            return self.Builder.sdiv(term, factor)
        return self.visitChildren(ctx)

    def visitEquation(self, ctx: SmallCParser.EquationContext):
        if (ctx.PLUS()):
            equation = self.getVal_of_expr(ctx.equation())
            if isinstance(equation.type, PointerType):
                equation = self.Builder.load(equation)
            if str(equation.type) != 'i32':
                equation = self.Builder.zext(equation, IntType(32))
            term = self.getVal_of_expr(ctx.term())
            if isinstance(term.type, PointerType):
                term = self.Builder.load(term)
            if str(term.type) != 'i32':
                term = self.Builder.zext(term, IntType(32))
            return self.Builder.add(equation, term)
        if (ctx.MINUS()):
            equation = self.getVal_of_expr(ctx.equation())
            if isinstance(equation.type, PointerType):
                equation = self.Builder.load(equation)
            if str(equation.type) != 'i32':
                equation = self.Builder.zext(equation, IntType(32))
            term = self.getVal_of_expr(ctx.term())
            if isinstance(term.type, PointerType):
                term = self.Builder.load(term)
            if str(term.type) != 'i32':
                term = self.Builder.zext(term, IntType(32))
            return self.Builder.sub(equation, term)
        return self.visitChildren(ctx)

    def visitIdentifier(self, ctx: SmallCParser.IdentifierContext):
        if (ctx.AMPERSAND() and ctx.array_indexing()):
            return self.Builder.gep(self.getVal_of_expr(ctx.IDENTIFIER()),
                                    self.getVal_of_expr(ctx.array_indexing()))
        if (ctx.ASTERIKS() and ctx.array_indexing()):
            return self.Builder.load(
                self.Builder.gep(self.getVal_of_expr(ctx.IDENTIFIER()),
                                 self.getVal_of_expr(ctx.array_indexing())))
        if (ctx.AMPERSAND()):
            return self.getVal_local(str(ctx.IDENTIFIER()))['ptr']
        if (ctx.ASTERIKS()):
            return self.getVal_local(str(ctx.IDENTIFIER()))['ptr']
        temp = self.getVal_local(ctx.IDENTIFIER().getText())
        temp_ptr = temp['ptr']
        # if isinstance(temp_val['type'],ArrayType):
        #     if temp.array_indexing():
        #         index = self.getVal_of_expr(temp.array_indexing().expr())
        #         temp_ptr = self.Builder.gep(temp_ptr, [Constant(IntType(32), 0), index], inbounds=True)
        #     elif temp.AMPERSAND():
        #         Constant(PointerType(IntType(8)), temp_ptr.getText())
        #     elif temp.ASTERIKS():
        #         pass
        #     else: #返回数组地址
        #         temp_ptr = self.Builder.gep(temp_ptr, [Constant(IntType(32), 0), Constant(IntType(32), 0)], inbounds=True)
        #         return temp_ptr
        if isinstance(temp['type'], ArrayType):
            if ctx.array_indexing():
                index = self.getVal_of_expr(ctx.array_indexing().expr())
                if isinstance(index.type, PointerType):
                    index = self.Builder.load(index)
                temp_ptr = self.Builder.gep(temp_ptr,
                                            [Constant(IntType(32), 0), index],
                                            inbounds=True)
                value = self.Builder.load(temp_ptr)
                self.var_stack[-1][temp_ptr.name] = {
                    'id': temp_ptr.name,
                    'type': temp_ptr.type,
                    'value': value,
                    'ptr': temp_ptr
                }
                return temp_ptr
            else:
                temp_ptr = self.Builder.gep(
                    temp_ptr,
                    [Constant(IntType(32), 0),
                     Constant(IntType(32), 0)],
                    inbounds=True)
                value = self.Builder.load(temp_ptr)
                self.var_stack[-1][temp_ptr.name] = {
                    'id': temp_ptr.name,
                    'type': temp_ptr.type,
                    'value': value,
                    'ptr': temp_ptr
                }
                return temp_ptr
        temp_val = temp['ptr']
        return temp_val

    def visitArray_indexing(self, ctx: SmallCParser.Array_indexingContext):
        return self.visit(ctx.expr())
Exemplo n.º 7
0
# Generate code in the then-branch
builder.position_at_end(then_block)
result = builder.add(a_var, b_var)
builder.store(result, c_var)
builder.branch(merge_block)

# Generate code in the else-branch
builder.position_at_end(else_block)
result = builder.sub(a_var, b_var)
builder.store(result, c_var)
builder.branch(merge_block)

# Emit code after the if-else
builder.position_at_end(merge_block)
builder.ret(builder.load(c_var))

print(mod)

print(":::: RUNNING ::::")

import llvmlite.binding as llvm

llvm.initialize()
llvm.initialize_native_target()
llvm.initialize_native_asmprinter()

target = llvm.Target.from_default_triple()
target_machine = target.create_target_machine()

compiled_mod = llvm.parse_assembly(str(mod))
Exemplo n.º 8
0
class GenerateLLVM(object):
    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')

        # 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.globals = {}
        self.vars = ChainMap(self.globals)

        # Dictionary that holds all of the temporary registers created in
        # the intermediate code.

        # Initialize the runtime library functions (see below)
        self.declare_runtime_library()

    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_byte'] = Function(self.module,
                                               FunctionType(
                                                   void_type, [byte_type]),
                                               name="_print_byte")

    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, 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 instr in ircode:
            if instr[0] == 'LABEL':
                self.blocks[instr[1]] = self.function.append_basic_block(
                    instr[1])

        for opcode, *args in ircode:
            if opcode == 'CALL':
                self.emit_CALL(*args[:-1], target=args[-1])
            elif hasattr(self, 'emit_' + opcode):
                getattr(self, 'emit_' + opcode)(*args)
            else:
                print('Warning: No emit_' + opcode + '() method')

    # ----------------------------------------------------------------------
    # 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_MOVI(self, value, target):
        self.temps[target] = Constant(int_type, value)

    def emit_MOVF(self, value, target):
        self.temps[target] = Constant(float_type, value)

    def emit_MOVB(self, value, target):
        self.temps[target] = Constant(byte_type, value)

    # Allocation of variables.  Declare as global variables and set to
    # a sensible initial value.
    def emit_VARI(self, name):
        var = GlobalVariable(self.module, int_type, name=name)
        var.initializer = Constant(int_type, 0)
        self.globals[name] = var

    def emit_VARF(self, name):
        var = GlobalVariable(self.module, float_type, name=name)
        var.initializer = Constant(float_type, 0.0)
        self.globals[name] = var

    def emit_VARB(self, name):
        var = GlobalVariable(self.module, byte_type, name=name)
        var.initializer = Constant(byte_type, 0)
        self.globals[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 emit_LOADI(self, name, target):
        self.temps[target] = self.builder.load(self.vars[name], target)

    def emit_LOADF(self, name, target):
        self.temps[target] = self.builder.load(self.vars[name], target)

    def emit_LOADB(self, name, target):
        self.temps[target] = self.builder.load(self.vars[name], target)

    def emit_STOREI(self, source, target):
        self.builder.store(self.temps[source], self.vars[target])

    def emit_STOREF(self, source, target):
        self.builder.store(self.temps[source], self.vars[target])

    def emit_STOREB(self, source, target):
        self.builder.store(self.temps[source], self.vars[target])

    # Binary + operator
    def emit_ADDI(self, left, right, target):
        self.temps[target] = self.builder.add(self.temps[left],
                                              self.temps[right], target)

    def emit_ADDF(self, left, right, target):
        self.temps[target] = self.builder.fadd(self.temps[left],
                                               self.temps[right], target)

    # Binary - operator
    def emit_SUBI(self, left, right, target):
        self.temps[target] = self.builder.sub(self.temps[left],
                                              self.temps[right], target)

    def emit_SUBF(self, left, right, target):
        self.temps[target] = self.builder.fsub(self.temps[left],
                                               self.temps[right], target)

    # Binary * operator
    def emit_MULI(self, left, right, target):
        self.temps[target] = self.builder.mul(self.temps[left],
                                              self.temps[right], target)

    def emit_MULF(self, left, right, target):
        self.temps[target] = self.builder.fmul(self.temps[left],
                                               self.temps[right], target)

    # Binary / operator
    def emit_DIVI(self, left, right, target):
        self.temps[target] = self.builder.sdiv(self.temps[left],
                                               self.temps[right], target)

    def emit_DIVF(self, left, right, target):
        self.temps[target] = self.builder.fdiv(self.temps[left],
                                               self.temps[right], target)

    def emit_CMPI(self, op, left, right, target):
        tmp = self.builder.icmp_signed(op, self.temps[left], self.temps[right],
                                       'tmp')
        self.temps[target] = self.builder.zext(tmp, int_type, target)

    def emit_CMPF(self, op, left, right, target):
        tmp = self.builder.fcmp_ordered(op, self.temps[left],
                                        self.temps[right], 'tmp')
        self.temps[target] = self.builder.zext(tmp, int_type, target)

    def emit_CMPB(self, op, left, right, target):
        tmp = self.builder.icmp_signed(op, self.temps[left], self.temps[right],
                                       'tmp')
        self.temps[target] = self.builder.zext(tmp, int_type, target)

    # Logical ops
    def emit_AND(self, left, right, target):
        self.temps[target] = self.builder.and_(self.temps[left],
                                               self.temps[right], target)

    def emit_OR(self, left, right, target):
        self.temps[target] = self.builder.or_(self.temps[left],
                                              self.temps[right], target)

    # control flow
    def emit_LABEL(self, label):
        self.block = self.blocks[label]
        self.builder.position_at_end(self.blocks[label])

    def emit_BRANCH(self, label):
        if not self.block.is_terminated:
            self.builder.branch(self.blocks[label])

    def emit_CBRANCH(self, test, label1, label2):
        tmp = self.builder.trunc(self.temps[test], IntType(1), 'tmp')
        self.builder.cbranch(tmp, self.blocks[label1], self.blocks[label2])

    # functions
    def emit_ALLOCI(self, name):
        var = self.builder.alloca(int_type, name=name)
        var.initializer = Constant(int_type, 0)
        self.vars[name] = var

    def emit_ALLOCF(self, name):
        var = self.builder.alloca(float_type, name=name)
        var.initializer = Constant(float_type, 0)
        self.vars[name] = var

    def emit_ALLOCB(self, name):
        var = self.builder.alloca(byte_type, name=name)
        var.initializer = Constant(byte_type, 0)
        self.vars[name] = var

    def emit_RET(self, source):
        self.builder.store(self.temps[source], self.vars['return'])
        self.builder.branch(self.return_block)

    def emit_CALL(self, func_name, *args, target):
        func = self.vars[func_name]
        args = [self.temps[arg] for arg in args]
        self.temps[target] = self.builder.call(func, args)

    # Print statements
    def emit_PRINTI(self, source):
        self.builder.call(self.runtime['_print_int'], [self.temps[source]])

    def emit_PRINTF(self, source):
        self.builder.call(self.runtime['_print_float'], [self.temps[source]])

    def emit_PRINTB(self, source):
        self.builder.call(self.runtime['_print_byte'], [self.temps[source]])
Exemplo n.º 9
0
loop_body_block = f_func.append_basic_block('loopbody')
loop_exit_block = f_func.append_basic_block('loopexit')

# Branch based on the test var
builder.cbranch(testvar, loop_body_block, loop_exit_block)

builder.position_at_end(loop_body_block)
tmp = builder.add(builder.load(total_var), builder.load(n_var))
builder.store(tmp, total_var)
tmp2 = builder.sub(builder.load(n_var), Constant(IntType(32), 1))
builder.store(tmp2, n_var)
builder.branch(loop_test_block)

# Emit code in the loop-exit
builder.position_at_end(loop_exit_block)
builder.ret(builder.load(total_var))

print(mod)

print(':::: RUNNING ::::')

import llvmlite.binding as llvm

llvm.initialize()
llvm.initialize_native_target()
llvm.initialize_native_asmprinter()

target = llvm.Target.from_default_triple()
target_machine = target.create_target_machine()

compiled_mod = llvm.parse_assembly(str(mod))
Exemplo n.º 10
0
 def codegen(self, builder: ir.IRBuilder, ctx: CodegenContext) -> None:
     if isinstance(self.e, Expr):
         value = self.e.codegen(builder, ctx)
         builder.ret(value)
     else:
         builder.ret_void()
Exemplo n.º 11
0
class GenerateLLVM(object):
    def __init__(self):
        # Perform the basic LLVM initialization.  You need the following parts:
        #
        #    1.  A top-level Module object
        #    2.  A dictionary of global declarations
        #    3.  Initialization of runtime functions (for printing)
        #
        self.module = Module('module')

        # Dictionary that holds all of the global variable/function declarations.
        # Any declaration in the Wabbit source code is going to get an entry here
        self.globals = {}

        # Initialize the runtime library functions (see below)
        self.declare_runtime_library()

    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 wabbitrt.c

        self.runtime = {}

        # Declare runtime functions
        functions = [
            ('_print_int', void_type, [int_type]),
            ('_print_float', void_type, [float_type]),
            ('_print_byte', void_type, [int_type]),
            ('_grow', int_type, [int_type]),
            ('_peeki', int_type, [int_type]),
            ('_peekf', float_type, [int_type]),
            ('_peekb', int_type, [int_type]),
            ('_pokei', void_type, [int_type, int_type]),
            ('_pokef', void_type, [int_type, float_type]),
            ('_pokeb', void_type, [int_type, int_type]),
            ]
        for name, rettype, args in functions:
            self.runtime[name] = Function(self.module,
                                          FunctionType(rettype, args),
                                          name=name)

    def declare_function(self, funcname, argtypes, rettype):
        self.function = Function(self.module,
                                 FunctionType(rettype, argtypes),
                                 name=funcname)

        # Insert a reference in global namespace
        self.globals[funcname] = self.function

    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()

    # Helper methods for LLVM temporary stack manipulation
    def push(self, value):
        self.stack.append(value)

    def pop(self):
        return self.stack.pop()

    def set_block(self, block):
        self.block = block
        self.builder.position_at_end(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_CONSTI(self, value):
        self.push(Constant(int_type, value))

    def emit_CONSTF(self, value):
        self.push(Constant(float_type, value))

    # Allocation of variables.  Declare as global variables and set to
    # a sensible initial value.
    def emit_VARI(self, name):
        self.locals[name] = self.builder.alloca(int_type, name=name)

    def emit_VARF(self, name):
        self.locals[name] = self.builder.alloca(float_type, name=name)

    # Allocation of globals
    def emit_GLOBALI(self, name):
        var = GlobalVariable(self.module, int_type, name=name)
        var.initializer = Constant(int_type, 0)
        self.globals[name] = var

    def emit_GLOBALF(self, name):
        var = GlobalVariable(self.module, float_type, name=name)
        var.initializer = Constant(float_type, 0.0)
        self.globals[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 emit_LOAD(self, name):
        self.push(self.builder.load(self.symbols[name], name))

    def emit_STORE(self, target):
        self.builder.store(self.pop(), self.symbols[target])

    # Binary + operator
    def emit_ADDI(self):
        self.push(self.builder.add(self.pop(), self.pop()))

    def emit_ADDF(self):
        self.push(self.builder.fadd(self.pop(), self.pop()))

    # Binary - operator
    def emit_SUBI(self):
        right = self.pop()
        left = self.pop()
        self.push(self.builder.sub(left, right))

    def emit_SUBF(self):
        right = self.pop()
        left = self.pop()
        self.push(self.builder.fsub(left, right))

    # Binary * operator
    def emit_MULI(self):
        self.push(self.builder.mul(self.pop(), self.pop()))

    def emit_MULF(self):
        self.push(self.builder.fmul(self.pop(), self.pop()))

    # Binary / operator
    def emit_DIVI(self):
        right = self.pop()
        left = self.pop()
        self.push(self.builder.sdiv(left, right))

    def emit_DIVF(self):
        right = self.pop()
        left = self.pop()
        self.push(self.builder.fdiv(left, right))

    # Conversion
    def emit_ITOF(self):
        self.push(self.builder.sitofp(self.pop(), float_type))

    def emit_FTOI(self):
        self.push(self.builder.fptosi(self.pop(), int_type))

    # Comparison operators
    def emit_LEI(self):
        right = self.pop()
        left = self.pop()
        result = self.builder.icmp_signed('<=', left, right)
        self.push(self.builder.zext(result, int_type))

    def emit_LTI(self):
        right = self.pop()
        left = self.pop()
        result = self.builder.icmp_signed('<', left, right)
        self.push(self.builder.zext(result, int_type))

    def emit_GEI(self):
        right = self.pop()
        left = self.pop()
        result = self.builder.icmp_signed('>=', left, right)
        self.push(self.builder.zext(result, int_type))

    def emit_GTI(self):
        right = self.pop()
        left = self.pop()
        result = self.builder.icmp_signed('>', left, right)
        self.push(self.builder.zext(result, int_type))
        
    def emit_EQI(self):
        right = self.pop()
        left = self.pop()
        result = self.builder.icmp_signed('==', left, right)
        self.push(self.builder.zext(result, int_type))

    def emit_NEI(self):
        right = self.pop()
        left = self.pop()
        result = self.builder.icmp_signed('!=', left, right)
        self.push(self.builder.zext(result, int_type))

    # Comparison operators
    def emit_LEF(self):
        right = self.pop()
        left = self.pop()
        result = self.builder.fcmp_ordered('<=', left, right)
        self.push(self.builder.zext(result, int_type))

    def emit_LTF(self):
        right = self.pop()
        left = self.pop()
        result = self.builder.fcmp_ordered('<', left, right)
        self.push(self.builder.zext(result, int_type))

    def emit_GEF(self):
        right = self.pop()
        left = self.pop()
        result = self.builder.fcmp_ordered('>=', left, right)
        self.push(self.builder.zext(result, int_type))

    def emit_GTF(self):
        right = self.pop()
        left = self.pop()
        result = self.builder.fcmp_ordered('>', left, right)
        self.push(self.builder.zext(result, int_type))
        
    def emit_EQF(self):
        right = self.pop()
        left = self.pop()
        result = self.builder.fcmp_ordered('==', left, right)
        self.push(self.builder.zext(result, int_type))

    def emit_NEF(self):
        right = self.pop()
        left = self.pop()
        result = self.builder.fcmp_ordered('!=', left, right)
        self.push(self.builder.zext(result, int_type))

    # Bitwise operations

    def emit_ANDI(self):
        right = self.pop()
        left = self.pop()
        self.push(self.builder.and_(left, right))

    def emit_ORI(self):
        right = self.pop()
        left = self.pop()
        self.push(self.builder.or_(left, right))
        
    # Print statements
    def emit_PRINTI(self):
        self.builder.call(self.runtime['_print_int'], [self.pop()])

    def emit_PRINTF(self):
        self.builder.call(self.runtime['_print_float'], [self.pop()])

    def emit_PRINTB(self):
        self.builder.call(self.runtime['_print_byte'], [self.pop()])

    # Memory statements
    def emit_GROW(self):
        self.push(self.builder.call(self.runtime['_grow'], [self.pop()]))

    def emit_PEEKI(self):
        self.push(self.builder.call(self.runtime['_peeki'], [self.pop()]))

    def emit_PEEKF(self):
        self.push(self.builder.call(self.runtime['_peekf'], [self.pop()]))

    def emit_PEEKB(self):
        self.push(self.builder.call(self.runtime['_peekb'], [self.pop()]))

    def emit_POKEI(self):
        value = self.pop()
        addr = self.pop()
        self.builder.call(self.runtime['_pokei'], [addr, value])

    def emit_POKEF(self):
        value = self.pop()
        addr = self.pop()
        self.builder.call(self.runtime['_pokef'], [addr, value])

    def emit_POKEB(self):
        value = self.pop()
        addr = self.pop()
        self.builder.call(self.runtime['_pokeb'], [addr, value])

    # Control flow
    def emit_IF(self):
        then_block = self.function.append_basic_block()
        else_block = self.function.append_basic_block()
        exit_block = self.function.append_basic_block()
        self.builder.cbranch(self.builder.trunc(self.pop(), IntType(1)), then_block, else_block)
        self.set_block(then_block)
        self.blocks.append([then_block, else_block, exit_block])

    def emit_ELSE(self):
        if not self.block.is_terminated:
            self.builder.branch(self.blocks[-1][2])
        self.set_block(self.blocks[-1][1])

    def emit_ENDIF(self):
        if not self.block.is_terminated:
            self.builder.branch(self.blocks[-1][2])
        self.set_block(self.blocks[-1][2])
        self.blocks.pop()

    def emit_LOOP(self):
        top_block = self.function.append_basic_block()
        exit_block = self.function.append_basic_block()
        self.builder.branch(top_block)
        self.set_block(top_block)
        self.blocks.append([top_block, exit_block])

    def emit_CBREAK(self):
        next_block = self.function.append_basic_block()
        self.builder.cbranch(self.builder.trunc(self.pop(), IntType(1)), self.blocks[-1][1], next_block)
        self.set_block(next_block)
        
    def emit_ENDLOOP(self):
        if not self.block.is_terminated:
            self.builder.branch(self.blocks[-1][0])
        self.set_block(self.blocks[-1][1])
        self.blocks.pop()

    def emit_RETURN(self):
        self.builder.ret(self.pop())

    def emit_CALL(self, name):
        func = self.globals[name]
        args = [self.pop() for _ in range(len(func.args))][::-1]
        self.push(self.builder.call(func, args))
Exemplo n.º 12
0
class LLVMGen:
    builder: Optional[IRBuilder]
    current_func: Optional[RIALFunction]
    conditional_block: Optional[LLVMBlock]
    end_block: Optional[LLVMBlock]
    current_block: Optional[LLVMBlock]
    current_struct: Optional[RIALIdentifiedStructType]
    currently_unsafe: bool

    def __init__(self):
        self.current_block = None
        self.conditional_block = None
        self.end_block = None
        self.current_func = None
        self.builder = None
        self.current_struct = None
        self.currently_unsafe = False

    def _get_by_identifier(self, identifier: str, variable: Optional = None) -> Optional:
        if isinstance(variable, RIALVariable):
            variable = variable.backing_value

        if not variable is None and hasattr(variable, 'type') and isinstance(variable.type, PointerType):
            if isinstance(variable.type.pointee, RIALIdentifiedStructType):
                struct = ParserState.find_struct(variable.type.pointee.name)

                if struct is None:
                    return None

                if not self.check_struct_access_allowed(struct):
                    raise PermissionError(f"Tried accesssing struct {struct.name}")

                prop = struct.definition.properties[identifier]

                if prop is None:
                    return None

                # Check property access
                if not self.check_property_access_allowed(struct, prop[1]):
                    raise PermissionError(f"Tried to access property {prop[1].name} but it was not allowed!")

                variable = self.builder.gep(variable, [ir.Constant(ir.IntType(32), 0),
                                                       ir.Constant(ir.IntType(32), prop[0])])
        else:
            # Search local variables
            variable = self.current_block.get_named_value(identifier)

            # Search for a global variable
            if variable is None:
                glob = ParserState.find_global(identifier)

                # Check if in same module
                if glob is not None:
                    if glob.backing_value.parent.name != ParserState.module().name:
                        glob_current_module = ParserState.module().get_global_safe(glob.name)

                        if glob_current_module is not None:
                            variable = glob_current_module
                        else:
                            # TODO: Check if global access is allowed
                            variable = self.gen_global(glob.name, None, glob.backing_value.type.pointee,
                                                       glob.access_modifier, "external",
                                                       glob.backing_value.global_constant)
                    else:
                        variable = glob.backing_value

        # If variable is none, just do a full function search
        if variable is None:
            variable = ParserState.find_function(identifier)

            if variable is None:
                variable = ParserState.find_function(mangle_function_name(identifier, []))

            # Check if in same module
            if variable is not None:
                if variable.module.name != ParserState.module().name:
                    variable_current_module = ParserState.module().get_global_safe(variable.name)

                    if variable_current_module is not None:
                        variable = variable_current_module
                    else:
                        variable = self.create_function_with_type(variable.name, variable.name, variable.function_type,
                                                                  variable.linkage,
                                                                  variable.calling_convention,
                                                                  variable.definition)

        return variable

    def get_exact_definition(self, identifier: str):
        identifiers = identifier.split('.')
        variable = None

        for ident in identifiers:
            variable = self._get_by_identifier(ident, variable)

        # Search for the full name
        if variable is None:
            variable = self._get_by_identifier(identifier, variable)

        if variable is None:
            return None

        if isinstance(variable, RIALFunction):
            return variable

        if isinstance(variable, RIALVariable):
            return variable

        return RIALVariable(identifier, map_llvm_to_type(variable.type.pointee), variable)

    def get_definition(self, identifier: str):
        variable = self.get_exact_definition(identifier)

        # Search with module specifier
        if variable is None:
            if ':' in identifier:
                parts = identifier.split(':')
                module_name = ':'.join(parts[0:-1])

                if CompilationManager.check_module_already_compiled(module_name):
                    return None

                if ParserState.add_dependency_and_wait(module_name):
                    return self.get_definition(identifier)

                return None

        return variable

    def gen_integer(self, number: int, length: int, unsigned: bool = False):
        return ir.Constant((unsigned and LLVMUIntType(length) or ir.IntType(length)), number)

    def gen_half(self, number: float):
        return ir.Constant(ir.HalfType(), number)

    def gen_float(self, number: float):
        return ir.Constant(ir.FloatType(), number)

    def gen_double(self, number: float):
        return ir.Constant(ir.DoubleType(), number)

    def gen_global(self, name: str, value: Optional[ir.Constant], ty: Type, access_modifier: RIALAccessModifier,
                   linkage: str,
                   constant: bool):
        rial_variable = ParserState.module().get_rial_variable(name)

        if rial_variable is not None:
            return rial_variable.backing_value

        glob = ir.GlobalVariable(ParserState.module(), ty, name=name)
        glob.linkage = linkage
        glob.global_constant = constant

        if value is not None:
            glob.initializer = value

        rial_variable = RIALVariable(name, map_llvm_to_type(ty), glob,
                                     access_modifier)
        ParserState.module().global_variables.append(rial_variable)

        return glob

    def gen_string_lit(self, name: str, value: str):
        value = eval("'{}'".format(value))

        # TODO: Remove the always-added \0 once we don't need that anymore
        arr = bytearray(value.encode("utf-8") + b"\x00")
        const_char_arr = ir.Constant(ir.ArrayType(ir.IntType(8), len(arr)), arr)
        glob = self.gen_global(name, const_char_arr, const_char_arr.type, RIALAccessModifier.PRIVATE, "private", True)

        return glob

    def gen_load_if_necessary(self, value):
        if isinstance(value, AllocaInstr) or isinstance(value, Argument) or isinstance(value, FormattedConstant) or (
                hasattr(value, 'type') and isinstance(value.type, PointerType)):
            return self.builder.load(value)
        return value

    def gen_var_if_necessary(self, value):
        # Check if variable is not:
        #   - Pointer
        #   - Alloca (Variable), inherently pointer
        #   - Argument (always pointer)
        #   - Type is pointer (e.g. getelementptr instruction)
        if not (isinstance(value, PointerType) or isinstance(value, AllocaInstr) or isinstance(value, Argument)
                or isinstance(value.type, PointerType)):
            allocad = self.builder.alloca(value.type)
            self.builder.store(value, allocad)
            return allocad
        return value

    def gen_addition(self, left, right):
        left = self.gen_load_if_necessary(left)
        right = self.gen_load_if_necessary(right)

        if isinstance(left.type, ir.IntType):
            return self.builder.add(left, right)

        if isinstance(left.type, ir.FloatType) or isinstance(left.type, ir.DoubleType):
            return self.builder.fadd(left, right)

        return None

    def gen_subtraction(self, left, right):
        left = self.gen_load_if_necessary(left)
        right = self.gen_load_if_necessary(right)

        if isinstance(left.type, ir.IntType):
            return self.builder.sub(left, right)

        if isinstance(left.type, ir.FloatType) or isinstance(left.type, ir.DoubleType):
            return self.builder.fsub(left, right)

        return None

    def gen_multiplication(self, left, right):
        left = self.gen_load_if_necessary(left)
        right = self.gen_load_if_necessary(right)

        if isinstance(left.type, ir.IntType):
            return self.builder.mul(left, right)

        if isinstance(left.type, ir.FloatType) or isinstance(left.type, ir.DoubleType):
            return self.builder.fmul(left, right)

        return None

    def gen_division(self, left, right):
        left = self.gen_load_if_necessary(left)
        right = self.gen_load_if_necessary(right)

        if isinstance(left.type, LLVMUIntType):
            return self.builder.udiv(left, right)

        if isinstance(left.type, ir.IntType):
            return self.builder.sdiv(left, right)

        if isinstance(left.type, ir.FloatType) or isinstance(left.type, ir.DoubleType):
            return self.builder.fdiv(left, right)

        return None

    def gen_comparison(self, comparison: str, left, right):
        left = left
        right = right

        if isinstance(left.type, LLVMUIntType) or isinstance(left.type, ir.PointerType):
            return self.builder.icmp_unsigned(comparison, left, right)

        if isinstance(left.type, ir.IntType):
            return self.builder.icmp_signed(comparison, left, right)

        if isinstance(left.type, ir.FloatType) or isinstance(left.type, ir.DoubleType):
            return self.builder.fcmp_ordered(comparison, left, right)

        return None

    def gen_shorthand(self, variable, value, operation):
        loaded = self.builder.load(variable)
        value = self.gen_load_if_necessary(value)
        mathed = None

        if operation == "+":
            mathed = self.gen_addition(loaded, value)
        elif operation == "-":
            mathed = self.gen_subtraction(loaded, value)
        elif operation == "*":
            mathed = self.gen_multiplication(loaded, value)
        elif operation == "/":
            mathed = self.gen_division(loaded, value)

        self.builder.store(mathed, variable)

        return mathed

    def gen_function_call(self, possible_function_names: List[str],
                          llvm_args: List) -> Optional[CallInstr]:
        func = None
        # Check if it's actually a local variable
        for function_name in possible_function_names:
            var = self.get_definition(function_name)

            if var is not None:
                func = var

        # Try to find by function name
        if func is None:
            for function_name in possible_function_names:
                func = ParserState.find_function(function_name)

                if func is not None:
                    break

        # Try to find by function name but enable canonical name
        if func is None:
            rial_arg_types = [map_llvm_to_type(arg.type) for arg in llvm_args]

            for function_name in possible_function_names:
                func = ParserState.find_function(function_name, rial_arg_types)

                if func is not None:
                    break

        if func is None:
            return None

        if isinstance(func, RIALVariable):
            func = func.backing_value

        if isinstance(func, ir.PointerType) and isinstance(func.pointee, RIALFunction):
            func = func.pointee
        elif isinstance(func, GlobalVariable) or isinstance(func, AllocaInstr):
            loaded_func = self.builder.load(func)
            call = self.builder.call(loaded_func, llvm_args)
            return call

        # Check if call is allowed
        if not self.check_function_call_allowed(func):
            raise PermissionError(f"Tried calling function {func.name} from {self.current_func.name}")

        # Check if function is declared in current module
        if ParserState.module().get_global_safe(func.name) is None:
            func = self.create_function_with_type(func.name, func.canonical_name, func.function_type, func.linkage,
                                                  func.calling_convention, func.definition)

        args = list()

        # Gen a load if necessary
        for i, arg in enumerate(llvm_args):
            llvm_arg = func.definition.rial_args[i].llvm_type

            if llvm_arg == arg.type:
                args.append(arg)
                continue

            args.append(self.builder.load(arg))

        # Check type matching
        for i, arg in enumerate(args):
            if len(func.args) > i and arg.type != func.args[i].type:
                # Check for base types
                ty = isinstance(arg.type, PointerType) and arg.type.pointee or arg.type
                func_arg_type = isinstance(func.args[i].type, PointerType) and func.args[i].type.pointee or func.args[
                    i].type

                if isinstance(ty, RIALIdentifiedStructType):
                    struct = ParserState.find_struct(ty.name)

                    if struct is not None:
                        found = False

                        # Check if a base struct matches the type expected
                        # TODO: Recursive check
                        for base_struct in struct.definition.base_structs:
                            if base_struct == func_arg_type.name:
                                args.remove(arg)
                                args.insert(i, self.builder.bitcast(arg, ir.PointerType(base_struct)))
                                found = True
                                break
                        if found:
                            continue

                    # TODO: SLOC information
                raise TypeError(
                    f"Function {func.name} expects a {func.args[i].type} but got a {arg.type}")

        # Gen call
        return self.builder.call(func, args)

    def gen_no_op(self):
        self.gen_function_call(["rial:builtin:settings:nop_function"], [])

    def check_function_call_allowed(self, func: RIALFunction):
        # Unsafe in safe context is big no-no
        if func.definition.unsafe and not self.currently_unsafe:
            return False
        # Public is always okay
        if func.definition.access_modifier == RIALAccessModifier.PUBLIC:
            return True
        # Internal only when it's the same TLM
        if func.definition.access_modifier == RIALAccessModifier.INTERNAL:
            return func.module.name.split(':')[0] == ParserState.module().name.split(':')[0]
        # Private is harder
        if func.definition.access_modifier == RIALAccessModifier.PRIVATE:
            # Allowed if not in struct and in current module
            if func.definition.struct == "" and func.module.name == ParserState.module().name:
                return True
            # Allowed if in same struct irregardless of module
            if self.current_struct is not None and self.current_struct.name == func.definition.struct:
                return True
        return False

    def check_property_access_allowed(self, struct: RIALIdentifiedStructType, prop: RIALVariable):
        # Same struct, anything goes
        if self.current_struct is not None and self.current_struct.name == struct.name:
            return True

        # Unless it's private it's okay
        if prop.access_modifier == RIALAccessModifier.PRIVATE:
            return False

        return True

    def check_struct_access_allowed(self, struct: RIALIdentifiedStructType):
        # Public is always okay
        if struct.definition.access_modifier == RIALAccessModifier.PUBLIC:
            return True

        # Private and same module
        if struct.definition.access_modifier == RIALAccessModifier.PRIVATE:
            return struct.module_name == ParserState.module().name

        # Internal and same TLM
        if struct.definition.access_modifier == RIALAccessModifier.INTERNAL:
            return struct.module_name.split(':')[0] == ParserState.module().name.split(':')[0]

        return False

    def declare_nameless_variable_from_rial_type(self, rial_type: str, value):
        returned_type = ParserState.map_type_to_llvm_no_pointer(rial_type)

        if isinstance(returned_type, ir.VoidType):
            return value
        returned_value = self.builder.alloca(returned_type)

        if isinstance(ParserState.map_type_to_llvm(rial_type), PointerType):
            self.builder.store(self.builder.load(value), returned_value)
        else:
            self.builder.store(value, returned_value)

        return returned_value

    def assign_non_constant_global_variable(self, glob: GlobalVariable, value):
        if self.current_func.name != "global_ctor":
            raise PermissionError()

        if isinstance(value, AllocaInstr):
            value = self.builder.load(value)
        elif isinstance(value, PointerType):
            value = self.builder.load(value.pointee)
        elif isinstance(value, FormattedConstant):
            value = self.builder.load(value)

        self.builder.store(value, glob)

    def declare_non_constant_global_variable(self, identifier: str, value, access_modifier: RIALAccessModifier,
                                             linkage: str):
        """
        Needs to be called with create_in_global_ctor or otherwise the store/load operations are going to fail.
        :param identifier:
        :param value:
        :param access_modifier:
        :param linkage:
        :return:
        """
        if ParserState.module().get_global_safe(identifier) is not None:
            return None

        if self.current_func.name != "global_ctor":
            return None

        if isinstance(value, AllocaInstr):
            variable = self.gen_global(identifier, null(value.type.pointee), value.type.pointee, access_modifier,
                                       linkage, False)
        elif isinstance(value, PointerType):
            variable = self.gen_global(identifier, null(value.pointee.type), value.pointee.type, access_modifier,
                                       linkage, False)
        elif isinstance(value, FormattedConstant) or isinstance(value, AllocaInstr):
            variable = self.gen_global(identifier, null(value.type.pointee), value.type.pointee, access_modifier,
                                       linkage, False)
        elif isinstance(value, Constant):
            variable = self.gen_global(identifier, null(value.type), value.type, access_modifier, linkage, False)
        else:
            variable = self.gen_global(identifier, null(value.type), value.type, access_modifier, linkage, False)

        self.assign_non_constant_global_variable(variable, value)

        return variable

    def declare_variable(self, identifier: str, value) -> Optional[AllocaInstr]:
        variable = self.current_block.get_named_value(identifier)
        if variable is not None:
            return None

        if isinstance(value, AllocaInstr) or isinstance(value, PointerType):
            variable = value
            variable.name = identifier
        elif isinstance(value, CastInstr) and value.opname == "inttoptr":
            variable = self.builder.alloca(value.type.pointee)
            variable.name = identifier
            rial_type = f"{map_llvm_to_type(value.type)}"
            variable.set_metadata('type',
                                  ParserState.module().add_metadata((rial_type,)))
            self.builder.store(self.builder.load(value), variable)
        elif isinstance(value, FormattedConstant):
            variable = self.builder.alloca(value.type.pointee)
            variable.name = identifier
            rial_type = f"{map_llvm_to_type(value.type)}"
            variable.set_metadata('type',
                                  ParserState.module().add_metadata((rial_type,)))
            self.builder.store(self.builder.load(value), variable)
        elif isinstance(value, Constant):
            variable = self.builder.alloca(value.type)
            variable.name = identifier
            rial_type = f"{map_llvm_to_type(value.type)}"
            variable.set_metadata('type',
                                  ParserState.module().add_metadata((rial_type,)))
            self.builder.store(value, variable)
        else:
            variable = self.builder.alloca(value.type)
            variable.name = identifier
            rial_type = f"{map_llvm_to_type(value.type)}"
            variable.set_metadata('type', ParserState.module().add_metadata((rial_type,)))
            self.builder.store(value, variable)

        self.current_block.add_named_value(identifier, variable)

        return variable

    def assign_to_variable(self, identifier: Union[str, Any], value):
        if isinstance(identifier, str):
            variable = self.get_definition(identifier)
        else:
            variable = identifier

        if variable is None:
            return None

        if isinstance(value, AllocaInstr):
            value = self.builder.load(value)
        elif isinstance(value, PointerType) and not isinstance(variable, PointerType):
            value = self.builder.load(value.pointee)

        self.builder.store(value, variable)

        return variable

    def create_block(self, block_name: str, parent: Optional[LLVMBlock] = None,
                     sibling: Optional[LLVMBlock] = None) -> \
            Optional[LLVMBlock]:
        if parent is None and sibling is None and block_name != "entry" and len(self.current_func.basic_blocks) > 0:
            return None

        block = self.builder.append_basic_block(block_name)
        llvmblock = create_llvm_block(block, parent, sibling)

        return llvmblock

    def create_loop(self, base_block_name: str, parent: LLVMBlock):
        (conditional_llvm_block, body_llvm_block, end_llvm_block) = self.create_conditional_block(base_block_name,
                                                                                                  parent)

        self.conditional_block = conditional_llvm_block
        self.end_block = end_llvm_block

        return conditional_llvm_block, body_llvm_block, end_llvm_block,

    def create_switch_blocks(self, base_block_name: str, parent: LLVMBlock, count_of_cases: int,
                             default_case: bool) -> \
            List[LLVMBlock]:
        blocks = list()

        for i in range(0, count_of_cases):
            blocks.append(self.create_block(f"{base_block_name}.case.{i}", parent, None))

        if default_case:
            blocks.append(self.create_block(f"{base_block_name}.default", parent))

        return blocks

    def create_conditional_block(self, base_block_name: str, parent: LLVMBlock) -> Tuple[
        LLVMBlock, LLVMBlock, LLVMBlock]:
        # Create three blocks, one condition, one body, and one after the loop
        conditional_llvm_block = self.create_block(f"{base_block_name}.condition", parent, None)
        body_llvm_block = self.create_block(f"{base_block_name}.body", conditional_llvm_block, None)
        end_llvm_block = self.create_block(f"{base_block_name}.end", None, parent)

        return conditional_llvm_block, body_llvm_block, end_llvm_block,

    def create_conditional_block_with_else(self, base_block_name: str, parent: LLVMBlock):
        (conditional_block, body_block, else_block) = self.create_conditional_block(base_block_name, parent)

        # Transform end block into else block
        else_block.block.name = f"{base_block_name}.else"
        else_block.sibling = None
        else_block.parent = conditional_block

        # Create new end block
        end_block = self.create_block(f"{base_block_name}.if_else.end", None, conditional_block.parent)

        return conditional_block, body_block, else_block, end_block

    def create_jump_if_not_exists(self, target_block: LLVMBlock) -> Optional[Branch]:
        if self.current_block.block.terminator is None:
            return self.builder.branch(target_block.block)

        return None

    def create_conditional_jump(self, condition, true_block: LLVMBlock, false_block: LLVMBlock,
                                true_branch_weight: int = 50, false_branch_weight: int = 50) -> ConditionalBranch:
        # Check if condition is a variable, we need to load that for LLVM
        condition = self.gen_load_if_necessary(condition)

        cbranch = self.builder.cbranch(condition, true_block.block, false_block.block)

        cbranch.set_weights([true_branch_weight, false_branch_weight])

        return cbranch

    def create_jump(self, target_block: LLVMBlock):
        return self.builder.branch(target_block.block)

    def enter_block(self, llvmblock: LLVMBlock):
        self.current_block = llvmblock
        self.builder.position_at_start(self.current_block.block)

    def enter_block_end(self, llvmblock: LLVMBlock):
        self.current_block = llvmblock
        self.builder.position_at_end(self.current_block.block)

    def create_identified_struct(self, name: str, linkage: str,
                                 rial_access_modifier: RIALAccessModifier,
                                 base_llvm_structs: List[RIALIdentifiedStructType],
                                 body: List[RIALVariable]) -> RIALIdentifiedStructType:
        # Build normal struct and switch out with RIALStruct
        struct = ParserState.module().context.get_identified_type(name)
        rial_struct = RIALIdentifiedStructType(struct.context, struct.name, struct.packed)
        ParserState.module().context.identified_types[struct.name] = rial_struct
        struct = rial_struct
        ParserState.module().structs.append(struct)

        # Create metadata definition
        struct_def = StructDefinition(rial_access_modifier)

        # Build body and body definition
        props_def = dict()
        props = list()
        prop_offset = 0
        for deriv in base_llvm_structs:
            for prop in deriv.definition.properties.values():
                props.append(ParserState.map_type_to_llvm(prop[1].rial_type))
                props_def[prop[1].name] = (prop_offset, prop[1])
                prop_offset += 1

            struct_def.base_structs.append(deriv.name)
        for bod in body:
            props.append(ParserState.map_type_to_llvm(bod.rial_type))
            props_def[bod.name] = (prop_offset, bod)
            prop_offset += 1

        struct.set_body(*tuple(props))
        struct.module_name = ParserState.module().name
        self.current_struct = struct

        struct_def.properties = props_def

        # Store def in metadata
        struct.definition = struct_def

        return struct

    def finish_struct(self):
        self.current_struct = None
        self.current_func = None
        self.current_block = None

    def create_function_with_type(self, name: str, canonical_name: str, ty: FunctionType,
                                  linkage: str,
                                  calling_convention: str,
                                  function_def: FunctionDefinition) -> RIALFunction:
        """
        Creates an IR Function with the specified arguments. NOTHING MORE.
        :param canonical_name:
        :param canonical_name:
        :param function_def:
        :param name:
        :param ty:
        :param linkage:
        :param calling_convention:
        :return:
        """

        # Create function with specified linkage (internal -> module only)
        func = RIALFunction(ParserState.module(), ty, name=name, canonical_name=canonical_name)
        func.linkage = linkage
        func.calling_convention = calling_convention
        func.definition = function_def

        # Set argument names
        for i, arg in enumerate(func.args):
            arg.name = function_def.rial_args[i].name

        ParserState.module().rial_functions.append(func)

        return func

    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 finish_current_block(self):
        if self.current_block.block.terminator is None:
            self.builder.position_at_end(self.current_block.block)
            self.builder.ret_void()
        self.current_block = self.current_block.parent

    def finish_current_func(self):
        # If we're in release mode
        # Reorder all possible allocas to the start of the function
        if CompilationManager.config.raw_opts.release:
            entry: Block = self.current_func.entry_basic_block
            pos = entry.instructions.index(
                next((instr for instr in reversed(entry.instructions) if isinstance(instr, AllocaInstr)),
                     entry.terminator))
            allocas: List[Tuple[AllocaInstr, Block]] = list()

            for block in self.current_func.blocks:
                block: Block
                # Skip first block
                if block == entry:
                    continue
                for instr in block.instructions:
                    if isinstance(instr, AllocaInstr):
                        allocas.append((instr, block))
            for instr_block in allocas:
                if instr_block is not None:
                    instr_block[1].instructions.remove(instr_block[0])
                    entry.instructions.insert(pos, instr_block[0])
                    pos += 1
                    # Insert nop if block is empty
                    if len(instr_block[1].instructions) == 0:
                        self.builder.position_before(instr_block[1].terminator)
                        self.gen_no_op()
        self.current_func = None

    def create_return_statement(self, statement=VoidType()):
        if isinstance(statement, VoidType):
            return self.builder.ret_void()

        return self.builder.ret(statement)

    def finish_loop(self):
        self.conditional_block = None
        self.end_block = None

    def create_function_type(self, llvm_return_type: Type, llvm_arg_types: List[Type], var_args: bool):
        return ir.FunctionType(llvm_return_type, tuple(llvm_arg_types), var_arg=var_args)

    @contextmanager
    def create_in_global_ctor(self):
        current_block = self.current_block
        current_func = self.current_func
        current_struct = self.current_struct
        conditional_block = self.conditional_block
        end_block = self.end_block
        pos = self.builder is not None and self.builder._anchor or 0

        func = ParserState.module().get_global_safe('global_ctor')

        if func is None:
            func_type = self.create_function_type(ir.VoidType(), [], False)
            func = self.create_function_with_type('global_ctor', 'global_ctor', func_type, "internal", "ccc",
                                                  FunctionDefinition('void'))
            self.create_function_body(func, [])
            struct_type = ir.LiteralStructType([ir.IntType(32), func_type.as_pointer(), ir.IntType(8).as_pointer()])
            glob_value = ir.Constant(ir.ArrayType(struct_type, 1), [ir.Constant.literal_struct(
                [ir.Constant(ir.IntType(32), 65535), func, NULL])])
            glob_type = ir.ArrayType(struct_type, 1)

            self.gen_global("llvm.global_ctors", glob_value, glob_type, RIALAccessModifier.PRIVATE, "appending", False)
        else:
            self.builder.position_before(func.entry_basic_block.terminator)
            self.current_func = func
            self.current_block = create_llvm_block(func.entry_basic_block)

        self.current_struct = None
        self.conditional_block = None
        self.end_block = None

        yield

        self.finish_current_block()
        self.finish_current_func()

        self.current_block = current_block
        self.current_func = current_func
        self.current_struct = current_struct
        self.conditional_block = conditional_block
        self.end_block = end_block
        self.builder._anchor = pos
        self.builder._block = self.current_block is not None and self.current_block.block or None

        return
Exemplo n.º 13
0
class CodeGenerator(NodeVisitor):
    def __init__(self, symbol_table) -> None:
        # Module is an LLVM construct that contains functions and global variables.
        # In many ways, it is the top-level structure that the LLVM IR uses to contain
        # code. It will own the memory for all of the IR that we generate, which is why
        # the codegen() method returns a raw Value*, rather than a unique_ptr<Value>.
        self.module = Module()

        # The Builder object is a helper object that makes it easy to generate LLVM
        # instructions. Instances of the IRBuilder class template keep track of the
        # current place to insert instructions and has methods to create new instructions.
        self.builder = None
        self.symbol_table = symbol_table
        self.expr_counter = 0
        self.str_counter = 0
        self.bool_counter = 0
        self.printf_counter = 0
        self.func_name = ""
        self.GLOBAL_MEMORY = {}

    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 visit_Program(self, node: Program) -> None:
        """Visit the Block node in AST and call it.

        Args:
            node (Program): Program node (root)
        """

        self.visit(node.block)

    def visit_Block(self, node: Block) -> None:
        """Initializes the method calls according to the nodes represented by the
        variables and compound declarations.

        Args:
            node (Block): the block containing the VAR and BEGIN sections
        """

        for declaration in node.declarations:
            self.visit(declaration)
        self.visit(node.compound_statement)

    def visit_Compound(self, node: Compound) -> None:
        """Central component that coordinates the compound statements (assigments and
        writeln statement) and calls the methods according to their type.

        Args:
            node (Compound): node containing all of the compound statements (assigments
            and writeln statement)
        """

        for child in node.children:
            self.visit(child)

    def visit_Assign(self, node: Assign) -> None:
        """Creates the LLVM IR instructions for the expressions, strings or Booleans
        that are assigned to a variable and adds these to a **auxiliary** GLOBAL MEMORY.

        Args:
            node (Assign): node containing the assignment content
        """

        node_type = type(node.right).__name__
        if isinstance(node.right, String):
            self._create_instruct(node_type)
            self.visit(node.left)
            instruct = self.visit(node.right)
            c_str = self.builder.alloca(instruct.type)
            self.builder.store(instruct, c_str)
            self.builder.ret_void()
        else:
            self._create_instruct(node_type)
            self.visit(node.left)
            instruct = self.visit(node.right)
            self.builder.ret(instruct)

        self.GLOBAL_MEMORY[node.left.value] = instruct

    def visit_Var(self, node: Var) -> VarSymbol:
        """Search for the variable in the Symbol Table and define the Double Type.

        Args:
            node (Var): variable token

        Returns:
            VarSymbol: a variable symbol with updated type
        """

        var_name = node.value
        var_symbol = self.symbol_table.get_token(var_name)
        var_symbol.type = DoubleType()

        return var_symbol

    def visit_Num(self, node: Num) -> Constant:
        """Set the Double Type to a specific number.

        Args:
            node (Num): a token represeting a number (constant)

        Returns:
            Constant: a LLVM IR Constant representing the number.
        """
        return Constant(DoubleType(), float(node.value))

    def visit_BinaryOperator(self, node: BinaryOperator) -> Instruction:
        """Performs the Binary arithmetic operations and returns a LLVM IR Instruction
        according to the operation.

        Args:
            node (BinaryOperator): node containing the variables (or numbers) and the
            arithmetic operators

        Returns:
            Instruction: LLVM arithmetic instruction
        """

        left = self.visit(node.left)
        right = self.visit(node.right)

        if isinstance(left, VarSymbol):
            left_symbol = self.GLOBAL_MEMORY[left.name]
        else:
            left_symbol = left

        if isinstance(right, VarSymbol):
            right_symbol = self.GLOBAL_MEMORY[right.name]
        else:
            right_symbol = right

        if node.operator.type == TokenType.PLUS:
            return self.builder.fadd(left_symbol, right_symbol, "addtmp")
        elif node.operator.type == TokenType.MINUS:
            return self.builder.fsub(left_symbol, right_symbol, "subtmp")
        elif node.operator.type == TokenType.MUL:
            return self.builder.fmul(left_symbol, right_symbol, "multmp")
        elif node.operator.type == TokenType.INTEGER_DIV:
            return self.builder.fdiv(left_symbol, right_symbol, "udivtmp")
        elif node.operator.type == TokenType.FLOAT_DIV:
            return self.builder.fdiv(left_symbol, right_symbol, "fdivtmp")

    def visit_UnaryOperator(self, node: UnaryOperator) -> Constant:
        """Performs Unary Operations according to the arithmetic operator (PLUS and MINUS)
        transforming them into a LLVM IR Constant.

        Args:
            node (UnaryOperator): node containing the variables (or numbers) and the
            arithmetic operators (PLUS and MINUS)

        Returns:
            Constant: a LLVM IR Constant representing the number or variable.
        """

        operator = node.operator.type
        if operator == TokenType.PLUS:
            expression = self.visit(node.expression)
            return Constant(DoubleType(), float(+expression.constant))
        elif operator == TokenType.MINUS:
            expression = self.visit(node.expression)
            return Constant(DoubleType(), float(-expression.constant))

    def visit_String(self, node: String) -> Constant:
        """Converts the literal string to an array of characters.

        Args:
            node (String): a token represeting a string (literal)

        Returns:
            Constant: a constant containing a array of characters
        """

        content = node.value
        return Constant(ArrayType(IntType(8), len(content)),
                        bytearray(content.encode("utf8")))

    def visit_Boolean(self, node: Boolean) -> Constant:
        """Converts the boolean type to an integer (i1) constant.

        Args:
            nodo (Boolean): a token represeting a literal boolean type

        Returns:
            Constant: a constant of type IntType (1) representing the Boolean type:
                1 = True and 0 = False
        """

        if node.token.type == TokenType.FALSE:
            return Constant(IntType(1), 0)
        else:
            return Constant(IntType(1), 1)

    def visit_Writeln(self, node: Writeln) -> None:
        """Converts the contents of the command writeln to LLVM ir code and adds the
        print call to the operating system.

        Args:
            node (Writeln): content passed in the command writeln
        """

        self.printf_counter += 1
        output_operation_type = "%s"

        if isinstance(node.content[0], BinaryOperator):
            self._create_instruct("BinaryOperator", is_printf=True)

        writeln_content = self.visit(node.content[0])

        if isinstance(writeln_content, VarSymbol):
            content = self.GLOBAL_MEMORY[writeln_content.name]
        else:
            content = writeln_content

        content_type = type(content.type).__name__

        if self.builder.block.is_terminated:
            self._create_instruct(typ=content_type, is_printf=True)

        if isinstance(content.type, DoubleType):
            output_operation_type = "%f"

        output_format = f"{output_operation_type}\n\0"
        printf_format = Constant(
            ArrayType(IntType(8), len(output_format)),
            bytearray(output_format.encode("utf8")),
        )

        fstr = GlobalVariable(self.module,
                              printf_format.type,
                              name=f"fstr_{self.printf_counter}")
        fstr.linkage = "internal"
        fstr.global_constant = True
        fstr.initializer = printf_format

        writeln_type = FunctionType(IntType(32), [], var_arg=True)
        writeln = Function(
            self.module,
            writeln_type,
            name=f"printf_{content_type}_{self.printf_counter}",
        )

        body = self.builder.alloca(content.type)
        temp_loaded = self.builder.load(body)
        self.builder.store(temp_loaded, body)

        void_pointer_type = IntType(8).as_pointer()
        casted_arg = self.builder.bitcast(fstr, void_pointer_type)
        self.builder.call(writeln, [casted_arg, body])
        self.builder.ret_void()

    def visit_VarDeclaration(self, node: VarDeclaration) -> None:
        pass

    def visit_Type(self, node: Type) -> None:
        pass

    def visit_Empty(self, node: Empty) -> None:
        pass
Exemplo n.º 14
0
x = builder.alloca(int_type, name='x')
y = builder.alloca(int_type, name='y')
builder.store(Constant(int_type, 4), x)
builder.store(Constant(int_type, 5), y)
t1 = builder.load(x)
t2 = builder.load(x)
t3 = builder.mul(t1, t2)
t4 = builder.load(y)
t5 = builder.load(y)
t6 = builder.mul(t4, t5)
t7 = builder.add(t3, t6)
d = builder.alloca(int_type, name='d')
builder.store(t7, d)
builder.call(_print_int, [builder.load(d)])
builder.ret(Constant(int_type, 37))
print(mod)

def run_jit(module):
    import llvmlite.binding as llvm

    llvm.initialize()
    llvm.initialize_native_target()
    llvm.initialize_native_asmprinter()

    target = llvm.Target.from_default_triple()
    target_machine = target.create_target_machine()
    compiled_mod = llvm.parse_assembly(str(module))
    engine = llvm.create_mcjit_compiler(compiled_mod, target_machine)

    # Look up the function pointer (a Python int)
Exemplo n.º 15
0
class GenerateLLVM(object):
    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 = ChainMap()

        # Dictionary that holds all of the temporary registers created in
        # the intermediate code.
        self.temps = {}

        self.blocks = { }

        self.funcs = { }


        self.current_func = None

        # Initialize the runtime library functions (see below)
        self.declare_runtime_library()


    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_byte'] = Function(self.module,
                                                FunctionType(void_type, [byte_type]),
                                                name="_print_byte")

    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)

        # first collect all the blocks
        for instr in ircode:
            if instr[0] == 'BLK':
                self.blocks[instr[1]] = self.function.append_basic_block(instr[1])


        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()

    # ----------------------------------------------------------------------
    # Opcode implementation.   You must implement the opcodes.  A few
    # sample opcodes have been given to get you started.
    # ----------------------------------------------------------------------

    def emit_FUNCTION_END(self, f_name):

        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.return_var))


    def emit_FUNCTION(self, f_name, arguments, return_type):
        arguments = eval(arguments)
        argtypes = list(map(lambda z: z[0], arguments))

        f_func = Function(self.module,
                          FunctionType(
                            type_lookup[return_type],
                            argtypes
                          ),
                          name=f_name)
        self.current_func = f_func

        self.block = self.current_func.append_basic_block('entry')

        self.return_block = self.current_func.append_basic_block('return')
        self.return_var = self.builder.alloca(type_lookup[return_type], name='return')
        self.funcs[f_name] = self.current_func

    def emit_CALL(self, f_name, sources, target):
        #import pdb
        #pdb.set_trace()
        f = self.funcs[f_name]
        self.temps[target] = self.builder.call(f,
            [self.temps[r] for r in eval(sources)]
        )

    def emit_RETURN(self, var):
        self.builder.ret(self.vars[var])


    # Creation of literal values.  Simply define as LLVM constants.
    def emit_MOVI(self, value, target):
        self.temps[target] = Constant(int_type, value)

    def emit_MOVF(self, value, target):
        self.temps[target] = Constant(float_type, value)

    def emit_MOVB(self, value, target):
        self.temps[target] = Constant(byte_type, value)

    # Allocation of variables.  Declare as global variables and set to
    # a sensible initial value.
    def emit_VARI(self, name):
        var = GlobalVariable(self.module, int_type, name=name)
        var.initializer = Constant(int_type, 0)
        self.vars[name] = var

    def emit_VARF(self, name):
        var = GlobalVariable(self.module, float_type, name=name)
        var.initializer = Constant(float_type, 0)
        self.vars[name] = var

    def emit_VARB(self, name):
        var = GlobalVariable(self.module, byte_type, name=name)
        var.initializer = Constant(byte_type, 0)
        self.vars[name] = var

    def emit_ALLOCI(self, name):
        var = self.builder.alloca(IntType(32), name=name)
        self.builder.store(Constant(int_type, 0), var)
        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 emit_LOADI(self, name, target):
        self.temps[target] = self.builder.load(self.vars[name], target)

    emit_LOADB = emit_LOADI
    emit_LOADF = emit_LOADI

    def emit_STOREI(self, source, target):
        self.builder.store(self.temps[source], self.vars[target])

    emit_STOREF = emit_STOREI
    emit_STOREB = emit_STOREI

    # Binary + operator
    def emit_ADDI(self, left, right, target):
        self.temps[target] = self.builder.add(self.temps[left], self.temps[right], target)

    # Binary + operator
    def emit_ADDF(self, left, right, target):
        self.temps[target] = self.builder.fadd(self.temps[left], self.temps[right], target)

    # Binary - operator
    def emit_SUBI(self, left, right, target):
        self.temps[target] = self.builder.sub(self.temps[left], self.temps[right], target)

    def emit_SUBF(self, left, right, target):
        self.temps[target] = self.builder.fsub(self.temps[left], self.temps[right], target)

    # Binary * operator
    def emit_MULI(self, left, right, target):
        self.temps[target] = self.builder.mul(self.temps[left], self.temps[right], target)

    def emit_MULF(self, left, right, target):
        self.temps[target] = self.builder.fmul(self.temps[left], self.temps[right], target)

    # Binary / operator
    def emit_DIVI(self, left, right, target):
        self.temps[target] = self.builder.sdiv(self.temps[left], self.temps[right], target)

    def emit_DIVF(self, left, right, target):
        self.temps[target] = self.builder.fdiv(self.temps[left], self.temps[right], target)

    def emit_AND(self, left, right, target):
        self.temps[target] = self.builder.and_(self.temps[left], self.temps[right], target)

    def emit_OR(self, left, right, target):
        self.temps[target] = self.builder.or_(self.temps[left], self.temps[right], target)

    def emit_CMPF(self, op, left, right, target):
        temp = self.builder.fcmp_ordered(op, self.temps[left], self.temps[right], 'temp')
        self.temps[target] = self.builder.zext(temp, int_type)

    def emit_CMPI(self, op, left, right, target):
        temp = self.builder.icmp_signed(op, self.temps[left], self.temps[right], 'temp')
        self.temps[target] = self.builder.zext(temp, int_type)

    def cast_to_int(self, value):
        return self.builder.zext(value, int_type)

    def cast_to_byte(self, value):
        return self.builder.trunc(value, IntType(1))

    # Print statements
    def emit_PRINTI(self, source):
        self.builder.call(self.runtime['_print_int'], [self.cast_to_int(self.temps[source])])

    def emit_PRINTF(self, source):
        self.builder.call(self.runtime['_print_float'], [self.temps[source]])

    def emit_PRINTB(self, source):
        self.builder.call(self.runtime['_print_byte'], [self.temps[source]])


    # conditionals and branches

    def emit_CJMP(self, target, true_branch, false_branch):
        self.builder.cbranch(self.cast_to_byte(self.temps[target]), self.blocks[true_branch], self.blocks[false_branch])

    def emit_JMP(self, target):
        self.builder.branch(self.blocks[target])

    def emit_BLK(self, target):
        self.builder.position_at_end(self.blocks[target])
Exemplo n.º 16
0
func = Function(mod,
                FunctionType(IntType(1),
                             [IntType(32), IntType(32)]),
                name='lessthan')

block = func.append_basic_block("entry")
builder = IRBuilder(block)

# The icmp_signed() instruction is used for all comparisons on
# integers.  For floating point, use fcmp_ordered().  The compare operation
# takes an operator expressed as a string such as '<', '>', '==',
# etc.

result = builder.icmp_signed('<', func.args[0], func.args[1])
builder.ret(result)

# ---- Test Code

import llvmlite.binding as llvm

llvm.initialize()
llvm.initialize_native_target()
llvm.initialize_native_asmprinter()

target = llvm.Target.from_default_triple()
target_machine = target.create_target_machine()

compiled_mod = llvm.parse_assembly(str(mod))
engine = llvm.create_mcjit_compiler(compiled_mod, target_machine)
Exemplo n.º 17
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)
Exemplo n.º 18
0
class GenerateLLVM(object):
    def __init__(self):
        self.module = Module('module')
        self.globals = {}
        self.blocks = {}
        self.declare_runtime_library()

    def declare_runtime_library(self):
        self.runtime = {}

        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_byte'] = Function(self.module,
                                               FunctionType(
                                                   void_type, [byte_type]),
                                               name="_print_byte")

    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 get_block(self, block_name):
        block = self.blocks.get(block_name)
        if block is None:
            block = self.function.append_basic_block(block_name)
            self.blocks[block_name] = block

        return block

    def emit_MOV(self, value, target, val_type):
        self.temps[target] = Constant(val_type, value)

    emit_MOVI = partialmethod(emit_MOV, val_type=int_type)
    emit_MOVF = partialmethod(emit_MOV, val_type=float_type)
    emit_MOVB = partialmethod(emit_MOV, val_type=byte_type)

    def emit_VAR(self, name, var_type):
        var = GlobalVariable(self.module, var_type, name=name)
        var.initializer = Constant(var_type, 0)
        self.globals[name] = var

    emit_VARI = partialmethod(emit_VAR, var_type=int_type)
    emit_VARF = partialmethod(emit_VAR, var_type=float_type)
    emit_VARB = partialmethod(emit_VAR, var_type=byte_type)

    def emit_ALLOC(self, name, var_type):
        self.locals[name] = self.builder.alloca(var_type, name=name)

    emit_ALLOCI = partialmethod(emit_ALLOC, var_type=int_type)
    emit_ALLOCF = partialmethod(emit_ALLOC, var_type=float_type)
    emit_ALLOCB = partialmethod(emit_ALLOC, var_type=byte_type)

    def emit_LOADI(self, name, target):
        self.temps[target] = self.builder.load(self.vars[name], name=target)

    emit_LOADF = emit_LOADI
    emit_LOADB = emit_LOADI

    def emit_STOREI(self, source, target):
        self.builder.store(self.temps[source], self.vars[target])

    emit_STOREF = emit_STOREI
    emit_STOREB = emit_STOREI

    def emit_ADDI(self, left, right, target):
        self.temps[target] = self.builder.add(self.temps[left],
                                              self.temps[right],
                                              name=target)

    def emit_ADDF(self, left, right, target):
        self.temps[target] = self.builder.fadd(self.temps[left],
                                               self.temps[right],
                                               name=target)

    def emit_SUBI(self, left, right, target):
        self.temps[target] = self.builder.sub(self.temps[left],
                                              self.temps[right],
                                              name=target)

    def emit_SUBF(self, left, right, target):
        self.temps[target] = self.builder.fsub(self.temps[left],
                                               self.temps[right],
                                               name=target)

    def emit_MULI(self, left, right, target):
        self.temps[target] = self.builder.mul(self.temps[left],
                                              self.temps[right],
                                              name=target)

    def emit_MULF(self, left, right, target):
        self.temps[target] = self.builder.fmul(self.temps[left],
                                               self.temps[right],
                                               name=target)

    def emit_DIVI(self, left, right, target):
        self.temps[target] = self.builder.sdiv(self.temps[left],
                                               self.temps[right],
                                               name=target)

    def emit_DIVF(self, left, right, target):
        self.temps[target] = self.builder.fdiv(self.temps[left],
                                               self.temps[right],
                                               name=target)

    def emit_PRINT(self, source, runtime_name):
        self.builder.call(self.runtime[runtime_name], [self.temps[source]])

    emit_PRINTI = partialmethod(emit_PRINT, runtime_name="_print_int")
    emit_PRINTF = partialmethod(emit_PRINT, runtime_name="_print_float")
    emit_PRINTB = partialmethod(emit_PRINT, runtime_name="_print_byte")

    def emit_CMPI(self, operator, left, right, target):
        if operator == "=":
            operator = "=="

        tmp = self.builder.icmp_signed(operator, self.temps[left],
                                       self.temps[right], 'tmp')
        self.temps[target] = self.builder.zext(tmp, int_type, target)

    def emit_CMPF(self, operator, left, right, target):
        if operator == "=":
            operator = "=="

        tmp = self.builder.fcmp_ordered(operator, self.temps[left],
                                        self.temps[right], 'tmp')
        self.temps[target] = self.builder.zext(tmp, int_type, target)

    emit_CMPB = emit_CMPI

    def emit_AND(self, left, right, target):
        self.temps[target] = self.builder.and_(self.temps[left],
                                               self.temps[right], target)

    def emit_OR(self, left, right, target):
        self.temps[target] = self.builder.or_(self.temps[left],
                                              self.temps[right], target)

    def emit_XOR(self, left, right, target):
        self.temps[target] = self.builder.xor(self.temps[left],
                                              self.temps[right], target)

    def emit_LABEL(self, lbl_name):
        self.block = self.get_block(lbl_name)
        self.builder.position_at_end(self.block)

    def emit_BRANCH(self, dst_label):
        if not self.block.is_terminated:
            self.builder.branch(self.get_block(dst_label))

    def emit_CBRANCH(self, test_target, true_label, false_label):
        true_block = self.get_block(true_label)
        false_block = self.get_block(false_label)
        testvar = self.temps[test_target]
        self.builder.cbranch(self.builder.trunc(testvar, IntType(1)),
                             true_block, false_block)

    def emit_RET(self, register):
        self.builder.store(self.temps[register], self.vars['return'])
        self.builder.branch(self.return_block)

    def emit_CALL(self, func_name, *registers):
        args = [self.temps[r] for r in registers[:-1]]
        target = registers[-1]
        self.temps[target] = self.builder.call(self.globals[func_name], args)
Exemplo n.º 19
0
# 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)
Exemplo n.º 20
0
Arquivo: ir.py Projeto: Delaunay/Kiwi
def ir_return_fun(builder: ir.IRBuilder, args):
    return builder.ret(args[0])