def codegen(self, builder: IRBuilder, ctx: Context): ptr = builder.alloca(ir.ArrayType(ir.IntType(8), len(self.value) + 1)) mapped = list( map(lambda x: ir.Constant(ir.IntType(8), ord(x)), list(self.value))) # La trasformo in un array di char mapped.append(ir.Constant(ir.IntType(8), 0)) # Aggiungo il terminatore value = ir.Constant.literal_array(mapped) builder.store(value, ptr) return builder.bitcast(ptr, ir.PointerType(ir.IntType(8)))
def memcpy(module: ir.Module, scope_stack: list, builder: ir.IRBuilder, dst, src, len, size): size >>= 3 dst = get_pointer(scope_stack, builder, dst) insert_memcpy(module, scope_stack) builder.call(memcpy_func, [ builder.bitcast(dst, char_ptr_type), src, int_type(len), int_type(size), bool_type(0) ])
def impl_construct_dtype_on_stack(context: BaseContext, builder: ir.IRBuilder, sig, args): ty = sig.args[0].dtype_as_type() containing_size = find_size_for_dtype(sig.args[0].dtype) ptr = builder.alloca(ir.IntType(8), containing_size) for i, (name, mem_ty) in enumerate(ty.members): llvm_mem_ty = context.get_value_type(mem_ty) offset = ty.offset(name) v = builder.extract_value(args[1], i) v = context.cast(builder, v, sig.args[1][i], mem_ty) v_ptr_byte = builder.gep(ptr, (ir.Constant(ir.IntType(32), offset),), True) v_ptr = builder.bitcast(v_ptr_byte, llvm_mem_ty.as_pointer()) builder.store(v, v_ptr) return ptr
def memzero(module: ir.Module, scope_stack: list, builder: ir.IRBuilder, dst, len): len *= get_type_size(dst.type.pointee.element) len >>= 3 dst = get_pointer(scope_stack, builder, dst) insert_memset(module, scope_stack) builder.call(memset_func, [ builder.bitcast(dst, char_ptr_type), char_type(0), int_type(len), int_type(4), bool_type(0) ])
def lower_finalize_func(self, lower): """ Lower the generator's finalizer. """ fnty = llvmlite.ir.FunctionType(llvmlite.ir.VoidType(), [self.context.get_value_type(self.gentype)]) function = cgutils.get_or_insert_function( lower.module, fnty, self.gendesc.llvm_finalizer_name) entry_block = function.append_basic_block('entry') builder = IRBuilder(entry_block) genptrty = self.context.get_value_type(self.gentype) genptr = builder.bitcast(function.args[0], genptrty) self.lower_finalize_func_body(builder, genptr)
class CodeGenerator(NodeVisitor): def __init__(self, symbol_table) -> None: # Module is an LLVM construct that contains functions and global variables. # In many ways, it is the top-level structure that the LLVM IR uses to contain # code. It will own the memory for all of the IR that we generate, which is why # the codegen() method returns a raw Value*, rather than a unique_ptr<Value>. self.module = Module() # The Builder object is a helper object that makes it easy to generate LLVM # instructions. Instances of the IRBuilder class template keep track of the # current place to insert instructions and has methods to create new instructions. self.builder = None self.symbol_table = symbol_table self.expr_counter = 0 self.str_counter = 0 self.bool_counter = 0 self.printf_counter = 0 self.func_name = "" self.GLOBAL_MEMORY = {} def _create_instruct(self, typ: str, is_printf: bool = False) -> None: """Create a new Function instruction and attach it to a new Basic Block Entry. Args: typ (str): node type. is_printf (bool, optional): Defaults to False. """ if is_printf or typ in ["String", "ArrayType"]: self.str_counter += 1 self.func_name = f"_{typ}.{self.str_counter}" func_type = FunctionType(VoidType(), []) elif typ == "Boolean": self.bool_counter += 1 self.func_name = f"_{typ}.{self.bool_counter}" func_type = FunctionType(IntType(1), []) else: self.expr_counter += 1 self.func_name = f"_{typ}_Expr.{self.expr_counter}" func_type = FunctionType(DoubleType(), []) main_func = Function(self.module, func_type, self.func_name) bb_entry = main_func.append_basic_block("entry") self.builder = IRBuilder(bb_entry) def visit_Program(self, node: Program) -> None: """Visit the Block node in AST and call it. Args: node (Program): Program node (root) """ self.visit(node.block) def visit_Block(self, node: Block) -> None: """Initializes the method calls according to the nodes represented by the variables and compound declarations. Args: node (Block): the block containing the VAR and BEGIN sections """ for declaration in node.declarations: self.visit(declaration) self.visit(node.compound_statement) def visit_Compound(self, node: Compound) -> None: """Central component that coordinates the compound statements (assigments and writeln statement) and calls the methods according to their type. Args: node (Compound): node containing all of the compound statements (assigments and writeln statement) """ for child in node.children: self.visit(child) def visit_Assign(self, node: Assign) -> None: """Creates the LLVM IR instructions for the expressions, strings or Booleans that are assigned to a variable and adds these to a **auxiliary** GLOBAL MEMORY. Args: node (Assign): node containing the assignment content """ node_type = type(node.right).__name__ if isinstance(node.right, String): self._create_instruct(node_type) self.visit(node.left) instruct = self.visit(node.right) c_str = self.builder.alloca(instruct.type) self.builder.store(instruct, c_str) self.builder.ret_void() else: self._create_instruct(node_type) self.visit(node.left) instruct = self.visit(node.right) self.builder.ret(instruct) self.GLOBAL_MEMORY[node.left.value] = instruct def visit_Var(self, node: Var) -> VarSymbol: """Search for the variable in the Symbol Table and define the Double Type. Args: node (Var): variable token Returns: VarSymbol: a variable symbol with updated type """ var_name = node.value var_symbol = self.symbol_table.get_token(var_name) var_symbol.type = DoubleType() return var_symbol def visit_Num(self, node: Num) -> Constant: """Set the Double Type to a specific number. Args: node (Num): a token represeting a number (constant) Returns: Constant: a LLVM IR Constant representing the number. """ return Constant(DoubleType(), float(node.value)) def visit_BinaryOperator(self, node: BinaryOperator) -> Instruction: """Performs the Binary arithmetic operations and returns a LLVM IR Instruction according to the operation. Args: node (BinaryOperator): node containing the variables (or numbers) and the arithmetic operators Returns: Instruction: LLVM arithmetic instruction """ left = self.visit(node.left) right = self.visit(node.right) if isinstance(left, VarSymbol): left_symbol = self.GLOBAL_MEMORY[left.name] else: left_symbol = left if isinstance(right, VarSymbol): right_symbol = self.GLOBAL_MEMORY[right.name] else: right_symbol = right if node.operator.type == TokenType.PLUS: return self.builder.fadd(left_symbol, right_symbol, "addtmp") elif node.operator.type == TokenType.MINUS: return self.builder.fsub(left_symbol, right_symbol, "subtmp") elif node.operator.type == TokenType.MUL: return self.builder.fmul(left_symbol, right_symbol, "multmp") elif node.operator.type == TokenType.INTEGER_DIV: return self.builder.fdiv(left_symbol, right_symbol, "udivtmp") elif node.operator.type == TokenType.FLOAT_DIV: return self.builder.fdiv(left_symbol, right_symbol, "fdivtmp") def visit_UnaryOperator(self, node: UnaryOperator) -> Constant: """Performs Unary Operations according to the arithmetic operator (PLUS and MINUS) transforming them into a LLVM IR Constant. Args: node (UnaryOperator): node containing the variables (or numbers) and the arithmetic operators (PLUS and MINUS) Returns: Constant: a LLVM IR Constant representing the number or variable. """ operator = node.operator.type if operator == TokenType.PLUS: expression = self.visit(node.expression) return Constant(DoubleType(), float(+expression.constant)) elif operator == TokenType.MINUS: expression = self.visit(node.expression) return Constant(DoubleType(), float(-expression.constant)) def visit_String(self, node: String) -> Constant: """Converts the literal string to an array of characters. Args: node (String): a token represeting a string (literal) Returns: Constant: a constant containing a array of characters """ content = node.value return Constant(ArrayType(IntType(8), len(content)), bytearray(content.encode("utf8"))) def visit_Boolean(self, node: Boolean) -> Constant: """Converts the boolean type to an integer (i1) constant. Args: nodo (Boolean): a token represeting a literal boolean type Returns: Constant: a constant of type IntType (1) representing the Boolean type: 1 = True and 0 = False """ if node.token.type == TokenType.FALSE: return Constant(IntType(1), 0) else: return Constant(IntType(1), 1) def visit_Writeln(self, node: Writeln) -> None: """Converts the contents of the command writeln to LLVM ir code and adds the print call to the operating system. Args: node (Writeln): content passed in the command writeln """ self.printf_counter += 1 output_operation_type = "%s" if isinstance(node.content[0], BinaryOperator): self._create_instruct("BinaryOperator", is_printf=True) writeln_content = self.visit(node.content[0]) if isinstance(writeln_content, VarSymbol): content = self.GLOBAL_MEMORY[writeln_content.name] else: content = writeln_content content_type = type(content.type).__name__ if self.builder.block.is_terminated: self._create_instruct(typ=content_type, is_printf=True) if isinstance(content.type, DoubleType): output_operation_type = "%f" output_format = f"{output_operation_type}\n\0" printf_format = Constant( ArrayType(IntType(8), len(output_format)), bytearray(output_format.encode("utf8")), ) fstr = GlobalVariable(self.module, printf_format.type, name=f"fstr_{self.printf_counter}") fstr.linkage = "internal" fstr.global_constant = True fstr.initializer = printf_format writeln_type = FunctionType(IntType(32), [], var_arg=True) writeln = Function( self.module, writeln_type, name=f"printf_{content_type}_{self.printf_counter}", ) body = self.builder.alloca(content.type) temp_loaded = self.builder.load(body) self.builder.store(temp_loaded, body) void_pointer_type = IntType(8).as_pointer() casted_arg = self.builder.bitcast(fstr, void_pointer_type) self.builder.call(writeln, [casted_arg, body]) self.builder.ret_void() def visit_VarDeclaration(self, node: VarDeclaration) -> None: pass def visit_Type(self, node: Type) -> None: pass def visit_Empty(self, node: Empty) -> None: pass