def visit_While(self, node: While): # Start insertion onto the current block self.builder.position_at_end(block=self.builder.block) # Create basic blocks in the current function to express the control flow cond_bb = ir.Block(self.builder.function, "while.cond") body_bb = ir.Block(self.builder.function, "while.body") end_bb = ir.Block(self.builder.function, "while.end") self.loop_end_blocks.append(end_bb) # Condition: self.builder.branch(target=cond_bb) self.builder.function.basic_blocks.append(cond_bb) self.builder.position_at_start(block=cond_bb) cond_value = self.visit(node.cond) self.builder.cbranch(cond=cond_value, truebr=body_bb, falsebr=end_bb) # Body: self.builder.function.basic_blocks.append(body_bb) self.builder.position_at_start(block=body_bb) self.visit(node.body) self.__terminate(target=cond_bb) # End: self.builder.function.basic_blocks.append(end_bb) self.builder.position_at_start(block=end_bb) self.loop_end_blocks.pop()
def codegen(self: IfElse, generator: LLVMCodeGenerator): builder = generator.builder cmp = self.condition().codegen(generator) trueBlock = builder.function.append_basic_block('true-block') falseBlock = ir.Block(builder.function, 'false-block') mergedBlock = ir.Block(builder.function, 'merged') builder.cbranch(cmp, trueBlock, falseBlock) # true block builder.position_at_start(trueBlock) self.trueBlock().codegen(generator) builder.branch(mergedBlock) trueBlock = generator.builder.block # false block builder.function.basic_blocks.append(falseBlock) builder.position_at_start(falseBlock) self.falseBlock().codegen(generator) builder.branch(mergedBlock) falseBlock = generator.builder.block builder.function.basic_blocks.append(mergedBlock) builder.position_at_start(mergedBlock) return mergedBlock
def generate(self): cond_val = self.condition.generate() then_block = self.builder.function.append_basic_block('then') else_block = ir.Block(self.builder.function, 'else') merge_block = ir.Block(self.builder.function, 'after_if') self.builder.cbranch(cond_val, then_block, else_block) self.builder.position_at_start(then_block) then_val = None for stmt in self.then_body: then_val = stmt.generate() self.builder.branch(merge_block) self.builder.function.basic_blocks.append(else_block) self.builder.position_at_start(else_block) else_val = None for stmt in self.else_body: else_val = stmt.generate() self.builder.branch(merge_block) self.builder.function.basic_blocks.append(merge_block) self.builder.position_at_start(merge_block) phi = self.builder.phi(self.cg.int64, 'if_phi') phi.add_incoming(then_val, then_block) phi.add_incoming(else_val, else_block) return phi
def _codegen_condop(self, node, func_symtab): cond_val = self._codegen(node.children[0], func_symtab) cmpr = self.builder.trunc(cond_val, ir.IntType(1)) then_bb = self.builder.function.append_basic_block('then') else_bb = ir.Block(self.builder.function, 'else') merge_bb = ir.Block(self.builder.function, 'ifcont') self.builder.cbranch(cmpr, then_bb, else_bb) self.builder.position_at_start(then_bb) then_val = self._codegen(node.children[1], func_symtab) self.builder.branch(merge_bb) self.builder.function.basic_blocks.append(else_bb) self.builder.position_at_start(else_bb) else_val = self._codegen(node.children[2], func_symtab) self.builder.branch(merge_bb) self.builder.function.basic_blocks.append(merge_bb) self.builder.position_at_start(merge_bb) result = self.builder.phi(then_val.type) result.add_incoming(then_val, then_bb) result.add_incoming(else_val, else_bb) return result
def _codegen_if_else(self, node, func_symtab): cond_val = self._codegen(node.children[0], func_symtab) cmpr = self.builder.trunc(cond_val, ir.IntType(1)) then_bb = self.builder.function.append_basic_block('then') else_bb = ir.Block(self.builder.function, 'else') merge_bb = ir.Block(self.builder.function, 'ifcont') self.builder.cbranch(cmpr, then_bb, else_bb) self.builder.position_at_start(then_bb) then_val = self._codegen(node.children[1], func_symtab) if not self.builder.block.is_terminated: self.builder.branch(merge_bb) else: cont = self.builder.function.append_basic_block('contbr') self.builder.position_at_start(cont) self.builder.branch(merge_bb) self.builder.function.basic_blocks.append(else_bb) self.builder.position_at_start(else_bb) else_val = self._codegen(node.children[2], func_symtab) if not self.builder.block.is_terminated: self.builder.branch(merge_bb) else: cont = self.builder.function.append_basic_block('contbr') self.builder.position_at_start(cont) self.builder.branch(merge_bb) self.builder.function.basic_blocks.append(merge_bb) self.builder.position_at_start(merge_bb) pass
def visitWhile_stmt(self, ctx: EasyParser.While_stmtContext): """ We generate while cycle code :param ctx: :return: """ testb = ir.Block(self.builder.function, 'test') cycleb = ir.Block(self.builder.function, 'cycle') endb = ir.Block(self.builder.function, 'end') self.builder.branch(testb) # Test while self.builder.function.basic_blocks.append(testb) self.builder.position_at_start(testb) test_val = self.generateCode(ctx.test()) cmp = self.builder.icmp_signed('==', test_val, ir.Constant(self.int1, 0)) self.builder.cbranch(cmp, endb, cycleb) # While block self.builder.function.basic_blocks.append(cycleb) self.builder.position_at_start(cycleb) self.generateCode(ctx.block()) if not self.builder.block.is_terminated: self.builder.branch(testb) # end of cycle self.builder.function.basic_blocks.append(endb) self.builder.position_at_start(endb) return
def codegen(self: While, generator: LLVMCodeGenerator): builder = generator.builder conditionBlock = builder.function.append_basic_block( 'while-condition-block') bodyBlock = ir.Block(builder.function, 'while-body-block') mergedBlock = ir.Block(builder.function, 'while-merged') builder.branch(conditionBlock) # condition block builder.position_at_start(conditionBlock) cmp = self.condition().codegen(generator) builder.cbranch(cmp, bodyBlock, mergedBlock) conditionBlock = generator.builder.block # body block builder.function.basic_blocks.append(bodyBlock) builder.position_at_start(bodyBlock) self.body().codegen(generator) builder.branch(conditionBlock) conditionBlock = generator.builder.block # merged section builder.function.basic_blocks.append(mergedBlock) builder.position_at_start(mergedBlock) return mergedBlock
def _codegen_binary_logical_operator(self, node, func_symtab): if node.leaf == '&&': check1_bb = self.builder.function.append_basic_block('check1') check2_bb = ir.Block(self.builder.function, 'check2') ret_bb = ir.Block(self.builder.function, 'afterLAND') self.builder.branch(check1_bb) self.builder.position_at_start(check1_bb) lhs = self._codegen(node.children[0], func_symtab) check1_bb = self.builder.block cmpr = self.builder.trunc(lhs, ir.IntType(1)) self.builder.cbranch(cmpr, check2_bb, ret_bb) self.builder.function.basic_blocks.append(check2_bb) self.builder.position_at_start(check2_bb) rhs = self._codegen(node.children[1], func_symtab) check2_bb = self.builder.block out = self.builder.and_(lhs, rhs, 'landtmp') self.builder.branch(ret_bb) self.builder.function.basic_blocks.append(ret_bb) self.builder.position_at_start(ret_bb) phi = self.builder.phi(boolean) phi.add_incoming(lhs, check1_bb) phi.add_incoming(out, check2_bb) return phi elif node.leaf == '||': check1_bb = self.builder.function.append_basic_block('check1') check2_bb = ir.Block(self.builder.function, 'check2') ret_bb = ir.Block(self.builder.function, 'afterLOR') self.builder.branch(check1_bb) self.builder.position_at_start(check1_bb) lhs = self._codegen(node.children[0], func_symtab) check1_bb = self.builder.block cmpr = self.builder.trunc(lhs, ir.IntType(1)) self.builder.cbranch(cmpr, ret_bb, check2_bb) self.builder.function.basic_blocks.append(check2_bb) self.builder.position_at_start(check2_bb) rhs = self._codegen(node.children[1], func_symtab) check2_bb = self.builder.block out = self.builder.or_(lhs, rhs, 'landtmp') self.builder.branch(ret_bb) self.builder.function.basic_blocks.append(ret_bb) self.builder.position_at_start(ret_bb) phi = self.builder.phi(boolean) phi.add_incoming(lhs, check1_bb) phi.add_incoming(out, check2_bb) return phi else: lhs = self._codegen(node.children[0], func_symtab) rhs = self._codegen(node.children[1], func_symtab) return rhs
def visitFor_stmt(self, ctx: EasyParser.For_stmtContext): """ We generate code for fo cycle, now it just support integer ineration :param ctx: :return: """ name = str(ctx.NAME()) init = self.generateCode(ctx.expr(0)) end = self.generateCode(ctx.expr(1)) step = self.generateCode(ctx.expr(2)) i_ptr = self.alloc(name, self.int32) self.builder.store(init, i_ptr) checkb = ir.Block(self.builder.function, 'check') cycleb = ir.Block(self.builder.function, 'cycle') endb = ir.Block(self.builder.function, 'end') #Jump tp loop self.builder.branch(checkb) self.funcs_symtab.set(ctx, name, i_ptr) #Check condition self.builder.function.basic_blocks.append(checkb) self.builder.position_at_start(checkb) i_value = self.builder.load(i_ptr) cmp = self.builder.icmp_signed('==', i_value, end, "comp") self.builder.cbranch(cmp, endb, cycleb) #Generate inner block code self.builder.function.basic_blocks.append(cycleb) self.builder.position_at_start(cycleb) self.generateCode(ctx.block()) #step added = self.builder.add(i_value, step) self.builder.store(added, i_ptr) #Jump to loop self.builder.branch(checkb) #endblock self.builder.function.basic_blocks.append(endb) self.builder.position_at_start(endb) self.funcs_symtab.pop(name)
def visit_Assert(self, node: Assert): expr_value = self.visit(node.expr) true_bb = ir.Block(self.builder.function, "assert.true") false_bb = ir.Block(self.builder.function, "assert.false") self.builder.cbranch(cond=expr_value, truebr=true_bb, falsebr=false_bb) # False: self.builder.function.basic_blocks.append(false_bb) self.builder.position_at_start(block=false_bb) self.__printf(f"Assertion failed {node.coord}", "assert.msg") self.builder.branch(target=self.func_exit_block) # True: self.builder.function.basic_blocks.append(true_bb) self.builder.position_at_start(true_bb)
def _codegen_Match(self, node): cond_item = self._codegen(node.cond_item) default = ir.Block(self.builder.function, 'defaultmatch') exit = ir.Block(self.builder.function, 'endmatch') switch_instr = self.builder.switch(cond_item, default) cases = [] exprs = {} values = set() for value, expr in node.match_list: val_codegen = self._codegen(value) if not isinstance(val_codegen, ir.values.Constant): raise CodegenError( f'Match parameter must be a constant, not an expression', value.position) if val_codegen.type != cond_item.type: raise CodegenError( f'Type of match object ("{cond_item.type.describe()}") and match parameter ("{val_codegen.type.describe()}") must be consistent)', value.position) if val_codegen.constant in values: raise CodegenError(f'Match parameter {value} duplicated', value.position) values.add(val_codegen.constant) if expr in exprs: switch_instr.add_case(val_codegen, exprs[expr]) else: n = ir.Block(self.builder.function, 'match') switch_instr.add_case(val_codegen, n) exprs[expr] = n cases.append([n, expr]) for block, expr in cases: self.builder.function.basic_blocks.append(block) self.builder.position_at_start(block) result = self._codegen(expr, False) #if result and not self.builder.block.is_terminated: if not self.builder.block.is_terminated: self.builder.branch(exit) self.builder.function.basic_blocks.append(default) self.builder.position_at_start(default) if node.default: self._codegen(node.default, False) if not self.builder.block.is_terminated: self.builder.branch(exit) self.builder.function.basic_blocks.append(exit) self.builder.position_at_start(exit) return cond_item
def codegen(self: For, generator: LLVMCodeGenerator): builder = generator.builder initBlock = builder.function.append_basic_block('for-init-block') conditionBlock = ir.Block(builder.function, 'for-condition') bodyBlock = ir.Block(builder.function, 'for-body-block') mergedBlock = ir.Block(builder.function, 'for-merged') builder.branch(initBlock) # init block builder.position_at_start(initBlock) left, right = self.range().codegen(generator) name = self.id().name() alloca = builder.alloca(irIntType(), name=name) builder.store(left, alloca) generator.symbolTable.put(name, alloca) builder.branch(conditionBlock) initBlock = generator.builder.block # condition block builder.function.basic_blocks.append(conditionBlock) builder.position_at_start(conditionBlock) current = builder.load(alloca) current = generator.builder.sitofp(current, irDoubleType()) limit = generator.builder.sitofp(right, irDoubleType()) cmp = generator.builder.fcmp_unordered('<=', current, limit, 'for-cmp') builder.cbranch(cmp, bodyBlock, mergedBlock) conditionBlock = generator.builder.block # body block builder.function.basic_blocks.append(bodyBlock) builder.position_at_start(bodyBlock) self.body().codegen(generator) current = builder.load(alloca) current = builder.add(current, ir.Constant(irIntType(), 1)) builder.store(current, alloca) builder.branch(conditionBlock) conditionBlock = generator.builder.block # merged block builder.function.basic_blocks.append(mergedBlock) builder.position_at_start(mergedBlock) return mergedBlock
def visitIf_stmt(self, ctx: EasyParser.If_stmtContext): """ generate if statement code :param ctx: :return: """ cond_val = self.generateCode(ctx.test()) is_else = ctx.block(1) is not None cmp = self.builder.icmp_signed('!=', cond_val, ir.Constant(self.int1, 0), 'notnull') thenb = ir.Block(self.builder.function, 'then') #Do we have else statement? if is_else: elseb = ir.Block(self.builder.function, 'else') mergeb = ir.Block(self.builder.function, 'merge') if is_else: self.builder.cbranch(cmp, thenb, elseb) else: self.builder.cbranch(cmp, thenb, mergeb) #Generate then block self.builder.function.basic_blocks.append(thenb) self.builder.position_at_start(thenb) self.generateCode(ctx.block(0)) # Maybe we add return statement in this block if not self.builder.block.is_terminated: self.builder.branch(mergeb) #Generate else block if is_else: self.builder.function.basic_blocks.append(elseb) self.builder.position_at_start(elseb) self.generateCode(ctx.block(1)) # Maybe we add return statement in this block if not self.builder.block.is_terminated: self.builder.branch(mergeb) # End of if self.builder.function.basic_blocks.append(mergeb) self.builder.position_at_start(mergeb) return
def visitFunction(self, ctx: SoLangParser.FunctionContext): name = ctx.Ident().getText() block_name = 'entry' self.current_function = name self.variables[self.current_function] = {} params = [] paramnames = [] if ctx.paramdefs() is not None: for paramdef in self.visit(ctx.paramdefs()): params.append(self.i64) paramnames.append(paramdef) # register function ftype = ir.FunctionType(self.i64, params) func = ir.Function(self.module, ftype, name=name) entrybb = func.append_basic_block(name=block_name) retbb = ir.Block(entrybb, name='_ret') # retbb = func.append_basic_block(name='ret') self.functions[name] = { 'func': func, 'entrybb': entrybb, 'retbb': retbb } # make a block for func entry self.builder = ir.IRBuilder(entrybb) # define variables for the paramnames for paramname in paramnames: var = self.builder.alloca(self.i64, size=8, name=paramname) self.variables[self.current_function][paramname] = var # create _ret variable var = self.builder.alloca(self.i64, size=8, name='_ret') self.variables[self.current_function]['_ret'] = var # store parameter values to the variables i = 0 for paramname in paramnames: ptr = self.variables[self.current_function][paramname] value = func.args[i] self.builder.store(value, ptr) i += 1 ret = self.visitChildren(ctx) # make a block for ret func.basic_blocks.append(retbb) self.builder = ir.IRBuilder(retbb) ptr = self.variables[self.current_function]['_ret'] value = self.builder.load(ptr, name) self.builder.ret(value) # ret is always None return ret
def codegen_If(self, node): node.show() cond_val, _ = self.codegen(node.cond) cmp = self.builder.fcmp_ordered('!=', cond_val, ir.Constant(ir.DoubleType(), 0.0)) then_bb = self.builder.function.append_basic_block('then') else_bb = ir.Block(self.builder.function, 'else') merge_bb = ir.Block(self.builder.function, 'ifend') self.builder.cbranch(cmp, then_bb, else_bb) self.builder.position_at_start(then_bb) if_return = False else_return = False then_val, _ = self.codegen(node.iftrue) # if return at it we don't branch if not self.return_in_branch: self.builder.branch(merge_bb) else: self.return_in_branch.pop() if_return = True then_bb = self.builder.block # Emit the 'else' part self.builder.function.basic_blocks.append(else_bb) self.builder.position_at_start(else_bb) if node.iffalse: else_val, _ = self.codegen(node.iffalse) else_bb = self.builder.block if not self.return_in_branch: self.builder.branch(merge_bb) else: self.return_in_branch.pop() else_return = True # Emit the merge ('ifend') block if if_return and else_return: pass else: self.builder.function.basic_blocks.append(merge_bb) self.builder.position_at_start(merge_bb) return None, None
def visit_For(self, node: For): # Start insertion onto the current block self.builder.position_at_end(block=self.builder.block) # Create basic blocks in the current function to express the control flow cond_bb = ir.Block(self.builder.function, "for.cond") body_bb = ir.Block(self.builder.function, "for.body") end_bb = ir.Block(self.builder.function, "for.end") self.loop_end_blocks.append(end_bb) # Init: if node.init is not None: self.visit(node.init) # Condition: self.builder.branch(target=cond_bb) self.builder.function.basic_blocks.append(cond_bb) self.builder.position_at_start(block=cond_bb) if node.cond is None: self.builder.branch(target=body_bb) else: cond_value = self.visit(node.cond) self.builder.cbranch(cond=cond_value, truebr=body_bb, falsebr=end_bb) # Body: self.builder.function.basic_blocks.append(body_bb) self.builder.position_at_start(block=body_bb) self.visit(node.body) if node.next is not None: self.visit(node.next) self.__terminate(target=cond_bb) # End: self.builder.function.basic_blocks.append(end_bb) self.builder.position_at_start(block=end_bb) self.loop_end_blocks.pop()
def emit_BR(self, cond, label, else_label='fallthrough'): if label not in self.bbs: target = ir.Block(self.builder.function, label) self.bbs[label] = target else: target = self.bbs[label] fall_bb = self.builder.function.append_basic_block(else_label) self.builder.cbranch(cond, target, fall_bb) self.builder.position_at_start(fall_bb) return
def emit_JMP(self, label): if label not in self.bbs: target = ir.Block(self.builder.function, label) self.bbs[label] = target else: target = self.bbs[label] fall_bb = self.builder.function.append_basic_block('fallthrough') self.builder.branch(target) self.builder.position_at_start(fall_bb) return
def _codegen_for(self, node, func_symtab): if node.children[0].type != 'no_expression': start_val = self._codegen(node.children[0], func_symtab) endcond_bb = self.builder.function.append_basic_block('endcond') loop_bb = ir.Block(self.builder.function, 'loop') step_bb = ir.Block(self.builder.function, 'step') afterloop_bb = ir.Block(self.builder.function, 'afterloop') temp = self.label['_1'] self.label['_1'] = [step_bb, afterloop_bb] self.builder.branch(endcond_bb) self.builder.position_at_start(endcond_bb) if node.children[1].type == 'no_expression': self.builder.branch(loop_bb) else: endcond = self._codegen(node.children[1], func_symtab) cmpr = self.builder.trunc(endcond, ir.IntType(1)) self.builder.cbranch(cmpr, loop_bb, afterloop_bb) self.builder.function.basic_blocks.append(loop_bb) self.builder.position_at_start(loop_bb) self._codegen(node.children[3], func_symtab) if not self.builder.block.is_terminated: self.builder.branch(step_bb) else: cont = self.builder.function.append_basic_block('contbr') self.builder.position_at_start(cont) self.builder.branch(step_bb) self.builder.function.basic_blocks.append(step_bb) self.builder.position_at_start(step_bb) if node.children[2].type != 'no_expression': stepval = self._codegen(node.children[2], func_symtab) self.builder.branch(endcond_bb) self.builder.function.basic_blocks.append(afterloop_bb) self.builder.position_at_end(afterloop_bb) self.label['_1'] = temp pass
def _codegen_IfExprAST(self, node): # Emit comparison value cond_val = self._codegen(node.cond_expr) cmp = self.builder.fcmp_ordered('!=', cond_val, ir.Constant(ir.DoubleType(), 0.0)) # Create basic blocks to express the control flow, with a conditional # branch to either then_bb or else_bb depending on cmp. else_bb and # merge_bb are not yet attached to the function's list of BBs because # if a nested IfExpr is generated we want to have a reasonably nested # order of BBs generated into the function. then_bb = self.builder.function.append_basic_block('then') else_bb = ir.Block(self.builder.function, 'else') merge_bb = ir.Block(self.builder.function, 'ifcont') self.builder.cbranch(cmp, then_bb, else_bb) # Emit the 'then' part self.builder.position_at_start(then_bb) then_val = self._codegen(node.then_expr) self.builder.branch(merge_bb) # Emission of then_val could have modified the current basic block. To # properly set up the PHI, remember which block the 'then' part ends in. then_bb = self.builder.block # Emit the 'else' part self.builder.function.basic_blocks.append(else_bb) self.builder.position_at_start(else_bb) else_val = self._codegen(node.else_expr) # Emission of else_val could have modified the current basic block. else_bb = self.builder.block self.builder.branch(merge_bb) # Emit the merge ('ifcnt') block self.builder.function.basic_blocks.append(merge_bb) self.builder.position_at_start(merge_bb) phi = self.builder.phi(ir.DoubleType(), 'iftmp') phi.add_incoming(then_val, then_bb) phi.add_incoming(else_val, else_bb) return phi
def visit_ForExpr(self, expr): start_val = self.visit(expr.start) alloca = self.add_alloca(expr.name, _llvm_ty(expr.decl_ty)) ok = True if alloca and start_val: self.builder.store(start_val, alloca) else: ok = False self.decl_values[expr] = alloca # generate loop for_block = self.builder.append_basic_block('for') exit_block = ir.Block(self.builder.function, name='for.exit') self.builder.branch(for_block) self.builder.position_at_end(for_block) # generate loop test end_val = self.visit(expr.end) if end_val: end_val_bool = self.builder.fcmp_ordered( '==', end_val, ir.Constant(ir.DoubleType(), 0.0)) else: end_val_bool = ir.Constant(ir.IntType(1), 0) ok = False with self.builder.if_then(end_val_bool): self.builder.branch(exit_block) # generate loop body if not self.visit(expr.body): ok = False # generate increment if alloca: indvar_val = self.builder.load(alloca) step_val = self.visit(expr.step) new_indvar_val = self.builder.fadd(indvar_val, step_val, expr.name) self.builder.store(new_indvar_val, alloca) # branch to loop entry self.builder.branch(for_block) self.builder.function.blocks.append(exit_block) self.builder.position_at_end(exit_block) if not ok: return None return ir.Constant(ir.DoubleType(), 0.0)
def visit_if_else(self, if_else): meta_cond = if_else.cond_expr.visit(self) val = self.env.builder.load(meta_cond.ir_value) meta_type = self.env.scope.get_type('bool') cond = self.env.builder.icmp_signed('!=', ir.Constant(meta_type.ir_type, 0), val) then_bb = self.env.builder.function.append_basic_block('then') else_bb = ir.Block(self.env.builder.function, 'else') merge_bb = ir.Block(self.env.builder.function, 'ifcont') if if_else.has_else: self.env.builder.cbranch(cond, then_bb, else_bb) else: self.env.builder.cbranch(cond, then_bb, merge_bb) # then self.env.builder.position_at_start(then_bb) if_else.then_block.visit(self) # save block may have been modified then_bb = self.env.builder.block self.env.builder.branch(merge_bb) if if_else.has_else: # else self.env.builder.function.basic_blocks.append(else_bb) self.env.builder.position_at_start(else_bb) if_else.else_block.visit(self) else_bb = self.env.builder.block # block may have been modified else_bb = self.env.builder.block self.env.builder.branch(merge_bb) self.env.builder.function.basic_blocks.append(merge_bb) self.env.builder.position_at_start(merge_bb)
def _codegen_If(self, node): # Emit comparison value cond_val = self._codegen(node.cond_expr) cmp = self.builder.fcmp_ordered('!=', cond_val, irdouble(0.0), 'notnull') # Create basic blocks to express the control flow then_bb = ir.Block(self.builder.function, 'then') else_bb = ir.Block(self.builder.function, 'else') merge_bb = ir.Block(self.builder.function, 'endif') # branch to either then_bb or else_bb depending on cmp. self.builder.cbranch(cmp, then_bb, else_bb) # Emit the 'then' part self.builder.function.basic_blocks.append(then_bb) self.builder.position_at_start(then_bb) then_val = self._codegen(node.then_expr) self.builder.branch(merge_bb) # Emission of then_val could have generated a new basic block # (and thus modified the current basic block). # To properly set up the PHI, remember which block the 'then' part ends in. then_bb = self.builder.block # Emit the 'else' part self.builder.function.basic_blocks.append(else_bb) self.builder.position_at_start(else_bb) else_val = self._codegen(node.else_expr) self.builder.branch(merge_bb) else_bb = self.builder.block # Emit the merge block self.builder.function.basic_blocks.append(merge_bb) self.builder.position_at_start(merge_bb) phi = self.builder.phi(ir.DoubleType(), 'ifval') phi.add_incoming(then_val, then_bb) phi.add_incoming(else_val, else_bb) return phi
def block(self, block: Block, depth=0) -> Any: trace(depth, 'block') blk = ir.Block(self.parent) old = self.builder old_p = self.parent self.builder = ir.IRBuilder(block) for expr in block.expressions: self.visit(expr, depth + 1) self.builder = old self.parent = old_p return blk
def visit_FuncDef(self, node: FuncDef): _ass(isinstance(node.spec, Type)) _ass(isinstance(node.decl, Decl)) _ass(isinstance( node.body, Compound)) # TODO double check for empty/single-line functions # Reset the current function symbol table self.func_symtab = {} # Generate an ir.Function from the prototype (i.e. the function declaration) fn: ir.Function = self.visit(node.decl) # Create a new basic block to start insertion into func_entry_block = fn.append_basic_block(name="entry") self.builder = ir.IRBuilder(block=func_entry_block) if fn.ftype.return_type is not UCLLVM.Type.Void: self.func_return_addr = self.__alloca(var_name="retval", ir_type=fn.ftype.return_type) self.builder.store(value=UCLLVM.Const.zero(fn.ftype.return_type), ptr=self.func_return_addr) # Add the function arguments to the stack (and the symbol table) for arg in fn.args: arg_addr = self.__alloca(var_name=arg.name, ir_type=arg.type) self.builder.store(value=arg, ptr=arg_addr) self.func_exit_block = ir.Block(self.builder.function, "exit") # Finish off generating the function self.visit(node.body) # Return self.__terminate(target=self.func_exit_block) self.builder.function.basic_blocks.append(self.func_exit_block) self.builder.position_at_start(block=self.func_exit_block) if self.func_return_addr is not None: ret_value = self.builder.load(self.func_return_addr, "retval") self.builder.ret(ret_value) else: self.builder.ret_void() self.func_exit_block = None self.func_return_addr = None self.builder = None return fn
def visit_If(self, node: If): # Start insertion onto the current block self.builder.position_at_end(block=self.builder.block) # Create basic blocks in the current function to express the control flow then_bb = ir.Block(self.builder.function, "if.then") if node.ifelse is not None: else_bb = ir.Block(self.builder.function, "if.else") end_bb = ir.Block(self.builder.function, "if.end") # Condition: cond_value = self.visit(node.cond) self.builder.cbranch( cond=cond_value, truebr=then_bb, falsebr=end_bb if node.ifelse is None else else_bb) # Then: self.builder.function.basic_blocks.append(then_bb) self.builder.position_at_start(block=then_bb) self.visit(node.ifthen) self.__terminate(target=end_bb) # Else: if node.ifelse is not None: self.builder.function.basic_blocks.append(else_bb) self.builder.position_at_start(block=else_bb) self.visit(node.ifelse) self.__terminate(target=end_bb) # End: self.builder.function.basic_blocks.append(end_bb) self.builder.position_at_start(block=end_bb)
def test_attributes(self): func = self.function() block = ir.Block(parent=func, name='start') self.assertIs(block.parent, func) self.assertFalse(block.is_terminated)
def _codegen_For(self, node): # Output this as: # ... # start = startexpr # goto loopcond # loopcond: # variable = phi [start, loopheader], [nextvariable, loopbody] # step = stepexpr (or variable + 1) # nextvariable = step # endcond = endexpr # br endcond, loopbody, loopafter # loopbody: # bodyexpr # jmp loopcond # loopafter: # return variable # Define blocks loopcond_bb = ir.Block(self.builder.function, 'loopcond') loopbody_bb = ir.Block(self.builder.function, 'loopbody') loopafter_bb = ir.Block(self.builder.function, 'loopafter') # ########### # loop header ############# # Allocate the variable on the stack var_addr = self._alloca(node.id_name) # Evaluate the starting value for the counter and store it start_val = self._codegen(node.start_expr) self.builder.store(start_val, var_addr) # Save the current block to tell the loop cond where we are coming from loopheader_bb = self.builder.block # Jump to loop cond self.builder.branch(loopcond_bb) ########### # loop cond ########### self.builder.function.basic_blocks.append(loopcond_bb) self.builder.position_at_start(loopcond_bb) # Set the symbol table to to reach de local counting variable. # If it shadows an existing variable, save it before and restore it later. oldval = self.func_symtab.get(node.id_name) self.func_symtab[node.id_name] = var_addr # Compute the end condition endcond = self._codegen(node.end_expr) cmp = self.builder.fcmp_ordered('!=', endcond, irdouble(0.0), 'loopcond') # Goto loop body if condition satisfied, otherwise, exit. self.builder.cbranch(cmp, loopbody_bb, loopafter_bb) ############ # loop body ############ self.builder.function.basic_blocks.append(loopbody_bb) self.builder.position_at_start(loopbody_bb) # Emit the body of the loop. # Note that we ignore the value computed by the body. body_val = self._codegen(node.body) # If the step is unknown, make it increment by 1 if node.step_expr is None: node.step_expr = Binary("+", Variable(node.id_name), Number(1.0)) # Evaluate the step and update the counter nextval = self._codegen(node.step_expr) self.builder.store(nextval, var_addr) # Goto loop cond self.builder.branch(loopcond_bb) ############# # loop after ############# # New code will be inserted into a new block self.builder.function.basic_blocks.append(loopafter_bb) self.builder.position_at_start(loopafter_bb) # Remove the loop variable from the symbol table; # if it shadowed an existing variable, restore that. if oldval is None: del self.func_symtab[node.id_name] else: self.func_symtab[node.id_name] = oldval # The 'for' expression returns the last value of the counter return self.builder.load(var_addr)
def jit(pgm): module = ir.Module(name="brainfuck_jit") # declare the getchar/putchar C functions # they are available in the cpython runtime so don't need any linking getchar = ir.Function(module, ir.FunctionType(int32, []), name="getchar") putchar = ir.Function(module, ir.FunctionType(ir.VoidType(), [int32]), name="putchar") memset = module.declare_intrinsic('llvm.memset', [int8ptr, int32]) # our program bfrun = ir.Function(module, ir.FunctionType(ir.VoidType(), []), name="bfrun") block = bfrun.append_basic_block(name="entry") builder = ir.IRBuilder(block) # initialise the brainfuck memory space: the "tape" tape = builder.alloca(int32, 30000, name="tape") tape_ptr = builder.ptrtoint(tape, int64) tape8 = builder.inttoptr(tape_ptr, int8ptr) builder.call(memset, [tape8, int8(0), int32(4 * 30000), bit(0)]) # current position on the "tape" idx = builder.alloca(int32, name="idx") builder.store(int32(0), idx) # a stack to store nested block bracket_blocks = [] # generate llvm-ir for each opcode in the program for opcode in pgm: if opcode == '>': # idx++ idxval = builder.load(idx, "idxval") inc = builder.add(idxval, int32(1), name="inc") builder.store(inc, idx) elif opcode == '<': # idx-- idxval = builder.load(idx, "idxval") dec = builder.add(idxval, int32(-1), name="dec") builder.store(dec, idx) elif opcode == '+': # tape[idx] +=1 idxval = builder.load(idx, "idxval") cell = builder.gep(tape, [idxval], name="cell") data = builder.load(cell, 'data') incr = builder.add(data, int32(1), name="incr") builder.store(incr, cell) elif opcode == '-': # tape[idx] -=1 idxval = builder.load(idx, "idxval") cell = builder.gep(tape, [idxval], name="cell") data = builder.load(cell, 'data') decr = builder.add(data, int32(-1), name="decr") builder.store(decr, cell) elif opcode == '[': # while tape[idx] != 0: idxval = builder.load(idx, "idxval") cell = builder.gep(tape, [idxval], name="cell") data = builder.load(cell, 'data') cmp = builder.icmp_signed('!=', data, int32(0)) inner = bfrun.append_basic_block(name='inner') after = ir.Block(parent=bfrun, name='after') builder.cbranch(cmp, inner, after) # next instructions are written inside the loop now builder.position_at_start(inner) # save the "after" block for later bracket_blocks.append((inner, after)) elif opcode == ']': idxval = builder.load(idx, "idxval") cell = builder.gep(tape, [idxval], name="cell") data = builder.load(cell, 'data') cmp = builder.icmp_signed('!=', data, int32(0)) # blocks saved from matching '[' inner, after = bracket_blocks.pop() builder.cbranch(cmp, inner, after) bfrun.blocks.append(after) builder.position_at_start(after) elif opcode == '.': idxval = builder.load(idx, "idxval") cell = builder.gep(tape, [idxval], name="cell") data = builder.load(cell, 'data') builder.call(putchar, [data]) elif opcode == ',': data = builder.call(getchar, []) idxval = builder.load(idx, "idxval") cell = builder.gep(tape, [idxval], name="cell") builder.store(data, cell) builder.ret_void() return module
def basic_block(self, func, name): block = BasicBlock() block.block = ir.Block(parent=func, name=name) if self.basic_blocks: self.basic_blocks[-1].blocks.append(block) return block