def constructor_call(self, tree: Tree): nodes = tree.children name = nodes[0] struct = ParserState.find_struct(name) if struct is None: log_fail(f"Instantiation of unknown type {name}") arguments = list() instantiated = self.llvmgen.builder.alloca(struct) instantiated = RIALVariable(name, name, instantiated) arguments.append(instantiated) arguments.extend(self.transform_helper(nodes[1])) llvm_args = [arg.backing_value for arg in arguments] constructor_name = mangle_function_name( "constructor", [arg.llvm_type for arg in arguments], name) try: call_instr = self.llvmgen.gen_function_call([constructor_name], llvm_args) if call_instr is None: log_fail( f"Failed to generate call to function {constructor_name}") return NULL return instantiated except IndexError: log_fail("Missing argument in function call") return NULL
def math(self, tree: Tree): nodes = tree.children left: RIALVariable = self.transform_helper(nodes[0]) op = nodes[1].type right: RIALVariable = self.transform_helper(nodes[2]) if left.rial_type != right.rial_type: raise TypeError(str(left), op, str(right)) left_val = left.raw_backing_value right_val = right.raw_backing_value result = None if op == "PLUS": result = self.llvmgen.gen_addition(left_val, right_val) elif op == "MINUS": result = self.llvmgen.gen_subtraction(left_val, right_val) elif op == "MUL": result = self.llvmgen.gen_multiplication(left_val, right_val) elif op == "DIV": result = self.llvmgen.gen_division(left_val, right_val) return RIALVariable(f"{left}{op}{right}", left.rial_type, result)
def external_function_decl(self, tree: Tree): nodes = tree.children access_modifier: RIALAccessModifier = nodes[0].access_modifier unsafe: bool = nodes[0].unsafe linkage = "external" calling_convention = "ccc" return_type = nodes[1].value name = nodes[2].value # External functions cannot be declared in a struct if self.llvmgen.current_struct is not None: log_fail(f"External function {name} cannot be declared inside a class!") raise Discard() args: List[RIALVariable] = list() var_args = False i = 3 while i < len(nodes): if var_args is True: log_fail("PARAMS must be last in arguments") break if nodes[i].type == "PARAMS": var_args = True i += 1 if nodes[i].type == "IDENTIFIER": arg_type = nodes[i].value i += 1 arg_name = nodes[i].value if var_args: arg_name += "..." args.append(RIALVariable(arg_name, arg_type, None)) i += 1 if not unsafe and not self.llvmgen.currently_unsafe: raise PermissionError("Can only declare external functions in unsafe blocks or as unsafe functions.") # Map RIAL args to llvm arg types llvm_args = [arg.llvm_type for arg in args if not arg.name.endswith("...")] # Hasn't been declared previously, redeclare the function type here llvm_return_type = ParserState.map_type_to_llvm(return_type) func_type = self.llvmgen.create_function_type(llvm_return_type, llvm_args, var_args) # Create the actual function in IR func = self.llvmgen.create_function_with_type(name, name, func_type, linkage, calling_convention, FunctionDefinition(return_type, access_modifier, args, "", unsafe)) # Update backing values for i, arg in enumerate(func.args): args[i].backing_value = arg raise Discard()
def sizeof(self, tree: Tree): nodes = tree.children variable: Optional[RIALVariable] = self.transform_helper(nodes[0]) name = "" # If it's not a variable, extract the name manually since it's the name of a type to get the size of if variable is None: name = nodes[0].children[0].value ty = ParserState.map_type_to_llvm_no_pointer(name) size = Int32(get_size(ty)) elif isinstance(variable.backing_value, ir.GEPInstr) or (variable.is_array and isinstance( variable.raw_llvm_type, ir.PointerType)): # This is worst case as it cannot be optimized away. base = self.llvmgen.builder.ptrtoint( self.llvmgen.builder.gep(variable.backing_value, [ir.Constant(ir.IntType(32), 0)]), ir.IntType(32)) val = self.llvmgen.builder.ptrtoint( self.llvmgen.builder.gep(variable.backing_value, [ir.Constant(ir.IntType(32), 1)]), ir.IntType(32)) size = self.llvmgen.builder.sub(val, base) name = "unknown" elif variable.is_array: if variable.is_constant_sized_array: ty = variable.raw_llvm_type size = get_size(ty.element) * ty.count size = Int32(size) name = f"{ty.element}[{ty.count}]" else: value = variable.raw_backing_value size = get_size(value.type.element) size = self.llvmgen.gen_multiplication( Int32(size), self.llvmgen.gen_load_if_necessary(value.type.count)) name = f"{value.type.element}[{value.type.count}]" elif variable.raw_llvm_type == variable.raw_backing_value.type: size = Int32(get_size(variable.raw_llvm_type)) name = f"{variable.rial_type}" else: # This is worst case as it cannot be optimized away. base = self.llvmgen.builder.ptrtoint( self.llvmgen.builder.gep(variable.backing_value, [ir.Constant(ir.IntType(32), 0)]), ir.IntType(32)) val = self.llvmgen.builder.ptrtoint( self.llvmgen.builder.gep(variable.backing_value, [ir.Constant(ir.IntType(32), 1)]), ir.IntType(32)) size = self.llvmgen.builder.sub(val, base) name = "unknown" return RIALVariable(f"sizeof_{name}", "Int32", size)
def create_function_body(self, func: RIALFunction, rial_arg_types: List[str]): self.current_func = func # Create entry block bb = func.append_basic_block("entry") llvm_bb = create_llvm_block(bb) self.current_block = llvm_bb if self.builder is None: self.builder = IRBuilder(bb) self.builder.position_at_start(bb) # Allocate new variables for the passed arguments for i, arg in enumerate(func.args): # Don't copy variables that are a pointer if isinstance(arg.type, PointerType): variable = RIALVariable(arg.name, rial_arg_types[i], arg) else: allocated_arg = self.builder.alloca(arg.type) self.builder.store(arg, allocated_arg) variable = RIALVariable(arg.name, rial_arg_types[i], allocated_arg) self.current_block.add_named_value(arg.name, variable)
def function_call(self, tree: Tree): nodes = tree.children full_function_name: str function_name: str # Function call specialisation if isinstance(nodes[0], Tree): return self.visit(nodes[0]) function_name = nodes[0].value arguments: List[RIALVariable] = self.transform_helper(nodes[1]) arg_types = [arg.llvm_type for arg in arguments] try: call_instr = self.llvmgen.gen_function_call([ mangle_function_name(function_name, arg_types), function_name ], [arg.backing_value for arg in arguments]) if call_instr is None: log_fail( f"Failed to generate call to function {function_name}") return NULL rial_func = call_instr.callee if isinstance(rial_func, RIALFunction): return RIALVariable(f"{rial_func.name}_call", rial_func.definition.rial_return_type, call_instr) return RIALVariable(f"{rial_func.name}_call", "Unknown", call_instr) except IndexError: log_fail("Missing argument in function call") return NULL
def array_access(self, tree: Tree): nodes = tree.children variable: RIALVariable = self.transform_helper(nodes[0]) index: RIALVariable = self.transform_helper(nodes[1]) if not variable.is_array: raise TypeError(variable) # Check if an array or a pointer to the first element of an array if variable.is_pointer and not isinstance( variable.backing_value.type.pointee, ir.ArrayType): indices = [index.raw_backing_value] else: indices = [Int32(0), index.raw_backing_value] var = self.llvmgen.builder.gep(variable.backing_value, indices) return RIALVariable(f"{variable.name}[{index}]", variable.array_type, var)
def array_constructor(self, tree: Tree): nodes = tree.children name = nodes[0].value number: RIALVariable = self.transform_helper(nodes[1]) if isinstance(number.raw_backing_value, ir.Constant): number = number.raw_backing_value.constant else: number = number.raw_backing_value ty = ParserState.map_type_to_llvm_no_pointer(name) arr_type = ir.ArrayType(ty, number) allocated = self.llvmgen.builder.alloca(arr_type) return RIALVariable( f"array_{name}[{number}]", f"{name}[{isinstance(number, int) and f'{number}' or ''}]", allocated)
def gen_global(self, name: str, value: Optional[ir.Constant], ty: Type, access_modifier: RIALAccessModifier, linkage: str, constant: bool): rial_variable = ParserState.module().get_rial_variable(name) if rial_variable is not None: return rial_variable.backing_value glob = ir.GlobalVariable(ParserState.module(), ty, name=name) glob.linkage = linkage glob.global_constant = constant if value is not None: glob.initializer = value rial_variable = RIALVariable(name, map_llvm_to_type(ty), glob, access_modifier) ParserState.module().global_variables.append(rial_variable) return glob
def get_exact_definition(self, identifier: str): identifiers = identifier.split('.') variable = None for ident in identifiers: variable = self._get_by_identifier(ident, variable) # Search for the full name if variable is None: variable = self._get_by_identifier(identifier, variable) if variable is None: return None if isinstance(variable, RIALFunction): return variable if isinstance(variable, RIALVariable): return variable return RIALVariable(identifier, map_llvm_to_type(variable.type.pointee), variable)
def equal(self, tree: Tree): nodes = tree.children left: RIALVariable = self.transform_helper(nodes[0]) comparison = nodes[1].value right: RIALVariable = self.transform_helper(nodes[2]) if left.is_pointer and right.is_pointer and not (left.is_primitive and right.is_primitive): result = self.llvmgen.gen_comparison(comparison, left.backing_value, right.backing_value) else: result = self.llvmgen.gen_comparison(comparison, left.raw_backing_value, right.raw_backing_value) if result is None: print(comparison) print(left.backing_value.type) print(right.backing_value.type) return RIALVariable(comparison, "Int1", result)
def shorthand_if(self, tree: Tree): nodes = tree.children name = self.llvmgen.current_block.block.name (conditional_block, body_block, else_block, end_block) = \ self.llvmgen.create_conditional_block_with_else(name, self.llvmgen.current_block) # Create condition self.llvmgen.create_jump(conditional_block) self.llvmgen.enter_block(conditional_block) cond: RIALVariable = self.transform_helper(nodes[0]) self.llvmgen.create_conditional_jump(cond.raw_backing_value, body_block, else_block) # Create body self.llvmgen.enter_block(body_block) true_value: RIALVariable = self.transform_helper(nodes[1]) # Jump out of body self.llvmgen.create_jump_if_not_exists(end_block) # Create else self.llvmgen.enter_block(else_block) false_value: RIALVariable = self.transform_helper(nodes[2]) # Jump out of else self.llvmgen.create_jump_if_not_exists(end_block) # Leave conditional block self.llvmgen.enter_block(end_block) # PHI the values phi = self.llvmgen.builder.phi(true_value.llvm_type) phi.add_incoming(true_value.backing_value, body_block.block) phi.add_incoming(false_value.backing_value, else_block.block) return RIALVariable("phi", true_value.rial_type, phi)
def cast(self, tree: Tree): nodes = tree.children ty = ParserState.map_type_to_llvm_no_pointer(nodes[0]) value: RIALVariable = self.transform_helper(nodes[1]) if is_builtin_type(nodes[0]): # Simple cast for primitive to primitive if value.is_primitive: cast_function = get_casting_function(value.raw_llvm_type, ty) if hasattr(self.llvmgen.builder, cast_function): casted = getattr(self.llvmgen.builder, cast_function)(value.raw_backing_value, ty) else: raise TypeError( f"No casting function found for casting {value.rial_type} to {nodes[0]}" ) else: # Casting type to integer ("pointer") (unsafe!) with only_allowed_in_unsafe(): casted = self.llvmgen.builder.ptrtoint( value.backing_value, ty) else: # Casting integer to type (unsafe!) if value.is_primitive: with only_allowed_in_unsafe(): casted = self.llvmgen.builder.inttoptr( value.raw_backing_value, ty.as_pointer()) else: # Simple type cast casted = self.llvmgen.builder.bitcast(value.backing_value, ty.as_pointer()) return RIALVariable(f"cast_{value.rial_type}_to_{nodes[0]}", nodes[0], casted)
def variable_decl(self, tree: Tree): nodes = tree.children identifier = nodes[0].value value: RIALVariable = self.transform_helper(nodes[2]) if isinstance(value, RIALFunction): value: RIALFunction ty = value.function_type raw_value = self.llvmgen.builder.load(value) rial_type = str(value.function_type).replace("i8*", "CString") else: ty: ir.Type = value.raw_llvm_type raw_value = value.raw_backing_value rial_type = value.rial_type if not value.rial_type == "CString" and isinstance( ty, ir.ArrayType): ty: ir.ArrayType if isinstance(raw_value.type, ir.ArrayType): ty = raw_value.type else: ty = ty.element.as_pointer() raw_value = self.llvmgen.builder.gep( value.backing_value, [Int32(0), Int32(0)]) if isinstance(raw_value.type, ir.IntType) and raw_value.type.width == 8: print(raw_value, value) variable = self.llvmgen.builder.alloca(ty) variable.name = identifier self.llvmgen.builder.store(raw_value, variable) variable = RIALVariable(identifier, rial_type, variable) self.llvmgen.current_block.add_named_value(identifier, variable) return variable
def extension_function_decl(self, tree: Tree): nodes = tree.children access_modifier: RIALAccessModifier = nodes[0].access_modifier unsafe: bool = nodes[0].unsafe linkage = access_modifier.get_linkage() calling_convention = self.default_cc return_type = nodes[1].value name = nodes[2].value # Extension functions cannot be declared inside other classes. if self.llvmgen.current_struct is not None: log_fail(f"Extension function {name} cannot be declared inside another class!") raise Discard() if not self.mangling: log_fail(f"Extension function {name} does not qualify for no mangling.") raise Discard() args: List[RIALVariable] = list() this_arg = map_shortcut_to_type(nodes[3].value) has_body = False args.append(RIALVariable(nodes[4].value, nodes[3].value, None)) i = 5 while i < len(nodes): if not isinstance(nodes[i], Token): has_body = True break if nodes[i].type == "IDENTIFIER": arg_type = nodes[i].value i += 1 arg_name = nodes[i].value args.append(RIALVariable(arg_name, arg_type, None)) i += 1 else: break # Map RIAL args to llvm arg types llvm_args = [arg.llvm_type for arg in args if not arg.name.endswith("...")] full_function_name = mangle_function_name(name, llvm_args, this_arg) full_function_name = f"{ParserState.module().name}:{full_function_name}" # Hasn't been declared previously, redeclare the function type here llvm_return_type = ParserState.map_type_to_llvm(return_type) func_type = self.llvmgen.create_function_type(llvm_return_type, llvm_args, False) # Create the actual function in IR func = self.llvmgen.create_function_with_type(full_function_name, name, func_type, linkage, calling_convention, FunctionDefinition(return_type, access_modifier, args, self.llvmgen.current_struct is not None and self.llvmgen.current_struct.name or "", unsafe)) # Update backing values for i, arg in enumerate(func.args): args[i].backing_value = arg if is_builtin_type(this_arg): if this_arg not in ParserState.builtin_types: ParserState.builtin_types[this_arg] = dict() ParserState.builtin_types[this_arg][func.name] = func if not this_arg in ParserState.module().builtin_type_methods: ParserState.module().builtin_type_methods[this_arg] = list() ParserState.module().builtin_type_methods[this_arg].append(func.name) else: struct = ParserState.find_struct(this_arg) if struct is None: log_fail(f"Extension function for non-existing type {this_arg}") ParserState.module().functions.remove(func) raise Discard() struct.definition.functions.append(func.name) if not has_body: raise Discard() token = nodes[2] metadata_token = MetadataToken(token.type, token.value) metadata_token.metadata["func"] = func metadata_token.metadata["body_start"] = i nodes.remove(token) nodes.insert(0, metadata_token) return Tree('function_decl', nodes)
def nested_function_call(self, tree: Tree): nodes = tree.children i = 0 full_name = "" arguments = list() while i < len(nodes): if not isinstance(nodes[i], Token): break full_name += f".{nodes[i].value}" i += 1 implicit_parameter_name = '.'.join(full_name.split('.')[0:-1]) function_name = full_name.split('.')[-1] implicit_parameter: RIALVariable = self.llvmgen.get_definition( implicit_parameter_name) if implicit_parameter is None: log_fail( f"Could not find implicit parameter {implicit_parameter_name} in function call {full_name}" ) return NULL arguments.append(implicit_parameter) arguments.extend(self.transform_helper(nodes[i])) arg_types = [arg.llvm_type for arg in arguments] mangled_names = list() # Generate mangled names for implicit parameter and derived if implicit_parameter is not None: ty = implicit_parameter.rial_type # Check if it's a builtin type if ty in ParserState.builtin_types: mangled_names.append( mangle_function_name(function_name, arg_types, ty)) else: mangled_names.append( mangle_function_name(function_name, arg_types, ty)) struct = ParserState.find_struct(ty) # Also mangle base structs to see if it's a derived function for base_struct in struct.definition.base_structs: arg_tys = arg_types arg_tys.pop(0) arg_tys.insert(0, base_struct) mangled_names.append( mangle_function_name(function_name, arg_types, base_struct)) else: mangled_names.append(mangle_function_name(function_name, arg_types)) try: call_instr = self.llvmgen.gen_function_call( [*mangled_names, function_name], [arg.backing_value for arg in arguments]) if call_instr is None: log_fail( f"Failed to generate call to function {function_name}") return NULL rial_func = call_instr.callee if isinstance(rial_func, RIALFunction): return RIALVariable(f"{rial_func.name}_call", rial_func.definition.rial_return_type, call_instr) return RIALVariable(f"{rial_func.name}_call", "Unknown", call_instr) except IndexError: log_fail( f"Missing argument in function call to function {function_name}" ) return NULL
def function_decl(self, tree: Tree): nodes = tree.children # Function specialisation if isinstance(nodes[0], Tree): return self.visit(nodes[0]) access_modifier: RIALAccessModifier = nodes[0].access_modifier unsafe: bool = nodes[0].unsafe linkage = access_modifier.get_linkage() main_function = False calling_convention = self.default_cc return_type = nodes[1].value name = nodes[2].value args: List[RIALVariable] = list() # Add class as implicit parameter if self.llvmgen.current_struct is not None: args.append(RIALVariable("this", self.llvmgen.current_struct.name, None)) i = 3 has_body = False while i < len(nodes): if not isinstance(nodes[i], Token): has_body = True break if nodes[i].type == "IDENTIFIER": arg_type = nodes[i].value i += 1 arg_name = nodes[i].value args.append(RIALVariable(arg_name, arg_type, None)) i += 1 else: break # Map RIAL args to llvm arg types llvm_args = [arg.llvm_type for arg in args if not arg.name.endswith("...")] # If the function has the NoMangleAttribute we need to use the normal name if not self.mangling: full_function_name = name else: if self.llvmgen.current_struct is not None: full_function_name = mangle_function_name(name, llvm_args, self.llvmgen.current_struct.name) else: full_function_name = mangle_function_name(name, llvm_args) full_function_name = f"{ParserState.module().name}:{full_function_name}" # Check if main method if full_function_name.endswith("main:main") and full_function_name.count(':') == 2: main_function = True # Check that main method returns either Int32 or void if return_type != "Int32" and return_type != "void": log_fail(f"Main method must return an integer status code or void, {return_type} given!") # Search for function in the archives # func = ParserState.search_function(full_function_name) # Function has been previously declared in other module # if func is not None and (func.module.name != ParserState.module().name or not func.is_declaration): # # Check if either: # # - has no body (cannot redeclare functions) or # # - is already implemented and # # - is either public or # # - internal and # # - is in same package (cannot reimplement functions) # # TODO: LLVM checks this anyways but maybe we wanna do it, too? # log_fail(f"Function {full_function_name} already declared elsewhere") # raise Discard() # Hasn't been declared previously, redeclare the function type here llvm_return_type = ParserState.map_type_to_llvm(return_type) func_type = self.llvmgen.create_function_type(llvm_return_type, llvm_args, False) # Create the actual function in IR func = self.llvmgen.create_function_with_type(full_function_name, name, func_type, linkage, calling_convention, FunctionDefinition(return_type, access_modifier, args, self.llvmgen.current_struct is not None and self.llvmgen.current_struct.name or "", unsafe)) # Update backing values for i, arg in enumerate(func.args): args[i].backing_value = arg if self.llvmgen.current_struct is not None: self.llvmgen.current_struct.definition.functions.append(func.name) # Always inline the main function into the compiler supplied one if main_function: func.attributes.add('alwaysinline') # If it has no body we do not need to go through it later as it's already declared with this method. if not has_body: raise Discard() token = nodes[2] metadata_token = MetadataToken(token.type, token.value) metadata_token.metadata["func"] = func metadata_token.metadata["body_start"] = i nodes.remove(token) nodes.insert(0, metadata_token) return Tree('function_decl', nodes)
def struct_decl(self, tree: Tree): nodes: List = tree.children access_modifier = nodes[0].access_modifier name = nodes[1].value full_name = f"{ParserState.module().name}:{name}" if ParserState.search_structs(full_name) is not None: log_fail(f"Struct {full_name} has been previously declared!") raise Discard() body: List[RIALVariable] = list() function_decls: List[Tree] = list() bases: List[str] = list() # Find body of struct (variables) i = 2 while i < len(nodes): node: Tree = nodes[i] if isinstance(node, Tree) and node.data == "struct_property_declaration": variable = node.children acc_modifier = variable[0].access_modifier rial_type = variable[1].value variable_name = variable[2].value variable_value = None if len(variable) > 3: variable_value = variable[3] body.append( RIALVariable(variable_name, rial_type, backing_value=variable_value, access_modifier=acc_modifier)) elif isinstance(node, Tree) and node.data == "function_decl": function_decls.append(node) elif isinstance(node, Token) and node.type == "IDENTIFIER": bases.append(node.value) i += 1 base_llvm_structs = list() for base in bases: llvm_struct = ParserState.find_struct(base) if llvm_struct is not None: base_llvm_structs.append(llvm_struct) else: log_fail(f"Derived from undeclared type {base}") base_constructor = Tree('function_decl', [ RIALFunctionDeclarationModifier(access_modifier), Token('IDENTIFIER', "void"), Token('IDENTIFIER', "constructor"), *[ Tree('function_call', [ Token( 'IDENTIFIER', mangle_function_name("constructor", [base], base.name)), Tree('function_args', [ Tree('cast', [ Token('IDENTIFIER', base.name), Tree('var', [Token('IDENTIFIER', "this")]) ]) ]) ]) for base in base_llvm_structs ], *[ Tree('variable_assignment', [ Tree('var', [Token('IDENTIFIER', f"this.{bod.name}")]), Token('ASSIGN', '='), bod.backing_value ]) for bod in body ], Tree('return', [Token('IDENTIFIER', "void")]) ]) function_decls.insert(0, base_constructor) llvm_struct = self.llvmgen.create_identified_struct( full_name, access_modifier.get_linkage(), access_modifier, base_llvm_structs, body) declared_functions = list() # Create functions for function_decl in function_decls: metadata_node = self.fdt.visit(function_decl) if metadata_node is not None: declared_functions.append(metadata_node) try: nodes.remove(function_decl) except ValueError: pass self.llvmgen.finish_struct() node: Token = nodes[1] md_node = MetadataToken(node.type, node.value) md_node.metadata['struct_name'] = full_name md_node.metadata['functions'] = declared_functions nodes.remove(node) nodes.insert(0, md_node) return Tree('struct_decl', nodes)