diff --git a/docs/source/user-guide/binding/file-system.rst b/docs/source/user-guide/binding/file-system.rst new file mode 100644 index 000000000..2ed25460a --- /dev/null +++ b/docs/source/user-guide/binding/file-system.rst @@ -0,0 +1,17 @@ +=========== +File system +=========== + +.. currentmodule:: llvmlite.binding + +.. function:: getDeviceForFile(path) + + Gets the llvm::sys::fs::UniqueID associated with the given file path + and then returns the device associated with that path through a call to + LLVM's getDevice method on that UniqueID. + +.. function:: getFileIdForFile(path) + + Gets the llvm::sys::fs::UniqueID associated with the given file path + and then returns the file ID associated with that path through a call to + LLVM's getFile method on that UniqueID. diff --git a/docs/source/user-guide/binding/index.rst b/docs/source/user-guide/binding/index.rst index 5199ef149..a5c91a8d6 100644 --- a/docs/source/user-guide/binding/index.rst +++ b/docs/source/user-guide/binding/index.rst @@ -31,6 +31,7 @@ implement Numba_'s JIT compiler. optimization-passes analysis-utilities pass_timings + file-system misc examples diff --git a/docs/source/user-guide/ir/ir-builder.rst b/docs/source/user-guide/ir/ir-builder.rst index 3c6e2cf7d..02ad67886 100644 --- a/docs/source/user-guide/ir/ir-builder.rst +++ b/docs/source/user-guide/ir/ir-builder.rst @@ -517,7 +517,7 @@ Function call --------------- .. method:: IRBuilder.call(fn, args, name='', cconv=None, tail=None, \ - fastmath=(), attrs=(), arg_attrs=None) + fastmath=(), attrs=(), arg_attrs=None, tags=None) Call function *fn* with arguments *args*, a sequence of values. @@ -548,6 +548,10 @@ Function call the attributes to attach to the respective argument at this call site. If an index is not present in the dictionary, or *arg_attrs* is missing entirely, no attributes are emitted for the given argument. + * *tags* is a string containing an LLVM operand bundle to + associate with this call. + For more information about operand bundles, see the + `official LLVM documentation `_. If some attributes, such as ``sret``, are specified at the function declaration, they must also be specified at each call site for @@ -682,7 +686,7 @@ Inline assembler * *name* is the optional name of the returned LLVM value. For more information about these parameters, see the - `official LLVM documentation `_. + `LLVM operand bundle documentation `_. EXAMPLE: Adding 2 64-bit values on x86:: diff --git a/docs/source/user-guide/ir/types.rst b/docs/source/user-guide/ir/types.rst index 52d04d251..420d2a300 100644 --- a/docs/source/user-guide/ir/types.rst +++ b/docs/source/user-guide/ir/types.rst @@ -198,3 +198,17 @@ Other types NOTE: This class was previously called "MetaData," but it was renamed for clarity. + + +.. class:: TokenType + + The type for tokens which, from the LLVM language reference, are used + when a value is associated with an instruction but all uses of the + value must not attempt to introspect or obscure it. One use of this + is to associate matching pseudo-calls that demarcate a region of code. + + EXAMPLE:: + + token_type = ir.TokenType() + start_region_fnty = ir.FunctionType(token_type, (...)) + end_region_fnty = ir.FunctionType(ir.Type.void(), (token_type,)) diff --git a/ffi/CMakeLists.txt b/ffi/CMakeLists.txt index 907b1e1ec..4e8a3f673 100755 --- a/ffi/CMakeLists.txt +++ b/ffi/CMakeLists.txt @@ -44,7 +44,7 @@ endif() add_library(llvmlite SHARED assembly.cpp bitcode.cpp core.cpp initfini.cpp module.cpp value.cpp executionengine.cpp transforms.cpp type.cpp passmanagers.cpp targets.cpp dylib.cpp linker.cpp object_file.cpp - custom_passes.cpp orcjit.cpp memorymanager.cpp) + custom_passes.cpp orcjit.cpp file_system.cpp memorymanager.cpp) # Find the libraries that correspond to the LLVM components # that we wish to use. diff --git a/ffi/Makefile.freebsd b/ffi/Makefile.freebsd index 09635d910..0f09376b7 100644 --- a/ffi/Makefile.freebsd +++ b/ffi/Makefile.freebsd @@ -12,7 +12,7 @@ INCLUDE = core.h SRC = assembly.cpp bitcode.cpp core.cpp initfini.cpp module.cpp value.cpp \ executionengine.cpp transforms.cpp passmanagers.cpp type.cpp targets.cpp \ dylib.cpp linker.cpp object_file.cpp orcjit.cpp custom_passes.cpp \ - memorymanager.cpp + memorymanager.cpp file_system.cpp OUTPUT = libllvmlite.so all: $(OUTPUT) diff --git a/ffi/Makefile.linux b/ffi/Makefile.linux index bc80d6112..827764dbc 100644 --- a/ffi/Makefile.linux +++ b/ffi/Makefile.linux @@ -13,7 +13,8 @@ LIBS = $(LLVM_LIBS) INCLUDE = core.h OBJ = assembly.o bitcode.o core.o initfini.o module.o value.o \ executionengine.o transforms.o passmanagers.o targets.o type.o dylib.o \ - linker.o object_file.o custom_passes.o orcjit.o memorymanager.o + linker.o object_file.o custom_passes.o orcjit.o memorymanager.o \ + file_system.o OUTPUT = libllvmlite.so all: $(OUTPUT) diff --git a/ffi/Makefile.osx b/ffi/Makefile.osx index afe4f3bac..d5515459d 100644 --- a/ffi/Makefile.osx +++ b/ffi/Makefile.osx @@ -9,7 +9,7 @@ INCLUDE = core.h SRC = assembly.cpp bitcode.cpp core.cpp initfini.cpp module.cpp value.cpp \ executionengine.cpp transforms.cpp passmanagers.cpp targets.cpp type.cpp \ dylib.cpp linker.cpp object_file.cpp custom_passes.cpp orcjit.cpp \ - memorymanager.cpp + memorymanager.cpp file_system.cpp OUTPUT = libllvmlite.dylib MACOSX_DEPLOYMENT_TARGET ?= 10.9 diff --git a/ffi/file_system.cpp b/ffi/file_system.cpp new file mode 100644 index 000000000..8e106e422 --- /dev/null +++ b/ffi/file_system.cpp @@ -0,0 +1,27 @@ +#include "core.h" + +#include "llvm-c/ExecutionEngine.h" +#include "llvm-c/Object.h" + +#include "llvm/Object/ObjectFile.h" +#include "llvm/Support/FileSystem.h" + +#include + +extern "C" { + +API_EXPORT(uint64_t) +LLVMPY_GetDeviceForFile(const char *path) { + llvm::sys::fs::UniqueID ID; + llvm::sys::fs::getUniqueID(path, ID); + return ID.getDevice(); +} + +API_EXPORT(uint64_t) +LLVMPY_GetFileIdForFile(const char *path) { + llvm::sys::fs::UniqueID ID; + llvm::sys::fs::getUniqueID(path, ID); + return ID.getFile(); +} + +} // end extern C diff --git a/llvmlite/binding/__init__.py b/llvmlite/binding/__init__.py index efc234075..bc2ee5dca 100644 --- a/llvmlite/binding/__init__.py +++ b/llvmlite/binding/__init__.py @@ -16,3 +16,4 @@ from .object_file import * from .context import * from .orcjit import * +from .file_system import * diff --git a/llvmlite/binding/file_system.py b/llvmlite/binding/file_system.py new file mode 100644 index 000000000..3e2fcda25 --- /dev/null +++ b/llvmlite/binding/file_system.py @@ -0,0 +1,20 @@ +from . import ffi +from ctypes import c_char_p, c_uint64 +from .common import _encode_string + + +def getDeviceForFile(path): + cp = _encode_string(path) + return ffi.lib.LLVMPY_GetDeviceForFile(cp) + + +def getFileIdForFile(path): + cp = _encode_string(path) + return ffi.lib.LLVMPY_GetFileIdForFile(cp) + + +ffi.lib.LLVMPY_GetDeviceForFile.argtypes = [c_char_p] +ffi.lib.LLVMPY_GetDeviceForFile.restype = c_uint64 + +ffi.lib.LLVMPY_GetFileIdForFile.argtypes = [c_char_p] +ffi.lib.LLVMPY_GetFileIdForFile.restype = c_uint64 diff --git a/llvmlite/ir/builder.py b/llvmlite/ir/builder.py index f62476ca8..9d2dee4e3 100644 --- a/llvmlite/ir/builder.py +++ b/llvmlite/ir/builder.py @@ -771,12 +771,17 @@ def store(self, value, ptr, align=None): Store value to pointer, with optional guaranteed alignment: *ptr = name """ - if not isinstance(ptr.type, types.PointerType): - msg = "cannot store to value of type %s (%r): not a pointer" - raise TypeError(msg % (ptr.type, str(ptr))) - if ptr.type.pointee != value.type: - raise TypeError("cannot store %s to %s: mismatching types" - % (value.type, ptr.type)) + if isinstance(ptr.type, types.TokenType): + if ptr.type != value.type: + raise TypeError("cannot store %s to %s: mismatching types" + % (value.type, ptr.type)) + else: + if not isinstance(ptr.type, types.PointerType): + msg = "cannot store to value of type %s (%r): not a pointer" + raise TypeError(msg % (ptr.type, str(ptr))) + if ptr.type.pointee != value.type: + raise TypeError("cannot store %s to %s: mismatching types" + % (value.type, ptr.type)) st = instructions.StoreInstr(self.block, value, ptr) st.align = align self._insert(st) @@ -873,14 +878,15 @@ def resume(self, landingpad): # Call APIs def call(self, fn, args, name='', cconv=None, tail=False, fastmath=(), - attrs=(), arg_attrs=None): + attrs=(), arg_attrs=None, tags=None): """ Call function *fn* with *args*: name = fn(args...) """ inst = instructions.CallInstr(self.block, fn, args, name=name, cconv=cconv, tail=tail, fastmath=fastmath, - attrs=attrs, arg_attrs=arg_attrs) + attrs=attrs, arg_attrs=arg_attrs, + tags=tags) self._insert(inst) return inst diff --git a/llvmlite/ir/instructions.py b/llvmlite/ir/instructions.py index c6d488aae..567862406 100644 --- a/llvmlite/ir/instructions.py +++ b/llvmlite/ir/instructions.py @@ -66,7 +66,7 @@ class FastMathFlags(AttributeSet): class CallInstr(Instruction): def __init__(self, parent, func, args, name='', cconv=None, tail=None, - fastmath=(), attrs=(), arg_attrs=None): + fastmath=(), attrs=(), arg_attrs=None, tags=None): self.cconv = (func.calling_convention if cconv is None and isinstance(func, Function) else cconv) @@ -83,6 +83,7 @@ def __init__(self, parent, func, args, name='', cconv=None, tail=None, self.tail = tail self.fastmath = FastMathFlags(fastmath) self.attributes = CallInstrAttributes(attrs) + self.tags = tags self.arg_attributes = {} if arg_attrs: for idx, attrs in arg_attrs.items(): @@ -161,15 +162,19 @@ def descr_arg(i, a): fm_attrs = ' ' + ' '.join(self.fastmath._to_list(fnty.return_type))\ if self.fastmath else '' - buf.append("{tail}{op}{fastmath} {callee}({args}){attr}{meta}\n".format( + tag_attrs = ' ' + self.tags if self.tags else '' + + msg = "{tail}{op}{fastmath} {callee}({args}){attr}{tags}{meta}\n" + buf.append(msg.format( tail=tail_marker, op=self.opname, - callee=callee_ref, fastmath=fm_attrs, + callee=callee_ref, args=args, attr=fn_attrs, + tags=tag_attrs, meta=(self._stringify_metadata(leading_comma=True) - if add_metadata else ""), + if add_metadata else "") )) def descr(self, buf): @@ -528,6 +533,9 @@ def descr(self, buf): if self.metadata: buf.append(self._stringify_metadata(leading_comma=True)) + def get_decl(self): + return '{0} %"{1}"'.format(self.type, self._get_name()) + class GEPInstr(Instruction): def __init__(self, parent, ptr, indices, inbounds, name): diff --git a/llvmlite/ir/module.py b/llvmlite/ir/module.py index 464f91ec3..113965764 100644 --- a/llvmlite/ir/module.py +++ b/llvmlite/ir/module.py @@ -17,6 +17,7 @@ def __init__(self, name='', context=context.global_context): self.namedmetadata = {} # Cache for metadata node deduplication self._metadatacache = {} + self.device_triples = None def _fix_metadata_operands(self, operands): fixed_ops = [] @@ -236,8 +237,10 @@ def __repr__(self): lines += [ '; ModuleID = "%s"' % (self.name,), 'target triple = "%s"' % (self.triple,), - 'target datalayout = "%s"' % (self.data_layout,), - ''] + 'target datalayout = "%s"' % (self.data_layout,)] + if self.device_triples is not None: + lines += ['target device_triples = "%s"' % self.device_triples] + lines += [''] # Body lines += self._get_body_lines() # Metadata diff --git a/llvmlite/ir/types.py b/llvmlite/ir/types.py index 707246e61..e01b75e81 100644 --- a/llvmlite/ir/types.py +++ b/llvmlite/ir/types.py @@ -162,6 +162,26 @@ def __hash__(self): return hash(VoidType) +class TokenType(Type): + """ + The type for tokens. From the LLVM Language Reference. + + 'The token type is used when a value is associated with an + instruction but all uses of the value must not attempt to + introspect or obscure it. As such, it is not appropriate + to have a phi or select of type token.' + """ + + def _to_string(self): + return 'token' + + def __eq__(self, other): + return isinstance(other, TokenType) + + def __hash__(self): + return hash(TokenType) + + class FunctionType(Type): """ The type for functions. diff --git a/llvmlite/tests/test_binding.py b/llvmlite/tests/test_binding.py index 215ee0f41..e185589ab 100644 --- a/llvmlite/tests/test_binding.py +++ b/llvmlite/tests/test_binding.py @@ -812,6 +812,12 @@ def test_no_accidental_warnings(self): cmdargs = [sys.executable, flags, "-c", code] subprocess.check_call(cmdargs) + def test_getDeviceForFile(self): + self.assertTrue(llvm.getDeviceForFile(__file__) != 0) + + def test_getFileIdForFile(self): + self.assertTrue(llvm.getFileIdForFile(__file__) != 0) + class TestModuleRef(BaseTest): diff --git a/llvmlite/tests/test_ir.py b/llvmlite/tests/test_ir.py index 284129679..fc8460306 100644 --- a/llvmlite/tests/test_ir.py +++ b/llvmlite/tests/test_ir.py @@ -1411,6 +1411,29 @@ def test_call_tail(self): tail call void @"my_fun"() """) # noqa E501 + def test_call_tags(self): + block = self.block(name='my_block') + builder = ir.IRBuilder(block) + token_ty = ir.TokenType() + sfun_ty = ir.FunctionType(token_ty, ()) + efun_ty = ir.FunctionType(ir.VoidType(), (token_ty,)) + sfun = ir.Function(builder.function.module, sfun_ty, 'start_fun') + efun = ir.Function(builder.function.module, efun_ty, 'end_fun') + cres = builder.call( + sfun, + (), + tags="[\"SOME_STRING\"(), \"SOME_OTHER_STRING\"(i64* %\"$const1\")]" + ) + builder.call( + efun, + (cres,) + ) + self.check_block(block, """\ + my_block: + %".6" = call token @"start_fun"() ["SOME_STRING"(), "SOME_OTHER_STRING"(i64* %"$const1")] + call void @"end_fun"(token %".6") + """) # noqa E501 + def test_invalid_call_attributes(self): block = self.block() builder = ir.IRBuilder(block)