def return_rule(self, tree: Tree): nodes = tree.children if self.module.current_block.terminator is not None: raise PermissionError("Return after return") if len(nodes) == 0: self.module.builder.ret_void() return variable: RIALVariable = self.transform_helper(nodes[0]) assert isinstance(variable, RIALVariable) if variable.rial_type != self.module.current_func.definition.rial_return_type: raise TypeError( variable.rial_type, self.module.current_func.definition.rial_return_type) if isinstance(variable.llvm_type, VoidType): self.module.builder.ret_void() elif is_builtin_type(variable.rial_type): self.module.builder.ret( variable.get_loaded_if_variable(self.module)) # Special case for CStrings for now elif self.module.current_func.definition.rial_return_type == map_shortcut_to_type( "CString") and isinstance(variable.value.type.pointee, ir.PointerType): self.module.builder.ret(self.module.builder.load(variable.value)) else: self.module.builder.ret(variable.value)
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 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 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 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 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