def codegen(self, builder: IRBuilder, ctx: Context): self.check_children() body = ctx.function.append_basic_block() condition = ctx.function.append_basic_block() end = ctx.function.append_basic_block() ctx.start_scope() builder.branch(body) builder.position_at_end(body) ctx.label = body body_node = self.children[1] body_node.codegen(builder, ctx) # Alla fine faccio il collect collect_fun = ctx.get_function(fn.collect_function) builder.call(collect_fun, []) builder.branch(condition) ctx.end_scope() builder.position_at_end(condition) ctx.label = condition condition_node = self.children[0] last = condition_node.codegen(builder, ctx) cond_res = builder.icmp_unsigned("!=", last, ir.Constant(ir.IntType(8), 0)) builder.cbranch(cond_res, body, end) builder.position_at_end(end) ctx.label = end
def codegen(self, builder: IRBuilder, ctx: Context): self.check_children() l = len(self.children) condition = ctx.function.append_basic_block() true = ctx.function.append_basic_block() false = None if l == 3: false = ctx.function.append_basic_block() end = ctx.function.append_basic_block() if false is None: false = end builder.branch(condition) builder.position_at_end(condition) condition_node = self.children[0] last = condition_node.codegen(builder, ctx) cond_res = builder.icmp_unsigned("!=", last, ir.Constant(ir.IntType(8), 0)) builder.cbranch(cond_res, true, false) ctx.start_scope() builder.position_at_end(true) ctx.label = true true_node = self.children[1] true_node.codegen(builder, ctx) builder.branch(end) ctx.end_scope() if l == 3: ctx.start_scope() builder.position_at_end(false) ctx.label = false false_node = self.children[2] false_node.codegen(builder, ctx) builder.branch(end) ctx.end_scope() builder.position_at_end(end) ctx.label = end # Alla fine faccio il collect collect_fun = ctx.get_function(fn.collect_function) builder.call(collect_fun, [])
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)
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)
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)
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())
# Perform the comparison testvar = builder.icmp_signed('<', a_var, b_var) # Make three blocks then_block = f_func.append_basic_block('then') else_block = f_func.append_basic_block('else') merge_block = f_func.append_basic_block('merge') # Emit the branch instruction builder.cbranch(testvar, then_block, else_block) # 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 ::::")
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]])
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)
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))
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])
# Copy the argument to a local variable. For reasons, that are # complicated, we copy the function argument to a local variable # allocated on the stack (this makes mutations of the variable # easier when looping). n_var = builder.alloca(IntType(32), name='n') builder.store(f_func.args[0], n_var) # Allocate the result variable total_var = builder.alloca(IntType(32), name='total') builder.store(Constant(IntType(32), 0), total_var) # Make a new basic-block for the loop test loop_test_block = f_func.append_basic_block('looptest') builder.branch(loop_test_block) builder.position_at_end(loop_test_block) # Perform the comparison testvar = builder.icmp_signed('>', builder.load(n_var), Constant(IntType(32), 0)) # Make three blocks 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)
def _build_wrapper(self, library, name): """ The LLVM IRBuilder code to create the gufunc wrapper. The *library* arg is the CodeLibrary to which the wrapper should be added. The *name* arg is the name of the wrapper function being created. """ intp_t = self.context.get_value_type(types.intp) fnty = self._wrapper_function_type() wrapper_module = library.create_ir_module('_gufunc_wrapper') func_type = self.call_conv.get_function_type(self.fndesc.restype, self.fndesc.argtypes) fname = self.fndesc.llvm_func_name func = ir.Function(wrapper_module, func_type, name=fname) func.attributes.add("alwaysinline") wrapper = ir.Function(wrapper_module, fnty, name) # The use of weak_odr linkage avoids the function being dropped due # to the order in which the wrappers and the user function are linked. wrapper.linkage = 'weak_odr' arg_args, arg_dims, arg_steps, arg_data = wrapper.args arg_args.name = "args" arg_dims.name = "dims" arg_steps.name = "steps" arg_data.name = "data" builder = IRBuilder(wrapper.append_basic_block("entry")) loopcount = builder.load(arg_dims, name="loopcount") pyapi = self.context.get_python_api(builder) # Unpack shapes unique_syms = set() for grp in (self.sin, self.sout): for syms in grp: unique_syms |= set(syms) sym_map = {} for syms in self.sin: for s in syms: if s not in sym_map: sym_map[s] = len(sym_map) sym_dim = {} for s, i in sym_map.items(): sym_dim[s] = builder.load( builder.gep(arg_dims, [self.context.get_constant(types.intp, i + 1)])) # Prepare inputs arrays = [] step_offset = len(self.sin) + len(self.sout) for i, (typ, sym) in enumerate( zip(self.signature.args, self.sin + self.sout)): ary = GUArrayArg(self.context, builder, arg_args, arg_steps, i, step_offset, typ, sym, sym_dim) step_offset += len(sym) arrays.append(ary) bbreturn = builder.append_basic_block('.return') # Prologue self.gen_prologue(builder, pyapi) # Loop with cgutils.for_range(builder, loopcount, intp=intp_t) as loop: args = [a.get_array_at_offset(loop.index) for a in arrays] innercall, error = self.gen_loop_body(builder, pyapi, func, args) # If error, escape cgutils.cbranch_or_continue(builder, error, bbreturn) builder.branch(bbreturn) builder.position_at_end(bbreturn) # Epilogue self.gen_epilogue(builder, pyapi) builder.ret_void() # Link library.add_ir_module(wrapper_module) library.add_linking_library(self.library)