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 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 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 _get_by_identifier(self, identifier: str, variable: Optional = None) -> Optional: if isinstance(variable, RIALVariable): variable = variable.backing_value if not variable is None and hasattr(variable, 'type') and isinstance(variable.type, PointerType): if isinstance(variable.type.pointee, RIALIdentifiedStructType): struct = ParserState.find_struct(variable.type.pointee.name) if struct is None: return None if not self.check_struct_access_allowed(struct): raise PermissionError(f"Tried accesssing struct {struct.name}") prop = struct.definition.properties[identifier] if prop is None: return None # Check property access if not self.check_property_access_allowed(struct, prop[1]): raise PermissionError(f"Tried to access property {prop[1].name} but it was not allowed!") variable = self.builder.gep(variable, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), prop[0])]) else: # Search local variables variable = self.current_block.get_named_value(identifier) # Search for a global variable if variable is None: glob = ParserState.find_global(identifier) # Check if in same module if glob is not None: if glob.backing_value.parent.name != ParserState.module().name: glob_current_module = ParserState.module().get_global_safe(glob.name) if glob_current_module is not None: variable = glob_current_module else: # TODO: Check if global access is allowed variable = self.gen_global(glob.name, None, glob.backing_value.type.pointee, glob.access_modifier, "external", glob.backing_value.global_constant) else: variable = glob.backing_value # If variable is none, just do a full function search if variable is None: variable = ParserState.find_function(identifier) if variable is None: variable = ParserState.find_function(mangle_function_name(identifier, [])) # Check if in same module if variable is not None: if variable.module.name != ParserState.module().name: variable_current_module = ParserState.module().get_global_safe(variable.name) if variable_current_module is not None: variable = variable_current_module else: variable = self.create_function_with_type(variable.name, variable.name, variable.function_type, variable.linkage, variable.calling_convention, variable.definition) return variable
def gen_function_call(self, possible_function_names: List[str], llvm_args: List) -> Optional[CallInstr]: func = None # Check if it's actually a local variable for function_name in possible_function_names: var = self.get_definition(function_name) if var is not None: func = var # Try to find by function name if func is None: for function_name in possible_function_names: func = ParserState.find_function(function_name) if func is not None: break # Try to find by function name but enable canonical name if func is None: rial_arg_types = [map_llvm_to_type(arg.type) for arg in llvm_args] for function_name in possible_function_names: func = ParserState.find_function(function_name, rial_arg_types) if func is not None: break if func is None: return None if isinstance(func, RIALVariable): func = func.backing_value if isinstance(func, ir.PointerType) and isinstance(func.pointee, RIALFunction): func = func.pointee elif isinstance(func, GlobalVariable) or isinstance(func, AllocaInstr): loaded_func = self.builder.load(func) call = self.builder.call(loaded_func, llvm_args) return call # Check if call is allowed if not self.check_function_call_allowed(func): raise PermissionError(f"Tried calling function {func.name} from {self.current_func.name}") # Check if function is declared in current module if ParserState.module().get_global_safe(func.name) is None: func = self.create_function_with_type(func.name, func.canonical_name, func.function_type, func.linkage, func.calling_convention, func.definition) args = list() # Gen a load if necessary for i, arg in enumerate(llvm_args): llvm_arg = func.definition.rial_args[i].llvm_type if llvm_arg == arg.type: args.append(arg) continue args.append(self.builder.load(arg)) # Check type matching for i, arg in enumerate(args): if len(func.args) > i and arg.type != func.args[i].type: # Check for base types ty = isinstance(arg.type, PointerType) and arg.type.pointee or arg.type func_arg_type = isinstance(func.args[i].type, PointerType) and func.args[i].type.pointee or func.args[ i].type if isinstance(ty, RIALIdentifiedStructType): struct = ParserState.find_struct(ty.name) if struct is not None: found = False # Check if a base struct matches the type expected # TODO: Recursive check for base_struct in struct.definition.base_structs: if base_struct == func_arg_type.name: args.remove(arg) args.insert(i, self.builder.bitcast(arg, ir.PointerType(base_struct))) found = True break if found: continue # TODO: SLOC information raise TypeError( f"Function {func.name} expects a {func.args[i].type} but got a {arg.type}") # Gen call return self.builder.call(func, args)
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)