1import os 2from ctypes import (POINTER, c_char_p, c_longlong, c_int, c_size_t, 3 c_void_p, string_at) 4 5from llvmlite.binding import ffi 6from llvmlite.binding.common import _decode_string, _encode_string 7 8 9def get_process_triple(): 10 """ 11 Return a target triple suitable for generating code for the current process. 12 An example when the default triple from ``get_default_triple()`` is not be 13 suitable is when LLVM is compiled for 32-bit but the process is executing 14 in 64-bit mode. 15 """ 16 with ffi.OutputString() as out: 17 ffi.lib.LLVMPY_GetProcessTriple(out) 18 return str(out) 19 20 21class FeatureMap(dict): 22 """ 23 Maps feature name to a boolean indicating the availability of the feature. 24 Extends ``dict`` to add `.flatten()` method. 25 """ 26 27 def flatten(self, sort=True): 28 """ 29 Args 30 ---- 31 sort: bool 32 Optional. If True, the features are sorted by name; otherwise, 33 the ordering is unstable between python session due to hash 34 randomization. Defaults to True. 35 36 Returns a string suitable for use as the ``features`` argument to 37 ``Target.create_target_machine()``. 38 39 """ 40 iterator = sorted(self.items()) if sort else iter(self.items()) 41 flag_map = {True: '+', False: '-'} 42 return ','.join('{0}{1}'.format(flag_map[v], k) 43 for k, v in iterator) 44 45 46def get_host_cpu_features(): 47 """ 48 Returns a dictionary-like object indicating the CPU features for current 49 architecture and whether they are enabled for this CPU. The key-value pairs 50 are the feature name as string and a boolean indicating whether the feature 51 is available. The returned value is an instance of ``FeatureMap`` class, 52 which adds a new method ``.flatten()`` for returning a string suitable for 53 use as the "features" argument to ``Target.create_target_machine()``. 54 55 If LLVM has not implemented this feature or it fails to get the information, 56 this function will raise a RuntimeError exception. 57 """ 58 with ffi.OutputString() as out: 59 outdict = FeatureMap() 60 if not ffi.lib.LLVMPY_GetHostCPUFeatures(out): 61 return outdict 62 flag_map = {'+': True, '-': False} 63 content = str(out) 64 if content: # protect against empty string 65 for feat in content.split(','): 66 if feat: # protect against empty feature 67 outdict[feat[1:]] = flag_map[feat[0]] 68 return outdict 69 70 71def get_default_triple(): 72 """ 73 Return the default target triple LLVM is configured to produce code for. 74 """ 75 with ffi.OutputString() as out: 76 ffi.lib.LLVMPY_GetDefaultTargetTriple(out) 77 return str(out) 78 79 80def get_host_cpu_name(): 81 """ 82 Get the name of the host's CPU, suitable for using with 83 :meth:`Target.create_target_machine()`. 84 """ 85 with ffi.OutputString() as out: 86 ffi.lib.LLVMPY_GetHostCPUName(out) 87 return str(out) 88 89 90_object_formats = { 91 1: "COFF", 92 2: "ELF", 93 3: "MachO", 94} 95 96 97def get_object_format(triple=None): 98 """ 99 Get the object format for the given *triple* string (or the default 100 triple if omitted). 101 A string is returned 102 """ 103 if triple is None: 104 triple = get_default_triple() 105 res = ffi.lib.LLVMPY_GetTripleObjectFormat(_encode_string(triple)) 106 return _object_formats[res] 107 108 109def create_target_data(layout): 110 """ 111 Create a TargetData instance for the given *layout* string. 112 """ 113 return TargetData(ffi.lib.LLVMPY_CreateTargetData(_encode_string(layout))) 114 115 116class TargetData(ffi.ObjectRef): 117 """ 118 A TargetData provides structured access to a data layout. 119 Use :func:`create_target_data` to create instances. 120 """ 121 122 def __str__(self): 123 if self._closed: 124 return "<dead TargetData>" 125 with ffi.OutputString() as out: 126 ffi.lib.LLVMPY_CopyStringRepOfTargetData(self, out) 127 return str(out) 128 129 def _dispose(self): 130 self._capi.LLVMPY_DisposeTargetData(self) 131 132 def get_abi_size(self, ty): 133 """ 134 Get ABI size of LLVM type *ty*. 135 """ 136 return ffi.lib.LLVMPY_ABISizeOfType(self, ty) 137 138 def get_element_offset(self, ty, position): 139 """ 140 Get byte offset of type's ty element at the given position 141 """ 142 143 offset = ffi.lib.LLVMPY_OffsetOfElement(self, ty, position) 144 if offset == -1: 145 raise ValueError("Could not determined offset of {}th " 146 "element of the type '{}'. Is it a struct" 147 "type?".format(position, str(ty))) 148 return offset 149 150 def get_pointee_abi_size(self, ty): 151 """ 152 Get ABI size of pointee type of LLVM pointer type *ty*. 153 """ 154 size = ffi.lib.LLVMPY_ABISizeOfElementType(self, ty) 155 if size == -1: 156 raise RuntimeError("Not a pointer type: %s" % (ty,)) 157 return size 158 159 def get_pointee_abi_alignment(self, ty): 160 """ 161 Get minimum ABI alignment of pointee type of LLVM pointer type *ty*. 162 """ 163 size = ffi.lib.LLVMPY_ABIAlignmentOfElementType(self, ty) 164 if size == -1: 165 raise RuntimeError("Not a pointer type: %s" % (ty,)) 166 return size 167 168 169RELOC = frozenset(['default', 'static', 'pic', 'dynamicnopic']) 170CODEMODEL = frozenset(['default', 'jitdefault', 'small', 'kernel', 'medium', 171 'large']) 172 173 174class Target(ffi.ObjectRef): 175 _triple = '' 176 177 # No _dispose() method since LLVMGetTargetFromTriple() returns a 178 # persistent object. 179 180 @classmethod 181 def from_default_triple(cls): 182 """ 183 Create a Target instance for the default triple. 184 """ 185 triple = get_default_triple() 186 return cls.from_triple(triple) 187 188 @classmethod 189 def from_triple(cls, triple): 190 """ 191 Create a Target instance for the given triple (a string). 192 """ 193 with ffi.OutputString() as outerr: 194 target = ffi.lib.LLVMPY_GetTargetFromTriple(triple.encode('utf8'), 195 outerr) 196 if not target: 197 raise RuntimeError(str(outerr)) 198 target = cls(target) 199 target._triple = triple 200 return target 201 202 @property 203 def name(self): 204 s = ffi.lib.LLVMPY_GetTargetName(self) 205 return _decode_string(s) 206 207 @property 208 def description(self): 209 s = ffi.lib.LLVMPY_GetTargetDescription(self) 210 return _decode_string(s) 211 212 @property 213 def triple(self): 214 return self._triple 215 216 def __str__(self): 217 return "<Target {0} ({1})>".format(self.name, self.description) 218 219 def create_target_machine(self, cpu='', features='', 220 opt=2, reloc='default', codemodel='jitdefault', 221 printmc=False, jit=False): 222 """ 223 Create a new TargetMachine for this target and the given options. 224 225 Specifying codemodel='default' will result in the use of the "small" 226 code model. Specifying codemodel='jitdefault' will result in the code 227 model being picked based on platform bitness (32="small", 64="large"). 228 229 The `printmc` option corresponds to llvm's `-print-machineinstrs`. 230 231 The `jit` option should be set when the target-machine is to be used 232 in a JIT engine. 233 """ 234 assert 0 <= opt <= 3 235 assert reloc in RELOC 236 assert codemodel in CODEMODEL 237 triple = self._triple 238 # MCJIT under Windows only supports ELF objects, see 239 # http://lists.llvm.org/pipermail/llvm-dev/2013-December/068341.html 240 # Note we still want to produce regular COFF files in AOT mode. 241 if os.name == 'nt' and codemodel == 'jitdefault': 242 triple += '-elf' 243 tm = ffi.lib.LLVMPY_CreateTargetMachine(self, 244 _encode_string(triple), 245 _encode_string(cpu), 246 _encode_string(features), 247 opt, 248 _encode_string(reloc), 249 _encode_string(codemodel), 250 int(printmc), 251 int(jit), 252 ) 253 if tm: 254 return TargetMachine(tm) 255 else: 256 raise RuntimeError("Cannot create target machine") 257 258 259class TargetMachine(ffi.ObjectRef): 260 261 def _dispose(self): 262 self._capi.LLVMPY_DisposeTargetMachine(self) 263 264 def add_analysis_passes(self, pm): 265 """ 266 Register analysis passes for this target machine with a pass manager. 267 """ 268 ffi.lib.LLVMPY_AddAnalysisPasses(self, pm) 269 270 def set_asm_verbosity(self, verbose): 271 """ 272 Set whether this target machine will emit assembly with human-readable 273 comments describing control flow, debug information, and so on. 274 """ 275 ffi.lib.LLVMPY_SetTargetMachineAsmVerbosity(self, verbose) 276 277 def emit_object(self, module): 278 """ 279 Represent the module as a code object, suitable for use with 280 the platform's linker. Returns a byte string. 281 """ 282 return self._emit_to_memory(module, use_object=True) 283 284 def emit_assembly(self, module): 285 """ 286 Return the raw assembler of the module, as a string. 287 288 llvm.initialize_native_asmprinter() must have been called first. 289 """ 290 return _decode_string(self._emit_to_memory(module, use_object=False)) 291 292 def _emit_to_memory(self, module, use_object=False): 293 """Returns bytes of object code of the module. 294 295 Args 296 ---- 297 use_object : bool 298 Emit object code or (if False) emit assembly code. 299 """ 300 with ffi.OutputString() as outerr: 301 mb = ffi.lib.LLVMPY_TargetMachineEmitToMemory(self, module, 302 int(use_object), 303 outerr) 304 if not mb: 305 raise RuntimeError(str(outerr)) 306 307 bufptr = ffi.lib.LLVMPY_GetBufferStart(mb) 308 bufsz = ffi.lib.LLVMPY_GetBufferSize(mb) 309 try: 310 return string_at(bufptr, bufsz) 311 finally: 312 ffi.lib.LLVMPY_DisposeMemoryBuffer(mb) 313 314 @property 315 def target_data(self): 316 return TargetData(ffi.lib.LLVMPY_CreateTargetMachineData(self)) 317 318 @property 319 def triple(self): 320 with ffi.OutputString() as out: 321 ffi.lib.LLVMPY_GetTargetMachineTriple(self, out) 322 return str(out) 323 324 325def has_svml(): 326 """ 327 Returns True if SVML was enabled at FFI support compile time. 328 """ 329 if ffi.lib.LLVMPY_HasSVMLSupport() == 0: 330 return False 331 else: 332 return True 333 334 335# ============================================================================ 336# FFI 337 338ffi.lib.LLVMPY_GetProcessTriple.argtypes = [POINTER(c_char_p)] 339 340ffi.lib.LLVMPY_GetHostCPUFeatures.argtypes = [POINTER(c_char_p)] 341ffi.lib.LLVMPY_GetHostCPUFeatures.restype = c_int 342 343ffi.lib.LLVMPY_GetDefaultTargetTriple.argtypes = [POINTER(c_char_p)] 344 345ffi.lib.LLVMPY_GetHostCPUName.argtypes = [POINTER(c_char_p)] 346 347ffi.lib.LLVMPY_GetTripleObjectFormat.argtypes = [c_char_p] 348ffi.lib.LLVMPY_GetTripleObjectFormat.restype = c_int 349 350ffi.lib.LLVMPY_CreateTargetData.argtypes = [c_char_p] 351ffi.lib.LLVMPY_CreateTargetData.restype = ffi.LLVMTargetDataRef 352 353ffi.lib.LLVMPY_CopyStringRepOfTargetData.argtypes = [ 354 ffi.LLVMTargetDataRef, 355 POINTER(c_char_p), 356] 357 358ffi.lib.LLVMPY_DisposeTargetData.argtypes = [ 359 ffi.LLVMTargetDataRef, 360] 361 362ffi.lib.LLVMPY_ABISizeOfType.argtypes = [ffi.LLVMTargetDataRef, 363 ffi.LLVMTypeRef] 364ffi.lib.LLVMPY_ABISizeOfType.restype = c_longlong 365 366ffi.lib.LLVMPY_OffsetOfElement.argtypes = [ffi.LLVMTargetDataRef, 367 ffi.LLVMTypeRef, 368 c_int] 369ffi.lib.LLVMPY_OffsetOfElement.restype = c_longlong 370 371ffi.lib.LLVMPY_ABISizeOfElementType.argtypes = [ffi.LLVMTargetDataRef, 372 ffi.LLVMTypeRef] 373ffi.lib.LLVMPY_ABISizeOfElementType.restype = c_longlong 374 375ffi.lib.LLVMPY_ABIAlignmentOfElementType.argtypes = [ffi.LLVMTargetDataRef, 376 ffi.LLVMTypeRef] 377ffi.lib.LLVMPY_ABIAlignmentOfElementType.restype = c_longlong 378 379ffi.lib.LLVMPY_GetTargetFromTriple.argtypes = [c_char_p, POINTER(c_char_p)] 380ffi.lib.LLVMPY_GetTargetFromTriple.restype = ffi.LLVMTargetRef 381 382ffi.lib.LLVMPY_GetTargetName.argtypes = [ffi.LLVMTargetRef] 383ffi.lib.LLVMPY_GetTargetName.restype = c_char_p 384 385ffi.lib.LLVMPY_GetTargetDescription.argtypes = [ffi.LLVMTargetRef] 386ffi.lib.LLVMPY_GetTargetDescription.restype = c_char_p 387 388ffi.lib.LLVMPY_CreateTargetMachine.argtypes = [ 389 ffi.LLVMTargetRef, 390 # Triple 391 c_char_p, 392 # CPU 393 c_char_p, 394 # Features 395 c_char_p, 396 # OptLevel 397 c_int, 398 # Reloc 399 c_char_p, 400 # CodeModel 401 c_char_p, 402 # PrintMC 403 c_int, 404 # JIT 405 c_int, 406] 407ffi.lib.LLVMPY_CreateTargetMachine.restype = ffi.LLVMTargetMachineRef 408 409ffi.lib.LLVMPY_DisposeTargetMachine.argtypes = [ffi.LLVMTargetMachineRef] 410 411ffi.lib.LLVMPY_GetTargetMachineTriple.argtypes = [ffi.LLVMTargetMachineRef, 412 POINTER(c_char_p)] 413 414ffi.lib.LLVMPY_SetTargetMachineAsmVerbosity.argtypes = [ 415 ffi.LLVMTargetMachineRef, c_int] 416 417ffi.lib.LLVMPY_AddAnalysisPasses.argtypes = [ 418 ffi.LLVMTargetMachineRef, 419 ffi.LLVMPassManagerRef, 420] 421 422ffi.lib.LLVMPY_TargetMachineEmitToMemory.argtypes = [ 423 ffi.LLVMTargetMachineRef, 424 ffi.LLVMModuleRef, 425 c_int, 426 POINTER(c_char_p), 427] 428ffi.lib.LLVMPY_TargetMachineEmitToMemory.restype = ffi.LLVMMemoryBufferRef 429 430ffi.lib.LLVMPY_GetBufferStart.argtypes = [ffi.LLVMMemoryBufferRef] 431ffi.lib.LLVMPY_GetBufferStart.restype = c_void_p 432 433ffi.lib.LLVMPY_GetBufferSize.argtypes = [ffi.LLVMMemoryBufferRef] 434ffi.lib.LLVMPY_GetBufferSize.restype = c_size_t 435 436ffi.lib.LLVMPY_DisposeMemoryBuffer.argtypes = [ffi.LLVMMemoryBufferRef] 437 438ffi.lib.LLVMPY_CreateTargetMachineData.argtypes = [ 439 ffi.LLVMTargetMachineRef, 440] 441ffi.lib.LLVMPY_CreateTargetMachineData.restype = ffi.LLVMTargetDataRef 442 443ffi.lib.LLVMPY_HasSVMLSupport.argtypes = [] 444ffi.lib.LLVMPY_HasSVMLSupport.restype = c_int 445