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