1- # Copyright 2019 typed_python Authors
1+ # Copyright 2023 typed_python Authors
22#
33# Licensed under the Apache License, Version 2.0 (the "License");
44# you may not use this file except in compliance with the License.
1313# limitations under the License.
1414
1515import llvmlite .binding as llvm
16- import llvmlite .ir
1716import typed_python .compiler .native_compiler .native_ast as native_ast
1817import typed_python .compiler .native_compiler .native_ast_to_llvm as native_ast_to_llvm
19-
18+ from typed_python .compiler .native_compiler .compiler_cache import CompilerCache
19+ from typed_python .compiler .native_compiler .llvm_execution_engine import create_execution_engine
2020from typed_python .compiler .native_compiler .loaded_module import LoadedModule
2121from typed_python .compiler .native_compiler .native_function_pointer import NativeFunctionPointer
2222from typed_python .compiler .native_compiler .binary_shared_object import BinarySharedObject
2323
24- import ctypes
25- from typed_python import _types
26-
27- llvm .initialize ()
28- llvm .initialize_native_target ()
29- llvm .initialize_native_asmprinter () # yes, even this one
30-
31- target_triple = llvm .get_process_triple ()
32- target = llvm .Target .from_triple (target_triple )
33- target_machine = target .create_target_machine ()
34- target_machine_shared_object = target .create_target_machine (reloc = 'pic' , codemodel = 'default' )
35-
36- ctypes .CDLL (_types .__file__ , mode = ctypes .RTLD_GLOBAL )
37-
38-
39- pointer_size = (
40- llvmlite .ir .PointerType (llvmlite .ir .DoubleType ())
41- .get_abi_size (target_machine .target_data )
42- )
43-
44- assert pointer_size == native_ast_to_llvm .pointer_size
45-
46-
47- def sizeof_native_type (native_type ):
48- if native_type .matches .Void :
49- return 0
50-
51- return (
52- native_ast_to_llvm .type_to_llvm_type (native_type )
53- .get_abi_size (target_machine .target_data )
54- )
55-
56-
57- # there can be only one llvm engine alive at once.
58- _engineCache = []
59-
60-
61- def create_execution_engine (inlineThreshold ):
62- if _engineCache :
63- return _engineCache [0 ]
64-
65- pmb = llvm .create_pass_manager_builder ()
66- pmb .opt_level = 3
67- pmb .size_level = 0
68- pmb .inlining_threshold = inlineThreshold
69- pmb .loop_vectorize = True
70- pmb .slp_vectorize = True
71-
72- pass_manager = llvm .create_module_pass_manager ()
73- pmb .populate (pass_manager )
74-
75- target_machine .add_analysis_passes (pass_manager )
76-
77- # And an execution engine with an empty backing module
78- backing_mod = llvm .parse_assembly ("" )
79- engine = llvm .create_mcjit_compiler (backing_mod , target_machine )
80-
81- _engineCache .append ((engine , pass_manager ))
82-
83- return engine , pass_manager
84-
8524
8625class NativeCompiler :
8726 """"Engine for compiling bundles of native_ast.Function objects into NativeFunctionPointers.
@@ -91,15 +30,23 @@ class NativeCompiler:
9130 * compiling functions into a runnable form using llvm
9231 * performing any runtime-based performance optimizations
9332 * maintaining the compiler cache
33+
34+ Note that this class is NOT threadsafe and clients are expected to serialize their
35+ access through Runtime.
9436 """
9537 def __init__ (self , inlineThreshold ):
38+ self .compilerCache = None
9639 self .engine , self .module_pass_manager = create_execution_engine (inlineThreshold )
9740 self .converter = native_ast_to_llvm .Converter ()
9841 self .functions_by_name = {}
9942 self .inlineThreshold = inlineThreshold
10043 self .verbose = False
10144 self .optimize = True
10245
46+ def initializeCompilerCache (self , compilerCacheDir ):
47+ """Indicate that we should use a compiler cache from disk at 'compilerCacheDir'."""
48+ self .compilerCache = CompilerCache (compilerCacheDir )
49+
10350 def markExternal (self , functionNameToType ):
10451 """Provide type signatures for a set of external functions."""
10552 self .converter .markExternal (functionNameToType )
@@ -110,7 +57,65 @@ def mark_converter_verbose(self):
11057 def mark_llvm_codegen_verbose (self ):
11158 self .verbose = True
11259
113- def buildSharedObject (self , functions ):
60+ def addFunctions (
61+ self ,
62+ # map from str to native_ast.Function
63+ functionDefinitions ,
64+ # map from str to the TypedCallTarget for any function that's actually typed
65+ typedCallTargets ,
66+ externallyUsed
67+ ):
68+ """Add a collection of functions to the compiler.
69+
70+ Once a function has been added, we can request a NativeFunctionPointer for it.
71+ """
72+ if self .compilerCache is None :
73+ loadedModule = self ._buildModule (functionDefinitions )
74+ loadedModule .linkGlobalVariables ()
75+ else :
76+ binary = self ._buildSharedObject (functionDefinitions )
77+
78+ self .compilerCache .addModule (
79+ binary ,
80+ typedCallTargets ,
81+ externallyUsed
82+ )
83+
84+ def functionPointerByName (self , linkerName ) -> NativeFunctionPointer :
85+ """Find a NativeFunctionPointer for a given link-time name.
86+
87+ Args:
88+ linkerName (str) - the name of the compiled symbol we want.
89+
90+ Returns:
91+ a NativeFunctionPointer or None if the function has never been defined.
92+ """
93+ if self .compilerCache is not None :
94+ # the compiler cache has every shared object and can load them
95+ return self .compilerCache .function_pointer_by_name (linkerName )
96+
97+ # the llvm compiler is just building shared objects, but the
98+ # compiler cache has all the pointers.
99+ return self .functions_by_name .get (linkerName )
100+
101+ def loadFromCache (self , linkName ):
102+ """Attempt to load a cached copy of 'linkName' and all reachable code.
103+
104+ If it isn't defined, or has already been defined, return None. If we're loading it
105+ for the first time, return a pair
106+
107+ (typedCallTargets, nativeTypes)
108+
109+ where typedCallTargets is a map from linkName to TypedCallTarget, and nativeTypes is
110+ a map from linkName to native_ast.Type.Function giving the native implementation type.
111+
112+ WARNING: this will return None if you already called 'functionPointerByName' on it
113+ """
114+ if self .compilerCache :
115+ if self .compilerCache .hasSymbol (linkName ):
116+ return self .compilerCache .loadForSymbol (linkName )
117+
118+ def _buildSharedObject (self , functions ):
114119 """Add native definitions and return a BinarySharedObject representing the compiled code."""
115120 module = self .converter .add_functions (functions )
116121
@@ -135,10 +140,7 @@ def buildSharedObject(self, functions):
135140 module .functionNameToType ,
136141 )
137142
138- def function_pointer_by_name (self , name ):
139- return self .functions_by_name .get (name )
140-
141- def buildModule (self , functions ):
143+ def _buildModule (self , functions ):
142144 """Compile a list of functions into a new module.
143145
144146 Args:
0 commit comments