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