Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable querying constants and value kinds #936

Merged
merged 11 commits into from
Jul 26, 2023
53 changes: 53 additions & 0 deletions docs/source/user-guide/binding/value-references.rst
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,38 @@ Enumerations
* .. data:: dllexport


.. class:: ValueKind

The value kinds allowed are:

* .. data:: argument
* .. data:: basic_block
* .. data:: memory_use
* .. data:: memory_def
* .. data:: memory_phi
* .. data:: function
* .. data:: global_alias
* .. data:: global_ifunc
* .. data:: global_variable
* .. data:: block_address
* .. data:: constant_expr
* .. data:: constant_array
* .. data:: constant_struct
* .. data:: constant_vector
* .. data:: undef_value
* .. data:: constant_aggregate_zero
* .. data:: constant_data_array
* .. data:: constant_data_vector
* .. data:: constant_int
* .. data:: constant_fp
* .. data:: constant_pointer_null
* .. data:: constant_token_none
* .. data:: metadata_as_value
* .. data:: inline_asm
* .. data:: instruction
* .. data:: poison_value


The ValueRef class
------------------

Expand Down Expand Up @@ -110,6 +142,11 @@ The ValueRef class
style---a :class:`Visibility` instance---for
this value. This attribute can be set.

* .. attribute:: value_kind

The LLVM value kind---a :class:`ValueKind` instance---for
this value.

* .. attribute:: blocks

An iterator over the basic blocks in this function.
Expand Down Expand Up @@ -164,3 +201,19 @@ The ValueRef class
* .. attribute:: is_operand

The value is a instruction's operand.

* .. attribute:: is_constant

The value is a constant.

* .. method:: get_constant_value(self, signed_int=False, round_fp=False)

Return the constant value, either as a literal (for example, int
or float) when supported, or as a string otherwise. Keyword arguments
specify the preferences during conversion:

* If ``signed_int`` is True and the constant is an integer, returns a
signed integer.
* If ``round_fp`` True and the constant is a floating point value,
rounds the result upon accuracy loss (e.g., when querying an fp128
value). By default, raises an exception on accuracy loss.
37 changes: 37 additions & 0 deletions ffi/value.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,43 @@ LLVMPY_DisposeOperandsIter(LLVMOperandsIteratorRef GI) {
delete llvm::unwrap(GI);
}

API_EXPORT(bool)
LLVMPY_IsConstant(LLVMValueRef Val) { return LLVMIsConstant(Val); }

API_EXPORT(const uint64_t *)
LLVMPY_GetConstantIntRawValue(LLVMValueRef Val, bool *littleEndian) {
if (littleEndian) {
*littleEndian = llvm::sys::IsLittleEndianHost;
}
if (llvm::ConstantInt *CI =
llvm::dyn_cast<llvm::ConstantInt>((llvm::Value *)Val)) {
return CI->getValue().getRawData();
}
return nullptr;
}

API_EXPORT(unsigned)
LLVMPY_GetConstantIntNumWords(LLVMValueRef Val) {
if (llvm::ConstantInt *CI =
llvm::dyn_cast<llvm::ConstantInt>((llvm::Value *)Val)) {
return CI->getValue().getNumWords();
}
return 0;
}

API_EXPORT(double)
LLVMPY_GetConstantFPValue(LLVMValueRef Val, bool *losesInfo) {
LLVMBool losesInfo_internal;
double result = LLVMConstRealGetDouble(Val, &losesInfo_internal);
if (losesInfo) {
*losesInfo = losesInfo_internal;
}
return result;
}

API_EXPORT(int)
LLVMPY_GetValueKind(LLVMValueRef Val) { return (int)LLVMGetValueKind(Val); }

API_EXPORT(void)
LLVMPY_PrintValueToString(LLVMValueRef Val, const char **outstr) {
*outstr = LLVMPrintValueToString(Val);
Expand Down
108 changes: 107 additions & 1 deletion llvmlite/binding/value.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from ctypes import POINTER, c_char_p, c_int, c_size_t, c_uint, c_bool, c_void_p
from ctypes import (POINTER, byref, cast, c_char_p, c_double, c_int, c_size_t,
c_uint, c_uint64, c_bool, c_void_p)
import enum

from llvmlite.binding import ffi
Expand Down Expand Up @@ -43,6 +44,41 @@ class StorageClass(enum.IntEnum):
dllexport = 2


class ValueKind(enum.IntEnum):
# The LLVMValueKind enum from llvm-c/Core.h

argument = 0
basic_block = 1
memory_use = 2
memory_def = 3
memory_phi = 4

function = 5
global_alias = 6
global_ifunc = 7
global_variable = 8
block_address = 9
constant_expr = 10
constant_array = 11
constant_struct = 12
constant_vector = 13

undef_value = 14
constant_aggregate_zero = 15
constant_data_array = 16
constant_data_vector = 17
constant_int = 18
constant_fp = 19
constant_pointer_null = 20
constant_token_none = 21

metadata_as_value = 22
inline_asm = 23

instruction = 24
poison_value = 25


class TypeRef(ffi.ObjectRef):
"""A weak reference to a LLVM type
"""
Expand Down Expand Up @@ -140,6 +176,14 @@ def is_instruction(self):
def is_operand(self):
return self._kind == 'operand'

@property
def is_constant(self):
tbennun marked this conversation as resolved.
Show resolved Hide resolved
return bool(ffi.lib.LLVMPY_IsConstant(self))

@property
def value_kind(self):
tbennun marked this conversation as resolved.
Show resolved Hide resolved
return ValueKind(ffi.lib.LLVMPY_GetValueKind(self))

@property
def name(self):
return _decode_string(ffi.lib.LLVMPY_GetValueName(self))
Expand Down Expand Up @@ -299,6 +343,51 @@ def opcode(self):
% (self._kind,))
return ffi.ret_string(ffi.lib.LLVMPY_GetOpcodeName(self))

def get_constant_value(self, signed_int=False, round_fp=False):
"""
Return the constant value, either as a literal (when supported)
or as a string.

Parameters
-----------
signed_int : bool
if True and the constant is an integer, returns a signed version
round_fp : bool
if True and the constant is a floating point value, rounds the
result upon accuracy loss (e.g., when querying an fp128 value).
By default, raises an exception on accuracy loss
"""
if not self.is_constant:
raise ValueError('expected constant value, got %s'
tbennun marked this conversation as resolved.
Show resolved Hide resolved
% (self._kind,))

if self.value_kind == ValueKind.constant_int:
# Python integers are also arbitrary-precision
little_endian = c_bool(False)
words = ffi.lib.LLVMPY_GetConstantIntNumWords(self)
ptr = ffi.lib.LLVMPY_GetConstantIntRawValue(
self, byref(little_endian))
asbytes = bytes(cast(ptr, POINTER(c_uint64 * words)).contents)
return int.from_bytes(
asbytes,
('little' if little_endian.value else 'big'),
signed=signed_int,
)
elif self.value_kind == ValueKind.constant_fp:
# Convert floating-point values to double-precision (Python float)
accuracy_loss = c_bool(False)
value = ffi.lib.LLVMPY_GetConstantFPValue(self,
byref(accuracy_loss))
if accuracy_loss.value and not round_fp:
raise ValueError(
'Accuracy loss encountered in conversion of constant '
f'value {str(self)}')

return value

# Otherwise, return the IR string
return str(self)
tbennun marked this conversation as resolved.
Show resolved Hide resolved


class _ValueIterator(ffi.ObjectRef):

Expand Down Expand Up @@ -516,3 +605,20 @@ def _next(self):

ffi.lib.LLVMPY_GetOpcodeName.argtypes = [ffi.LLVMValueRef]
ffi.lib.LLVMPY_GetOpcodeName.restype = c_void_p

ffi.lib.LLVMPY_IsConstant.argtypes = [ffi.LLVMValueRef]
ffi.lib.LLVMPY_IsConstant.restype = c_bool

ffi.lib.LLVMPY_GetValueKind.argtypes = [ffi.LLVMValueRef]
ffi.lib.LLVMPY_GetValueKind.restype = c_int

ffi.lib.LLVMPY_GetConstantIntRawValue.argtypes = [ffi.LLVMValueRef,
POINTER(c_bool)]
ffi.lib.LLVMPY_GetConstantIntRawValue.restype = POINTER(c_uint64)

ffi.lib.LLVMPY_GetConstantIntNumWords.argtypes = [ffi.LLVMValueRef]
ffi.lib.LLVMPY_GetConstantIntNumWords.restype = c_uint

ffi.lib.LLVMPY_GetConstantFPValue.argtypes = [ffi.LLVMValueRef,
POINTER(c_bool)]
ffi.lib.LLVMPY_GetConstantFPValue.restype = c_double
Loading