def variable_decl(self, tree: Tree): nodes = tree.children identifier = nodes[0].value value = self.transform_helper(nodes[2]) if isinstance(value, RIALFunction): variable = self.module.builder.alloca(value.function_type, name=identifier) self.module.builder.store(self.module.builder.load(value), variable) variable = RIALVariable(identifier, str(value.function_type).replace( "i8*", "Char[]"), value.function_type, variable, identified_variable=True) elif isinstance(value, RIALVariable): if value.identified_variable or not value.is_variable: variable = self.module.builder.alloca(value.llvm_type, name=identifier) self.module.builder.store( value.get_loaded_if_variable(self.module), variable) variable = RIALVariable(identifier, value.rial_type, value.llvm_type, variable) else: variable = value else: raise TypeError(value, nodes) self.module.current_block.add_named_value(identifier, variable) return variable
def array_access(self, tree: Tree): nodes = tree.children variable: RIALVariable = self.transform_helper(nodes[0]) index: RIALVariable = self.transform_helper(nodes[1]) if isinstance(variable, ir.Type): ty: ir.Type = variable number = index assert isinstance(number, RIALVariable) name = map_llvm_to_type(ty) if isinstance(number.value, ir.Constant): number = number.value.constant arr_type = ir.ArrayType(ty, number) allocated = self.module.builder.alloca(arr_type) name = f"{name}[{number}]" elif number.is_variable: number = self.module.builder.load(number.value) arr_type = ty.as_pointer() allocated = self.module.builder.alloca(ty, number) allocated = self.module.builder.gep(allocated, [Int32(0)]) name = f"{name}[]" else: number = number.value arr_type = ty.as_pointer() allocated = self.module.builder.alloca(ty, number) allocated = self.module.builder.gep(allocated, [Int32(0)]) name = f"{name}[]" return RIALVariable(f"array_{name}", name, arr_type, allocated) if not variable.rial_type.endswith("]"): raise TypeError(variable) # Check if it's a "GEP array" as we only need one index then if isinstance(variable.value, ir.GEPInstr) or variable.rial_type.endswith("[]"): indices = [index.get_loaded_if_variable(self.module)] else: indices = [Int32(0), index.get_loaded_if_variable(self.module)] var = self.module.builder.gep(variable.value, indices) return RIALVariable( f"{variable.name}[{index}]", variable.array_element_type, isinstance(variable.llvm_type, ir.ArrayType) and variable.llvm_type.element or variable.llvm_type.pointee, var)
def not_rule(self, nodes): return Tree('equal', [ nodes[0], Token('EQUAL', '=='), RIALVariable("number", "Int1", ir.IntType(1), ir.Constant(ir.IntType(1), 0)) ])
def struct_body(self, tree: Tree): nodes = tree.children collected = {'properties': [], 'function_decls': []} for node in nodes: if isinstance(node, Tree): if node.data == "struct_property_declaration": variable = node.children acc_modifier = variable[0].access_modifier llvm_type = self.module.get_definition(variable[1]) if isinstance(llvm_type, RIALIdentifiedStructType): rial_type = llvm_type.name else: rial_type = map_llvm_to_type(llvm_type) variable_name = variable[2].value variable_value = None if len(variable) > 3: variable_value = variable[3] collected['properties'].append( RIALVariable(variable_name, rial_type, llvm_type, variable_value, acc_modifier)) elif node.data == "function_decl" or node.data == "attributed_function_decl": collected['function_decls'].append(node) return collected
def cast(self, tree: Tree): nodes = tree.children ty = self.module.get_definition(nodes[0]) value: RIALVariable = self.transform_helper(nodes[1]) if isinstance(ty, ir.Type) and is_builtin_type(map_llvm_to_type(ty)): # Simple cast for primitive to primitive if is_builtin_type(value.rial_type): cast_function = get_casting_function(value.llvm_type, ty) if hasattr(self.module.builder, cast_function): casted = getattr(self.module.builder, cast_function)( value.get_loaded_if_variable(self.module), ty) return RIALVariable("cast", map_llvm_to_type(ty), ty, casted) 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.module.builder.ptrtoint(value.value, ty) return RIALVariable("cast", map_llvm_to_type(ty), ty, casted) elif isinstance(ty, RIALIdentifiedStructType): # Casting integer to type (unsafe!) if is_builtin_type(value.rial_type): with only_allowed_in_unsafe(): casted = self.module.builder.inttoptr( value.get_loaded_if_variable(self.module), ty.as_pointer()) return RIALVariable("cast", ty.name, ty, casted) else: # Simple type cast casted = self.module.builder.bitcast(value.value, ty.as_pointer()) return RIALVariable("cast", ty.name, ty, casted) raise TypeError(ty, value)
def char(self, nodes): value = nodes[0].value.strip("'") # Parse escape codes to be correct value = eval("'{}'".format(value)) name = ".const.char.%s" % good_hash(value) if len(value) == 0: value = '\00' value = ord(value) const_char = ir.Constant(LLVMUIntType(8), value) return RIALVariable(name, "Char", const_char.type, const_char)
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]) assert isinstance(left, RIALVariable) assert isinstance(right, RIALVariable) if left.rial_type != right.rial_type: raise TypeError(left, op, right) result = None left_val = left.get_loaded_if_variable(self.module) right_val = right.get_loaded_if_variable(self.module) if op == "PLUS": if isinstance(left.llvm_type, ir.IntType): result = self.module.builder.add(left_val, right_val) elif isinstance(left.llvm_type, ir.types._BaseFloatType): result = self.module.builder.fadd(left_val, right_val) elif op == "MINUS": if isinstance(left.llvm_type, ir.IntType): result = self.module.builder.sub(left_val, right_val) elif isinstance(left.llvm_type, ir.types._BaseFloatType): result = self.module.builder.fsub(left_val, right_val) elif op == "MUL": if isinstance(left.llvm_type, ir.IntType): result = self.module.builder.mul(left_val, right_val) elif isinstance(left.llvm_type, ir.types._BaseFloatType): result = self.module.builder.fmul(left_val, right_val) elif op == "DIV": if isinstance(left.llvm_type, LLVMUIntType): result = self.module.builder.udiv(left_val, right_val) elif isinstance(left.llvm_type, ir.IntType): result = self.module.builder.sdiv(left_val, right_val) elif isinstance(left.llvm_type, ir.types._BaseFloatType): result = self.module.builder.fdiv(left_val, right_val) elif op == "REM": if isinstance(left.llvm_type, LLVMUIntType): result = self.module.builder.urem(left_val, right_val) elif isinstance(left.llvm_type, ir.IntType): result = self.module.builder.srem(left_val, right_val) elif isinstance(left.llvm_type, ir.types._BaseFloatType): result = self.module.builder.frem(left_val, right_val) if result is None: raise TypeError(left, op, right) return RIALVariable(f"tmp_{op}", left.rial_type, left.llvm_type, result)
def _create_function_body(self, func: RIALFunction): with self._enter_function_body(func): # Allocate new variables for passed arguments unless they're already pointers for i, arg in enumerate(func.args): if func.definition.rial_args[i].is_variable: variable = func.definition.rial_args[i] else: variable = self.builder.alloca( func.definition.rial_args[i].llvm_type) self.builder.store(arg, variable) variable = RIALVariable( arg.name, func.definition.rial_args[i].rial_type, arg.type, variable) self.current_block.add_named_value(arg.name, variable) yield
def function_decl_args(self, tree: Tree): nodes = tree.children args: List[RIALVariable] = list() i = 0 var_args = False while i < len(nodes): if var_args: raise KeyError() # Params if not isinstance(nodes[i], List): var_args = True i += 1 ty = self.module.get_definition(nodes[i]) i += 1 name = nodes[i].value i += 1 if var_args: name = f"{name}..." if isinstance(ty, RIALVariable): rial_type = ty.rial_type llvm_type = ty.llvm_type elif isinstance(ty, RIALIdentifiedStructType): rial_type = ty.name llvm_type = ty elif isinstance(ty, RIALModule): raise KeyError(ty) elif isinstance(ty, RIALFunction): rial_type = str(ty.function_type) llvm_type = ty.function_type elif isinstance(ty, Type): rial_type = map_shortcut_to_type('.'.join(nodes[i - 2])) llvm_type = ty else: rial_type = None llvm_type = None args.append(RIALVariable(name, rial_type, llvm_type, None)) return args
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 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]) assert isinstance(left, RIALVariable) assert isinstance(right, RIALVariable) assert isinstance(comparison, str) # If both are pointers to arbitrary struct then we need to compare the pointers and not what they contain. # At least until we got a Equals method or something to overwrite if is_builtin_type(left.rial_type) and left.is_variable: left_val = self.module.builder.load(left.value) else: left_val = left.value if is_builtin_type(right.rial_type) and right.is_variable: right_val = self.module.builder.load(right.value) else: right_val = right.value # Unsigned and pointers if isinstance(left.llvm_type, LLVMUIntType) or (not is_builtin_type(left.rial_type) and left.is_variable): result = self.module.builder.icmp_unsigned(comparison, left_val, right_val) # Signed elif isinstance(left.llvm_type, ir.IntType): result = self.module.builder.icmp_signed(comparison, left_val, right_val) # Floating elif isinstance(left.llvm_type, ir._BaseFloatType): result = self.module.builder.fcmp_ordered(comparison, left_val, right_val) else: raise TypeError(left, right) assert isinstance(result, ir.instructions.CompareInstr) return RIALVariable(comparison, "Int1", ir.IntType(1), result)
def declare_global( self, name: str, rial_type: str, llvm_type: ir.Type, linkage: str, initializer: Optional[ir.Constant], access_modifier: AccessModifier = AccessModifier.PRIVATE, constant: bool = False): if self.get_global_safe(name) is not None: raise KeyError(name) glob = ir.GlobalVariable(self, llvm_type, name) glob.linkage = linkage glob.global_constant = constant if initializer is not None: glob.initializer = initializer variable = RIALVariable(name, rial_type, llvm_type, glob, access_modifier) self.global_variables[name] = variable return variable
def gen_function_call(self, candidates: List, arguments: List[RIALVariable], implicit_parameter=None): if len(candidates) > 1: from rial.ir.RIALModule import RIALModule for duplicate in candidates: if isinstance(duplicate, RIALVariable): # Check if wrong module if implicit_parameter is not None and isinstance( implicit_parameter, RIALModule): if duplicate.is_global and duplicate.value.parent.name != implicit_parameter.name: candidates.remove(duplicate) continue # Check if function if not isinstance(duplicate.llvm_type, ir.FunctionType): candidates.remove(duplicate) continue elif isinstance(duplicate, RIALIdentifiedStructType): # Check if wrong module if implicit_parameter is not None and isinstance( implicit_parameter, RIALModule): if duplicate.module_name != implicit_parameter.name: candidates.remove(duplicate) continue elif isinstance(duplicate, RIALFunction): # Check if wrong module if implicit_parameter is not None and isinstance( implicit_parameter, RIALModule): if duplicate.module.name != implicit_parameter.name: candidates.remove(duplicate) continue else: candidates.remove(duplicate) continue if len(candidates) > 1: candids = list() for duplicate in candidates: if isinstance(duplicate, RIALFunction): # Check arguments if len(arguments) != len( duplicate.definition.rial_args): continue for i, rial_arg in enumerate( duplicate.definition.rial_args): if rial_arg.rial_type != arguments[i].rial_type: candidates.remove(duplicate) break candids.append(duplicate) elif isinstance(duplicate, RIALVariable): # Check arguments against function_type parsed to rial_type for i, arg in duplicate.llvm_type.args: if arguments[i].rial_type != map_llvm_to_type( arg.type): candidates.remove(duplicate) break candids.append(duplicate) candidates = candids if len(candidates) == 1: func = candidates[0] else: raise KeyError(candidates) elif len(candidates) == 0: raise KeyError(candidates) else: func = candidates[0] if isinstance(func, RIALFunction): if func.definition.unsafe: from rial.util.only_allowed_in_unsafe import only_allowed_in_unsafe with only_allowed_in_unsafe( "Calls to unsafe or external functions are only allowed in unsafe blocks and functions!" ): pass args = list() for arg in arguments: # Builtins can be passed but need to be loaded if is_builtin_type(arg.rial_type): args.append(arg.get_loaded_if_variable(self.module)) # Pointer to first element for still normal arrays elif re.match(r".+\[[0-9]+\]$", arg.rial_type) is not None: args.append(self.gep(arg.value, [Int32(0), Int32(0)])) else: args.append(arg.value) # Check if it exists in the current module if self.module.get_global_safe(func.name) is None: func = self.module.declare_function( func.name, func.canonical_name, func.function_type, func.linkage, func.calling_convention, func.definition) call_instr = self.call(func, args) return RIALVariable( f"call_{func.name}", func.definition.rial_return_type, self.module.get_definition( func.definition.rial_return_type.split('.')), call_instr) elif isinstance(func, RIALIdentifiedStructType): from rial.ir.RIALModule import RIALModule mod: RIALModule = self.module funcs = mod.get_functions_by_canonical_name( f"{func.name}_constructor") allocad = self.alloca(func) variable = RIALVariable(f"{func.name}_allocad", func.name, func, allocad) arguments.insert(0, variable) # Only if we found at least one constructor. Some structs may not need constructors. if len(funcs) > 0: self.gen_function_call(funcs, arguments) elif len(arguments) > 1: raise KeyError(func.name, "constructor") return variable # TODO: Call to variable return None
def false(self, nodes): return RIALVariable("false", "Int1", FALSE.type, FALSE)
def shorthand_if(self, tree: Tree): nodes = tree.children name = f"{self.module.current_block.name}.shorthand_conditional" conditional_block = self.module.builder.create_block( f"{name}.condition", parent=self.module.current_block) body_block = self.module.builder.create_block(f"{name}.body", parent=conditional_block) else_block = self.module.builder.create_block(f"{name}.else", parent=conditional_block) end_block = self.module.builder.create_block( f"{name}.end", sibling=self.module.current_block) old_conditional_block = self.module.conditional_block old_end_block = self.module.end_block # Create condition self.module.builder.create_jump(conditional_block) self.module.builder.enter_block(conditional_block) cond: RIALVariable = self.transform_helper(nodes[0]) assert isinstance(cond, RIALVariable) self.module.builder.create_conditional_jump( cond.get_loaded_if_variable(self.module), body_block, else_block) # Create body self.module.builder.enter_block(body_block) true_value: RIALVariable = self.transform_helper(nodes[1]) assert isinstance(true_value, RIALVariable) # Builtins can be passed but need to be loaded if is_builtin_type(true_value.rial_type): true_val = true_value.get_loaded_if_variable(self.module) ty = true_value.llvm_type # Pointer to first element for still normal arrays elif re.match(r".+\[[0-9]+\]$", true_value.rial_type) is not None: true_val = self.module.builder.gep(true_value.value, [Int32(0), Int32(0)]) ty = true_val.type else: true_val = true_value.value ty = true_value.llvm_type # Jump out of body if not self.module.current_block.is_terminated: self.module.builder.create_jump(end_block) # Create else self.module.builder.enter_block(else_block) false_value: RIALVariable = self.transform_helper(nodes[2]) assert isinstance(false_value, RIALVariable) # Builtins can be passed but need to be loaded if is_builtin_type(false_value.rial_type): false_val = false_value.get_loaded_if_variable(self.module) # Pointer to first element for still normal arrays elif re.match(r".+\[[0-9]+\]$", false_value.rial_type) is not None: false_val = self.module.builder.gep(false_value.value, [Int32(0), Int32(0)]) else: false_val = false_value.value # Jump out of else if not self.module.current_block.is_terminated: self.module.builder.create_jump(end_block) # Leave conditional block self.module.builder.enter_block(end_block) # PHI the values phi = self.module.builder.phi(ty) phi.add_incoming(true_val, body_block) phi.add_incoming(false_val, else_block) self.module.conditional_block = old_conditional_block self.module.end_block = old_end_block return RIALVariable("phi", map_llvm_to_type(ty), ty, phi)
def number(self, nodes: List): value: str = nodes[0].value number = convert_number_to_constant(value) return RIALVariable("number", map_llvm_to_type(number.type), number.type, number)
def sizeof(self, tree: Tree): nodes = tree.children variable: Optional[RIALVariable] = self.transform_helper(nodes[0]) assert variable is None or isinstance( variable, RIALVariable) or isinstance(variable, ir.Type) # "Variable" is actually a type name that we need to extract and then get the size of if variable is None: ty = self.module.get_definition( [node.value for node in nodes[0].children]) if ty is None: raise TypeError(nodes) if isinstance(ty, RIALIdentifiedStructType): name = ty.name else: name = map_llvm_to_type(ty) size = Int32(CompilationManager.codegen.get_size(ty)) return RIALVariable(f"sizeof_{name}", "Int32", Int32, size) elif isinstance(variable, ir.Type): size = Int32(CompilationManager.codegen.get_size(variable)) return RIALVariable(f"sizeof_{variable}", "Int32", Int32, size) elif isinstance(variable.llvm_type, ir.ArrayType) and not isinstance( variable.value, ir.GEPInstr): ty: ir.ArrayType = variable.llvm_type if isinstance(ty.count, int): size = CompilationManager.codegen.get_size( ty.element) * ty.count size = Int32(size) elif isinstance(ty.count, ir.Constant): size = CompilationManager.codegen.get_size( ty.element) * ty.count.constant size = Int32(size) else: size = CompilationManager.codegen.get_size(ty.element) size = self.module.builder.mul( Int32(size), self.module.builder.load(ty.count)) return RIALVariable(f"sizeof_{ty.element}[{ty.count}]", "Int32", Int32, size) # If an array wasn't caught in the previous elif, then it doesn't have a set length and needs to be sized this way. elif isinstance(variable.value, ir.GEPInstr) or variable.rial_type.endswith("]"): # This is worst case and practically only reserved for GEPs # This is worst case as it cannot be optimized away. base = self.module.builder.ptrtoint( self.module.builder.gep(variable.value, [Int32(0)]), Int32) val = self.module.builder.ptrtoint( self.module.builder.gep(variable.value, [Int32(1)]), Int32) size = self.module.builder.sub(val, base) return RIALVariable("sizeof_unknown", "Int32", Int32, size) else: size = Int32( CompilationManager.codegen.get_size(variable.llvm_type)) return RIALVariable(f"sizeof_{variable.rial_type}", "Int32", Int32, size)
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 true(self, nodes): return RIALVariable("true", "Int1", TRUE.type, TRUE)
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 _get_recursive_definition( self, identifier: str, variable: Optional[Union[RIALVariable, RIALFunction, ir.Module, RIALIdentifiedStructType, ir.Type]] ) -> Optional[Union[RIALVariable, RIALFunction, ir.Module, RIALIdentifiedStructType, ir.Type]]: assert isinstance(identifier, str) if variable is not None: if isinstance(variable, RIALModule): return variable.get_definition([identifier]) elif isinstance(variable, RIALFunction): return None elif isinstance(variable, RIALIdentifiedStructType): return None elif isinstance(variable, RIALVariable): if not isinstance(variable.llvm_type, RIALIdentifiedStructType): return None struct: RIALIdentifiedStructType = variable.llvm_type # Check properties if identifier in struct.definition.properties: if self.builder is None: return None prop = struct.definition.properties[identifier] return RIALVariable( identifier, prop[1].rial_type, prop[1].llvm_type, self.builder.gep(variable.value, [Int32(0), Int32(prop[0])]), prop[1].access_modifier) # Check functions from rial.compilation_manager import CompilationManager mod = struct.module_name == self.name and self or CompilationManager.modules[ struct.module_name] variable = mod.get_global_safe(identifier) if variable is not None: if isinstance(variable, RIALFunction): if len(variable.definition.rial_args ) > 0 and variable.definition.rial_args[ 0].rial_type == struct.name: return variable return None # Check builtin's first identifier = map_shortcut_to_type(identifier) variable = map_type_to_llvm(identifier) if variable is None: # Arrays match = re.match(r"^([^\[]+)\[([0-9]+)?\]$", identifier) if match is not None: ty = match.group(1) count = match.group(2) definition = self.get_definition([ty]) if definition is not None: if count is not None: return ir.ArrayType(definition, int(count)) else: return definition.as_pointer() # Function type match = re.match(r"^([^(]+)\(([^,)]+\s*,?\s*)*\)$", identifier) if match is not None: return_type = "" arg_types = list() var_args = False for i, group in enumerate(match.groups()): if i == 0: return_type = group.strip() elif group == "...": var_args = True elif group is None: break else: arg_types.append(group.strip()) return ir.FunctionType(self.get_definition([ return_type ]), [self.get_definition([arg]) for arg in arg_types], var_args) # Check local variables first if variable is None and self.current_block is not None: variable = self.current_block.get_named_value(identifier) # Check module-local global variables next if variable is None: variable = identifier in self.global_variables and self.global_variables[ identifier] or None # Check structs next if variable is None: variable = identifier in self.get_identified_types() and \ self.get_identified_types()[identifier] or None # Check functions next if variable is None: variable = self.get_global_safe(identifier) # Check module imports if variable is None: mod = identifier in self.dependencies and self.dependencies[ identifier] or None if mod is not None: from rial.compilation_manager import CompilationManager variable = CompilationManager.modules[mod] # Check always imported last if variable is None and not self.name.startswith("rial:builtin:"): from rial.compilation_manager import CompilationManager for always_imported in CompilationManager.always_imported: mod = CompilationManager.modules[always_imported] variable = mod.get_definition([identifier]) if variable is not None: break return variable
def null(self, nodes): return RIALVariable("null", "Int8", NULL.type, NULL)