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 break_rule(self, tree: Tree): if self.module.end_block is None: raise PermissionError("Break outside of loop") self.module.builder.branch(self.module.end_block) raise Discard()
def continue_rule(self, tree: Tree): if self.module.conditional_block is None: raise PermissionError("Continue outside of loop") self.module.builder.branch(self.module.conditional_block) raise Discard()
def unsafe_block(self, tree: Tree): nodes = tree.children self.module.currently_unsafe = True for node in nodes[1:]: self.transform_helper(node) raise Discard()
def global_variable_decl(self, nodes: List): body = nodes[1].children access_modifier: AccessModifier = nodes[0].access_modifier variable_name = body[0].value value = body[2] if isinstance(value, RIALVariable): if value.is_global and value.value.global_constant: glob = self.module.declare_global( variable_name, value.rial_type, value.llvm_type, access_modifier.get_linkage(), value.value.initializer, access_modifier, True) else: with self.module.create_in_global_ctor(): glob = self.module.declare_global( variable_name, value.rial_type, value.llvm_type, access_modifier.get_linkage(), None, access_modifier) self.module.builder.store( value.get_loaded_if_variable(self.module), glob.value) else: with self.module.create_in_global_ctor(): if not isinstance(value, RIALFunction) and not isinstance( value, RIALVariable): value: RIALVariable = self.main_transformer.visit(value) if isinstance(value, RIALFunction): value: RIALFunction # Check if in module, otherwise redeclare # if self.module.get_global_safe(value.name) is None: # value = self.module.declare_function(value.name, value.canonical_name, value.function_type, # value.linkage, value.calling_convention, value.definition) rial_type = str(value.function_type).replace( "i8*", "Char[]") glob = self.module.declare_global( variable_name, rial_type, value.function_type, access_modifier.get_linkage(), value, access_modifier, False) else: glob = self.module.declare_global( variable_name, value.rial_type, value.llvm_type, access_modifier.get_linkage(), null(value.llvm_type), access_modifier) self.module.builder.store( value.get_loaded_if_variable(self.module), glob.value) raise Discard()
def imported(self, nodes): mutability = nodes[0] if mutability != VariableMutabilityModifier.CONST: raise PermissionError( "Cannot import modules as anything but const variables") var_name = nodes[1].value if var_name in self.module.dependencies: raise NameError(var_name) mod_name = ':'.join([node.value for node in nodes[3:]]) if mod_name.startswith("core") or mod_name.startswith( "std") or mod_name.startswith("startup"): mod_name = f"rial:{mod_name}" CompilationManager.request_module(mod_name) self.module.dependencies[var_name] = mod_name raise Discard()
def llvm_ir(self, tree: Tree): # Check for unsafe with only_allowed_in_unsafe( "llvm_ir is only allowed in unsafe functions or blocks!"): pass nodes = tree.children llvm_ir: str = nodes[0].value.strip("\"") llvm_ir = eval("'{}'".format(llvm_ir)) ty = None return_name = None for node in nodes[1:]: if isinstance(node, Tree): ty = self.transform_helper(node) elif isinstance(node, Token): return_name = node.value.strip("\"") self.module.builder.ir(ty, llvm_ir) if return_name is not None: if ty is None: raise KeyError( "Need to specify type if you want to use @llvm_ir as a variable!" ) if isinstance(ty, RIALIdentifiedStructType): ty_name = ty.name else: ty_name = map_llvm_to_type(ty) variable = self.module.builder.alloca(ty) self.module.builder.store( LLVMIRInstruction(ty, f"%\"{return_name}\""), variable) variable = RIALVariable("llvm_ir", ty_name, ty, variable) if return_name in self.module.current_block.named_values: raise KeyError(f"{return_name} has been previously defined!") self.module.current_block.add_named_value(return_name, variable) return variable raise Discard()
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)
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()
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 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)