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