1from ctypes import POINTER, c_char_p, c_int, c_size_t, c_uint, c_bool, c_void_p
2import enum
3
4from llvmlite.binding import ffi
5from llvmlite.binding.common import _decode_string, _encode_string
6
7
8class Linkage(enum.IntEnum):
9    # The LLVMLinkage enum from llvm-c/Core.h
10
11    external = 0
12    available_externally = 1
13    linkonce_any = 2
14    linkonce_odr = 3
15    linkonce_odr_autohide = 4
16    weak_any = 5
17    weak_odr = 6
18    appending = 7
19    internal = 8
20    private = 9
21    dllimport = 10
22    dllexport = 11
23    external_weak = 12
24    ghost = 13
25    common = 14
26    linker_private = 15
27    linker_private_weak = 16
28
29
30class Visibility(enum.IntEnum):
31    # The LLVMVisibility enum from llvm-c/Core.h
32
33    default = 0
34    hidden = 1
35    protected = 2
36
37
38class StorageClass(enum.IntEnum):
39    # The LLVMDLLStorageClass enum from llvm-c/Core.h
40
41    default = 0
42    dllimport = 1
43    dllexport = 2
44
45
46class TypeRef(ffi.ObjectRef):
47    """A weak reference to a LLVM type
48    """
49    @property
50    def name(self):
51        """
52        Get type name
53        """
54        return ffi.ret_string(ffi.lib.LLVMPY_GetTypeName(self))
55
56    @property
57    def is_pointer(self):
58        """
59        Returns true is the type is a pointer type.
60        """
61        return ffi.lib.LLVMPY_TypeIsPointer(self)
62
63    @property
64    def element_type(self):
65        """
66        Returns the pointed-to type. When the type is not a pointer,
67        raises exception.
68        """
69        if not self.is_pointer:
70            raise ValueError("Type {} is not a pointer".format(self))
71        return TypeRef(ffi.lib.LLVMPY_GetElementType(self))
72
73    def __str__(self):
74        return ffi.ret_string(ffi.lib.LLVMPY_PrintType(self))
75
76
77class ValueRef(ffi.ObjectRef):
78    """A weak reference to a LLVM value.
79    """
80
81    def __init__(self, ptr, kind, parents):
82        self._kind = kind
83        self._parents = parents
84        ffi.ObjectRef.__init__(self, ptr)
85
86    def __str__(self):
87        with ffi.OutputString() as outstr:
88            ffi.lib.LLVMPY_PrintValueToString(self, outstr)
89            return str(outstr)
90
91    @property
92    def module(self):
93        """
94        The module this function or global variable value was obtained from.
95        """
96        return self._parents.get('module')
97
98    @property
99    def function(self):
100        """
101        The function this argument or basic block value was obtained from.
102        """
103        return self._parents.get('function')
104
105    @property
106    def block(self):
107        """
108        The block this instruction value was obtained from.
109        """
110        return self._parents.get('block')
111
112    @property
113    def instruction(self):
114        """
115        The instruction this operand value was obtained from.
116        """
117        return self._parents.get('instruction')
118
119    @property
120    def is_global(self):
121        return self._kind == 'global'
122
123    @property
124    def is_function(self):
125        return self._kind == 'function'
126
127    @property
128    def is_block(self):
129        return self._kind == 'block'
130
131    @property
132    def is_argument(self):
133        return self._kind == 'argument'
134
135    @property
136    def is_instruction(self):
137        return self._kind == 'instruction'
138
139    @property
140    def is_operand(self):
141        return self._kind == 'operand'
142
143    @property
144    def name(self):
145        return _decode_string(ffi.lib.LLVMPY_GetValueName(self))
146
147    @name.setter
148    def name(self, val):
149        ffi.lib.LLVMPY_SetValueName(self, _encode_string(val))
150
151    @property
152    def linkage(self):
153        return Linkage(ffi.lib.LLVMPY_GetLinkage(self))
154
155    @linkage.setter
156    def linkage(self, value):
157        if not isinstance(value, Linkage):
158            value = Linkage[value]
159        ffi.lib.LLVMPY_SetLinkage(self, value)
160
161    @property
162    def visibility(self):
163        return Visibility(ffi.lib.LLVMPY_GetVisibility(self))
164
165    @visibility.setter
166    def visibility(self, value):
167        if not isinstance(value, Visibility):
168            value = Visibility[value]
169        ffi.lib.LLVMPY_SetVisibility(self, value)
170
171    @property
172    def storage_class(self):
173        return StorageClass(ffi.lib.LLVMPY_GetDLLStorageClass(self))
174
175    @storage_class.setter
176    def storage_class(self, value):
177        if not isinstance(value, StorageClass):
178            value = StorageClass[value]
179        ffi.lib.LLVMPY_SetDLLStorageClass(self, value)
180
181    def add_function_attribute(self, attr):
182        """Only works on function value
183
184        Parameters
185        -----------
186        attr : str
187            attribute name
188        """
189        if not self.is_function:
190            raise ValueError('expected function value, got %s' % (self._kind,))
191        attrname = str(attr)
192        attrval = ffi.lib.LLVMPY_GetEnumAttributeKindForName(
193            _encode_string(attrname), len(attrname))
194        if attrval == 0:
195            raise ValueError('no such attribute {!r}'.format(attrname))
196        ffi.lib.LLVMPY_AddFunctionAttr(self, attrval)
197
198    @property
199    def type(self):
200        """
201        This value's LLVM type.
202        """
203        # XXX what does this return?
204        return TypeRef(ffi.lib.LLVMPY_TypeOf(self))
205
206    @property
207    def is_declaration(self):
208        """
209        Whether this value (presumably global) is defined in the current
210        module.
211        """
212        if not (self.is_global or self.is_function):
213            raise ValueError('expected global or function value, got %s'
214                             % (self._kind,))
215        return ffi.lib.LLVMPY_IsDeclaration(self)
216
217    @property
218    def attributes(self):
219        """
220        Return an iterator over this value's attributes.
221        The iterator will yield a string for each attribute.
222        """
223        itr = iter(())
224        if self.is_function:
225            it = ffi.lib.LLVMPY_FunctionAttributesIter(self)
226            itr = _AttributeListIterator(it)
227        elif self.is_instruction:
228            if self.opcode == 'call':
229                it = ffi.lib.LLVMPY_CallInstAttributesIter(self)
230                itr = _AttributeListIterator(it)
231            elif self.opcode == 'invoke':
232                it = ffi.lib.LLVMPY_InvokeInstAttributesIter(self)
233                itr = _AttributeListIterator(it)
234        elif self.is_global:
235            it = ffi.lib.LLVMPY_GlobalAttributesIter(self)
236            itr = _AttributeSetIterator(it)
237        elif self.is_argument:
238            it = ffi.lib.LLVMPY_ArgumentAttributesIter(self)
239            itr = _AttributeSetIterator(it)
240        return itr
241
242    @property
243    def blocks(self):
244        """
245        Return an iterator over this function's blocks.
246        The iterator will yield a ValueRef for each block.
247        """
248        if not self.is_function:
249            raise ValueError('expected function value, got %s' % (self._kind,))
250        it = ffi.lib.LLVMPY_FunctionBlocksIter(self)
251        parents = self._parents.copy()
252        parents.update(function=self)
253        return _BlocksIterator(it, parents)
254
255    @property
256    def arguments(self):
257        """
258        Return an iterator over this function's arguments.
259        The iterator will yield a ValueRef for each argument.
260        """
261        if not self.is_function:
262            raise ValueError('expected function value, got %s' % (self._kind,))
263        it = ffi.lib.LLVMPY_FunctionArgumentsIter(self)
264        parents = self._parents.copy()
265        parents.update(function=self)
266        return _ArgumentsIterator(it, parents)
267
268    @property
269    def instructions(self):
270        """
271        Return an iterator over this block's instructions.
272        The iterator will yield a ValueRef for each instruction.
273        """
274        if not self.is_block:
275            raise ValueError('expected block value, got %s' % (self._kind,))
276        it = ffi.lib.LLVMPY_BlockInstructionsIter(self)
277        parents = self._parents.copy()
278        parents.update(block=self)
279        return _InstructionsIterator(it, parents)
280
281    @property
282    def operands(self):
283        """
284        Return an iterator over this instruction's operands.
285        The iterator will yield a ValueRef for each operand.
286        """
287        if not self.is_instruction:
288            raise ValueError('expected instruction value, got %s'
289                             % (self._kind,))
290        it = ffi.lib.LLVMPY_InstructionOperandsIter(self)
291        parents = self._parents.copy()
292        parents.update(instruction=self)
293        return _OperandsIterator(it, parents)
294
295    @property
296    def opcode(self):
297        if not self.is_instruction:
298            raise ValueError('expected instruction value, got %s'
299                             % (self._kind,))
300        return ffi.ret_string(ffi.lib.LLVMPY_GetOpcodeName(self))
301
302
303class _ValueIterator(ffi.ObjectRef):
304
305    kind = None  # derived classes must specify the Value kind value
306    # as class attribute
307
308    def __init__(self, ptr, parents):
309        ffi.ObjectRef.__init__(self, ptr)
310        # Keep parent objects (module, function, etc) alive
311        self._parents = parents
312        if self.kind is None:
313            raise NotImplementedError('%s must specify kind attribute'
314                                      % (type(self).__name__,))
315
316    def __next__(self):
317        vp = self._next()
318        if vp:
319            return ValueRef(vp, self.kind, self._parents)
320        else:
321            raise StopIteration
322
323    next = __next__
324
325    def __iter__(self):
326        return self
327
328
329class _AttributeIterator(ffi.ObjectRef):
330
331    def __next__(self):
332        vp = self._next()
333        if vp:
334            return vp
335        else:
336            raise StopIteration
337
338    next = __next__
339
340    def __iter__(self):
341        return self
342
343
344class _AttributeListIterator(_AttributeIterator):
345
346    def _dispose(self):
347        self._capi.LLVMPY_DisposeAttributeListIter(self)
348
349    def _next(self):
350        return ffi.ret_bytes(ffi.lib.LLVMPY_AttributeListIterNext(self))
351
352
353class _AttributeSetIterator(_AttributeIterator):
354
355    def _dispose(self):
356        self._capi.LLVMPY_DisposeAttributeSetIter(self)
357
358    def _next(self):
359        return ffi.ret_bytes(ffi.lib.LLVMPY_AttributeSetIterNext(self))
360
361
362class _BlocksIterator(_ValueIterator):
363
364    kind = 'block'
365
366    def _dispose(self):
367        self._capi.LLVMPY_DisposeBlocksIter(self)
368
369    def _next(self):
370        return ffi.lib.LLVMPY_BlocksIterNext(self)
371
372
373class _ArgumentsIterator(_ValueIterator):
374
375    kind = 'argument'
376
377    def _dispose(self):
378        self._capi.LLVMPY_DisposeArgumentsIter(self)
379
380    def _next(self):
381        return ffi.lib.LLVMPY_ArgumentsIterNext(self)
382
383
384class _InstructionsIterator(_ValueIterator):
385
386    kind = 'instruction'
387
388    def _dispose(self):
389        self._capi.LLVMPY_DisposeInstructionsIter(self)
390
391    def _next(self):
392        return ffi.lib.LLVMPY_InstructionsIterNext(self)
393
394
395class _OperandsIterator(_ValueIterator):
396
397    kind = 'operand'
398
399    def _dispose(self):
400        self._capi.LLVMPY_DisposeOperandsIter(self)
401
402    def _next(self):
403        return ffi.lib.LLVMPY_OperandsIterNext(self)
404
405
406# FFI
407
408ffi.lib.LLVMPY_PrintValueToString.argtypes = [
409    ffi.LLVMValueRef,
410    POINTER(c_char_p)
411]
412
413ffi.lib.LLVMPY_GetGlobalParent.argtypes = [ffi.LLVMValueRef]
414ffi.lib.LLVMPY_GetGlobalParent.restype = ffi.LLVMModuleRef
415
416ffi.lib.LLVMPY_GetValueName.argtypes = [ffi.LLVMValueRef]
417ffi.lib.LLVMPY_GetValueName.restype = c_char_p
418
419ffi.lib.LLVMPY_SetValueName.argtypes = [ffi.LLVMValueRef, c_char_p]
420
421ffi.lib.LLVMPY_TypeOf.argtypes = [ffi.LLVMValueRef]
422ffi.lib.LLVMPY_TypeOf.restype = ffi.LLVMTypeRef
423
424
425ffi.lib.LLVMPY_PrintType.argtypes = [ffi.LLVMTypeRef]
426ffi.lib.LLVMPY_PrintType.restype = c_void_p
427
428ffi.lib.LLVMPY_TypeIsPointer.argtypes = [ffi.LLVMTypeRef]
429ffi.lib.LLVMPY_TypeIsPointer.restype = c_bool
430
431ffi.lib.LLVMPY_GetElementType.argtypes = [ffi.LLVMTypeRef]
432ffi.lib.LLVMPY_GetElementType.restype = ffi.LLVMTypeRef
433
434
435ffi.lib.LLVMPY_GetTypeName.argtypes = [ffi.LLVMTypeRef]
436ffi.lib.LLVMPY_GetTypeName.restype = c_void_p
437
438ffi.lib.LLVMPY_GetLinkage.argtypes = [ffi.LLVMValueRef]
439ffi.lib.LLVMPY_GetLinkage.restype = c_int
440
441ffi.lib.LLVMPY_SetLinkage.argtypes = [ffi.LLVMValueRef, c_int]
442
443ffi.lib.LLVMPY_GetVisibility.argtypes = [ffi.LLVMValueRef]
444ffi.lib.LLVMPY_GetVisibility.restype = c_int
445
446ffi.lib.LLVMPY_SetVisibility.argtypes = [ffi.LLVMValueRef, c_int]
447
448ffi.lib.LLVMPY_GetDLLStorageClass.argtypes = [ffi.LLVMValueRef]
449ffi.lib.LLVMPY_GetDLLStorageClass.restype = c_int
450
451ffi.lib.LLVMPY_SetDLLStorageClass.argtypes = [ffi.LLVMValueRef, c_int]
452
453ffi.lib.LLVMPY_GetEnumAttributeKindForName.argtypes = [c_char_p, c_size_t]
454ffi.lib.LLVMPY_GetEnumAttributeKindForName.restype = c_uint
455
456ffi.lib.LLVMPY_AddFunctionAttr.argtypes = [ffi.LLVMValueRef, c_uint]
457
458ffi.lib.LLVMPY_IsDeclaration.argtypes = [ffi.LLVMValueRef]
459ffi.lib.LLVMPY_IsDeclaration.restype = c_int
460
461ffi.lib.LLVMPY_FunctionAttributesIter.argtypes = [ffi.LLVMValueRef]
462ffi.lib.LLVMPY_FunctionAttributesIter.restype = ffi.LLVMAttributeListIterator
463
464ffi.lib.LLVMPY_CallInstAttributesIter.argtypes = [ffi.LLVMValueRef]
465ffi.lib.LLVMPY_CallInstAttributesIter.restype = ffi.LLVMAttributeListIterator
466
467ffi.lib.LLVMPY_InvokeInstAttributesIter.argtypes = [ffi.LLVMValueRef]
468ffi.lib.LLVMPY_InvokeInstAttributesIter.restype = ffi.LLVMAttributeListIterator
469
470ffi.lib.LLVMPY_GlobalAttributesIter.argtypes = [ffi.LLVMValueRef]
471ffi.lib.LLVMPY_GlobalAttributesIter.restype = ffi.LLVMAttributeSetIterator
472
473ffi.lib.LLVMPY_ArgumentAttributesIter.argtypes = [ffi.LLVMValueRef]
474ffi.lib.LLVMPY_ArgumentAttributesIter.restype = ffi.LLVMAttributeSetIterator
475
476ffi.lib.LLVMPY_FunctionBlocksIter.argtypes = [ffi.LLVMValueRef]
477ffi.lib.LLVMPY_FunctionBlocksIter.restype = ffi.LLVMBlocksIterator
478
479ffi.lib.LLVMPY_FunctionArgumentsIter.argtypes = [ffi.LLVMValueRef]
480ffi.lib.LLVMPY_FunctionArgumentsIter.restype = ffi.LLVMArgumentsIterator
481
482ffi.lib.LLVMPY_BlockInstructionsIter.argtypes = [ffi.LLVMValueRef]
483ffi.lib.LLVMPY_BlockInstructionsIter.restype = ffi.LLVMInstructionsIterator
484
485ffi.lib.LLVMPY_InstructionOperandsIter.argtypes = [ffi.LLVMValueRef]
486ffi.lib.LLVMPY_InstructionOperandsIter.restype = ffi.LLVMOperandsIterator
487
488ffi.lib.LLVMPY_DisposeAttributeListIter.argtypes = [
489    ffi.LLVMAttributeListIterator]
490
491ffi.lib.LLVMPY_DisposeAttributeSetIter.argtypes = [ffi.LLVMAttributeSetIterator]
492
493ffi.lib.LLVMPY_DisposeBlocksIter.argtypes = [ffi.LLVMBlocksIterator]
494
495ffi.lib.LLVMPY_DisposeInstructionsIter.argtypes = [ffi.LLVMInstructionsIterator]
496
497ffi.lib.LLVMPY_DisposeOperandsIter.argtypes = [ffi.LLVMOperandsIterator]
498
499ffi.lib.LLVMPY_AttributeListIterNext.argtypes = [ffi.LLVMAttributeListIterator]
500ffi.lib.LLVMPY_AttributeListIterNext.restype = c_void_p
501
502ffi.lib.LLVMPY_AttributeSetIterNext.argtypes = [ffi.LLVMAttributeSetIterator]
503ffi.lib.LLVMPY_AttributeSetIterNext.restype = c_void_p
504
505ffi.lib.LLVMPY_BlocksIterNext.argtypes = [ffi.LLVMBlocksIterator]
506ffi.lib.LLVMPY_BlocksIterNext.restype = ffi.LLVMValueRef
507
508ffi.lib.LLVMPY_ArgumentsIterNext.argtypes = [ffi.LLVMArgumentsIterator]
509ffi.lib.LLVMPY_ArgumentsIterNext.restype = ffi.LLVMValueRef
510
511ffi.lib.LLVMPY_InstructionsIterNext.argtypes = [ffi.LLVMInstructionsIterator]
512ffi.lib.LLVMPY_InstructionsIterNext.restype = ffi.LLVMValueRef
513
514ffi.lib.LLVMPY_OperandsIterNext.argtypes = [ffi.LLVMOperandsIterator]
515ffi.lib.LLVMPY_OperandsIterNext.restype = ffi.LLVMValueRef
516
517ffi.lib.LLVMPY_GetOpcodeName.argtypes = [ffi.LLVMValueRef]
518ffi.lib.LLVMPY_GetOpcodeName.restype = c_void_p
519