def compile_ir(self, module: RIALModule) -> ModuleRef: with self.lock: llvm_ir = str(module) try: mod = self.binding.parse_assembly(llvm_ir) mod.verify() except Exception as e: log_fail(llvm_ir) raise e self._optimize_module(mod) return mod
def transform(self, tree): try: return super().transform(tree) except Discard: pass except Exception as e: from rial.util.log import log_fail log_fail(e) from rial.compilation_manager import CompilationManager log_fail( f"Current Module: {CompilationManager.current_module is not None and CompilationManager.current_module.name or ''}" ) import traceback log_fail(traceback.format_exc())
def visit(self, tree): f = getattr(self, tree.data) wrapper = getattr(f, 'visit_wrapper', None) try: if wrapper is not None: return f.visit_wrapper(f, tree.data, tree.children, tree.meta) else: return f(tree) except Discard: pass except Exception as e: from rial.util.log import log_fail log_fail(e) from rial.compilation_manager import CompilationManager log_fail( f"Current Module: {CompilationManager.current_module is not None and CompilationManager.current_module.name or ''}" ) import traceback log_fail(traceback.format_exc())
def struct_decl(self, tree: Tree): nodes: List = tree.children modifier: DeclarationModifier = nodes[0] access_modifier = modifier.access_modifier name = nodes[1].value if name in self.module.context.identified_types: log_fail(f"Struct {name} has been previously declared!") raise Discard() struct_body: Dict[str, List[Union[RIALVariable, Tree]]] = self.visit(nodes[-1]) bases: List[List[str]] = list() # Find body of struct (variables) i = 2 while i < len(nodes): node: Tree = nodes[i] if isinstance(node, List): bases.append(node) else: break i += 1 base_llvm_structs = list() for base in bases: llvm_struct = self.module.get_definition(base) if llvm_struct is not None and isinstance( llvm_struct, RIALIdentifiedStructType): base_llvm_structs.append(llvm_struct) else: log_fail(f"Derived from undeclared type {base}") # base_constructor = Tree('function_decl', # [ # DeclarationModifier(access_modifier, False), # ["void"], # Token('IDENTIFIER', "constructor"), # Tree('function_decl_args', [[name], Token('IDENTIFIER', "this")]), # *[Tree('function_call', [ # Token('IDENTIFIER', "constructor"), # 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.value]) for bod in struct_body['properties']], # Tree('return', [Token('IDENTIFIER', "void")]) # ] # ) # struct_body['function_decls'].insert(0, base_constructor) struct = create_identified_struct_type(self.module, name, access_modifier, base_llvm_structs, struct_body['properties']) self.module.current_struct = struct declared_functions = list() # Create functions for function_decl in struct_body['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.module.current_block = None self.module.current_func = None self.module.current_struct = None node: Token = nodes[1] nodes.pop(1) node: MetadataToken = MetadataToken(node.type, node.value) node.metadata['struct'] = struct node.metadata['functions'] = declared_functions nodes.insert(0, node) return Tree('struct_decl', nodes)
def function_decl(self, tree: Tree): nodes = tree.children modifier: DeclarationModifier = nodes[0] if isinstance(modifier, MetadataToken): return tree access_modifier: AccessModifier = modifier.access_modifier unsafe: bool = modifier.unsafe linkage = access_modifier.get_linkage() name = nodes[2].value llvm_return_type = self.module.get_definition(nodes[1]) if isinstance(llvm_return_type, RIALIdentifiedStructType): return_type = llvm_return_type.name else: return_type = map_shortcut_to_type('.'.join(nodes[1])) # Builtins can be passed as raw values if is_builtin_type(return_type): llvm_return_type = llvm_return_type # Pointer to first element for still normal arrays elif re.match(r".+\[[0-9]+\]$", return_type) is not None: llvm_return_type = llvm_return_type.as_pointer() elif isinstance(llvm_return_type, RIALIdentifiedStructType): llvm_return_type = llvm_return_type.as_pointer() else: llvm_return_type = llvm_return_type args: List[RIALVariable] = list() # Add class as implicit parameter if self.module.current_struct is not None and not modifier.static: args.append( RIALVariable("this", self.module.current_struct.name, self.module.current_struct, None)) args.extend(self.visit(nodes[3])) llvm_args = list() # Map RIAL args to llvm arg types for arg in args: if arg.name.endswith("..."): continue # Pointer for array and struct types if isinstance(arg.llvm_type, RIALIdentifiedStructType) or isinstance( arg.llvm_type, ArrayType): llvm_type = arg.llvm_type.as_pointer() else: llvm_type = arg.llvm_type llvm_args.append(llvm_type) # If the function has the NoMangleAttribute we need to use the normal name if 'noduplicate' in self.attributes: if modifier.static: raise PermissionError( "NoMangle for static function doesn't work") full_function_name = name else: if modifier.static: if self.module.current_struct is None: raise PermissionError( "Static functions can only be declared in types") full_function_name = self.module.get_unique_function_name( f"{self.module.current_struct.name}::{name}", [arg.rial_type for arg in args]) else: full_function_name = self.module.get_unique_function_name( name, [arg.rial_type for arg in args]) # Check if main method if name.endswith("main") and self.module.name.endswith("main"): self.attributes.add('alwaysinline') # 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!" ) # Hasn't been declared previously, redeclare the function type here func_type = FunctionType(llvm_return_type, llvm_args, False) # Create the actual function in IR func = RIALFunction(self.module, func_type, full_function_name, name) func.linkage = linkage func.calling_convention = self.default_cc func.definition = FunctionDefinition( return_type, access_modifier, args, self.module.current_struct is not None and self.module.current_struct or "", unsafe) func.attributes = self.attributes # Update args for i, arg in enumerate(func.args): arg.name = args[i].name args[i].value = arg # If it has no body we do not need to go through it later as it's already declared with this method. if not len(nodes) > 4: with self.module.create_or_enter_function_body(func): self.module.builder.ret_void() raise Discard() token = nodes[2] metadata_token = MetadataToken(token.type, token.value) metadata_token.metadata["func"] = func metadata_token.metadata["body_start"] = 4 nodes.remove(token) nodes.insert(0, metadata_token) return Tree('function_decl', nodes)
def extension_function_decl(self, tree: Tree): nodes = tree.children modifier: DeclarationModifier = nodes[0] if isinstance(modifier, MetadataToken): return tree if modifier.static: raise PermissionError("Extension functions cannot be static") access_modifier: AccessModifier = modifier.access_modifier unsafe: bool = modifier.unsafe linkage = access_modifier.get_linkage() name = nodes[2].value llvm_return_type = self.module.get_definition(nodes[1]) if isinstance(llvm_return_type, RIALIdentifiedStructType): return_type = llvm_return_type.name else: return_type = map_shortcut_to_type('.'.join(nodes[1])) # Builtins can be passed as raw values if is_builtin_type(return_type): llvm_return_type = llvm_return_type # Pointer to first element for still normal arrays elif re.match(r".+\[[0-9]+\]$", return_type) is not None: llvm_return_type = llvm_return_type.as_pointer() elif isinstance(llvm_return_type, RIALIdentifiedStructType): llvm_return_type = llvm_return_type.as_pointer() else: llvm_return_type = llvm_return_type # Extension functions cannot be declared inside other classes. if self.module.current_struct is not None: log_fail( f"Extension function {name} cannot be declared inside another class!" ) raise Discard() args: List[RIALVariable] = list() this_type = self.module.get_definition(nodes[3]) if isinstance(this_type, RIALIdentifiedStructType): rial_type = this_type.name else: rial_type = map_shortcut_to_type('.'.join(nodes[3])) args.append(RIALVariable(nodes[4].value, rial_type, this_type, None)) if isinstance(nodes[5], Tree) and nodes[5].data == "function_decl_args": args.extend(self.visit(nodes[5])) body_start = 6 else: body_start = 5 # If the function has the NoMangleAttribute we need to use the normal name if 'noduplicate' in self.attributes: full_function_name = name else: full_function_name = self.module.get_unique_function_name( name, [arg.rial_type for arg in args]) llvm_args = list() # Map RIAL args to llvm arg types for arg in args: if arg.name.endswith("..."): continue # Builtins can be passed as raw values if is_builtin_type(arg.rial_type): llvm_args.append(arg.llvm_type) # Pointer to first element for still normal arrays elif re.match(r".+\[[0-9]+\]$", arg.rial_type) is not None: llvm_args.append(arg.llvm_type.as_pointer()) elif isinstance(arg.llvm_type, RIALIdentifiedStructType): llvm_args.append(arg.llvm_type.as_pointer()) else: llvm_args.append(arg.llvm_type) # Hasn't been declared previously, redeclare the function type here func_type = FunctionType(llvm_return_type, llvm_args) # Create the actual function in IR func = RIALFunction(self.module, func_type, full_function_name, name) func.linkage = linkage func.calling_convention = self.default_cc func.definition = FunctionDefinition(return_type, access_modifier, args, args[0].rial_type, unsafe) # Update args for i, arg in enumerate(func.args): arg.name = args[i].name args[i].value = arg # If it has no body we can discard it now. if len(nodes) <= body_start: with self.module.create_or_enter_function_body(func): self.module.builder.ret_void() raise Discard() token = nodes[2] metadata_token = MetadataToken(token.type, token.value) metadata_token.metadata["func"] = func metadata_token.metadata["body_start"] = body_start nodes.remove(token) nodes.insert(0, metadata_token) return Tree('function_decl', nodes)
def external_function_decl(self, tree: Tree): nodes = tree.children modifier: DeclarationModifier = nodes[0] if isinstance(modifier, MetadataToken): return tree if modifier.static: raise PermissionError("External functions cannot be static") access_modifier: AccessModifier = modifier.access_modifier unsafe: bool = modifier.unsafe name = nodes[2].value llvm_return_type = self.module.get_definition(nodes[1]) if isinstance(llvm_return_type, RIALIdentifiedStructType): return_type = llvm_return_type.name else: return_type = map_shortcut_to_type('.'.join(nodes[1])) # Builtins can be passed as raw values if is_builtin_type(return_type): llvm_return_type = llvm_return_type # Pointer to first element for still normal arrays elif re.match(r".+\[[0-9]+\]$", return_type) is not None: llvm_return_type = llvm_return_type.as_pointer() elif isinstance(llvm_return_type, RIALIdentifiedStructType): llvm_return_type = llvm_return_type.as_pointer() else: llvm_return_type = llvm_return_type # External functions cannot be declared in a struct if self.module.current_struct is not None: log_fail( f"External function {name} cannot be declared inside a class!") raise Discard() args: List[RIALVariable] = self.visit(nodes[3]) llvm_args = list() # Map RIAL args to llvm arg types for arg in args: if arg.name.endswith("..."): continue # Pointer for array and struct types if isinstance(arg.llvm_type, RIALIdentifiedStructType) or isinstance( arg.llvm_type, ArrayType): llvm_type = arg.llvm_type.as_pointer() else: llvm_type = arg.llvm_type llvm_args.append(llvm_type) var_args = len(args) > 0 and args[-1].name.endswith("...") # Hasn't been declared previously, redeclare the function type here func_type = FunctionType(llvm_return_type, llvm_args, var_args) # Create function definition func = RIALFunction(self.module, func_type, name, name) func.linkage = "external" func.calling_convention = "ccc" func.definition = FunctionDefinition(return_type, access_modifier, args, unsafe=True) func.attributes = self.attributes # Update args for i, arg in enumerate(func.args): arg.name = args[i].name args[i].value = arg raise Discard()