summaryrefslogtreecommitdiff
path: root/src/gallium/frontends/clover
diff options
context:
space:
mode:
Diffstat (limited to 'src/gallium/frontends/clover')
-rw-r--r--src/gallium/frontends/clover/Doxyfile1716
-rw-r--r--src/gallium/frontends/clover/Makefile.sources68
-rw-r--r--src/gallium/frontends/clover/api/context.cpp144
-rw-r--r--src/gallium/frontends/clover/api/device.cpp421
-rw-r--r--src/gallium/frontends/clover/api/dispatch.cpp174
-rw-r--r--src/gallium/frontends/clover/api/dispatch.hpp105
-rw-r--r--src/gallium/frontends/clover/api/event.cpp309
-rw-r--r--src/gallium/frontends/clover/api/interop.cpp69
-rw-r--r--src/gallium/frontends/clover/api/kernel.cpp390
-rw-r--r--src/gallium/frontends/clover/api/memory.cpp497
-rw-r--r--src/gallium/frontends/clover/api/platform.cpp235
-rw-r--r--src/gallium/frontends/clover/api/program.cpp479
-rw-r--r--src/gallium/frontends/clover/api/queue.cpp135
-rw-r--r--src/gallium/frontends/clover/api/sampler.cpp100
-rw-r--r--src/gallium/frontends/clover/api/transfer.cpp1059
-rw-r--r--src/gallium/frontends/clover/api/util.hpp84
-rw-r--r--src/gallium/frontends/clover/core/compiler.hpp68
-rw-r--r--src/gallium/frontends/clover/core/context.cpp51
-rw-r--r--src/gallium/frontends/clover/core/context.hpp67
-rw-r--r--src/gallium/frontends/clover/core/device.cpp337
-rw-r--r--src/gallium/frontends/clover/core/device.hpp109
-rw-r--r--src/gallium/frontends/clover/core/error.hpp202
-rw-r--r--src/gallium/frontends/clover/core/event.cpp267
-rw-r--r--src/gallium/frontends/clover/core/event.hpp164
-rw-r--r--src/gallium/frontends/clover/core/format.cpp162
-rw-r--r--src/gallium/frontends/clover/core/format.hpp62
-rw-r--r--src/gallium/frontends/clover/core/kernel.cpp610
-rw-r--r--src/gallium/frontends/clover/core/kernel.hpp251
-rw-r--r--src/gallium/frontends/clover/core/memory.cpp214
-rw-r--r--src/gallium/frontends/clover/core/memory.hpp159
-rw-r--r--src/gallium/frontends/clover/core/module.cpp228
-rw-r--r--src/gallium/frontends/clover/core/module.hpp128
-rw-r--r--src/gallium/frontends/clover/core/object.hpp239
-rw-r--r--src/gallium/frontends/clover/core/platform.cpp46
-rw-r--r--src/gallium/frontends/clover/core/platform.hpp50
-rw-r--r--src/gallium/frontends/clover/core/program.cpp135
-rw-r--r--src/gallium/frontends/clover/core/program.hpp91
-rw-r--r--src/gallium/frontends/clover/core/property.hpp261
-rw-r--r--src/gallium/frontends/clover/core/queue.cpp102
-rw-r--r--src/gallium/frontends/clover/core/queue.hpp78
-rw-r--r--src/gallium/frontends/clover/core/resource.cpp231
-rw-r--r--src/gallium/frontends/clover/core/resource.hpp133
-rw-r--r--src/gallium/frontends/clover/core/sampler.cpp73
-rw-r--r--src/gallium/frontends/clover/core/sampler.hpp58
-rw-r--r--src/gallium/frontends/clover/core/timestamp.cpp64
-rw-r--r--src/gallium/frontends/clover/core/timestamp.hpp74
-rw-r--r--src/gallium/frontends/clover/llvm/codegen.hpp68
-rw-r--r--src/gallium/frontends/clover/llvm/codegen/bitcode.cpp91
-rw-r--r--src/gallium/frontends/clover/llvm/codegen/common.cpp209
-rw-r--r--src/gallium/frontends/clover/llvm/codegen/native.cpp163
-rw-r--r--src/gallium/frontends/clover/llvm/compat.hpp216
-rw-r--r--src/gallium/frontends/clover/llvm/invocation.cpp465
-rw-r--r--src/gallium/frontends/clover/llvm/invocation.hpp54
-rw-r--r--src/gallium/frontends/clover/llvm/metadata.hpp86
-rw-r--r--src/gallium/frontends/clover/llvm/util.hpp137
-rw-r--r--src/gallium/frontends/clover/meson.build164
-rw-r--r--src/gallium/frontends/clover/nir/invocation.cpp173
-rw-r--r--src/gallium/frontends/clover/nir/invocation.hpp36
-rw-r--r--src/gallium/frontends/clover/spirv/invocation.cpp740
-rw-r--r--src/gallium/frontends/clover/spirv/invocation.hpp56
-rw-r--r--src/gallium/frontends/clover/util/adaptor.hpp183
-rw-r--r--src/gallium/frontends/clover/util/algebra.hpp160
-rw-r--r--src/gallium/frontends/clover/util/algorithm.hpp218
-rw-r--r--src/gallium/frontends/clover/util/factor.hpp131
-rw-r--r--src/gallium/frontends/clover/util/functional.hpp419
-rw-r--r--src/gallium/frontends/clover/util/lazy.hpp161
-rw-r--r--src/gallium/frontends/clover/util/pointer.hpp284
-rw-r--r--src/gallium/frontends/clover/util/range.hpp419
-rw-r--r--src/gallium/frontends/clover/util/tuple.hpp117
69 files changed, 15449 insertions, 0 deletions
diff --git a/src/gallium/frontends/clover/Doxyfile b/src/gallium/frontends/clover/Doxyfile
new file mode 100644
index 00000000000..19337bbd656
--- /dev/null
+++ b/src/gallium/frontends/clover/Doxyfile
@@ -0,0 +1,1716 @@
+# Doxyfile 1.7.4
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = Clover
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER =
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer
+# a quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF =
+
+# With the PROJECT_LOGO tag one can specify an logo or icon that is
+# included in the documentation. The maximum height of the logo should not
+# exceed 55 pixels and the maximum width should not exceed 200 pixels.
+# Doxygen will copy the logo to the output directory.
+
+PROJECT_LOGO =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY =
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful if your file system
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF = YES
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given extension.
+# Doxygen has a built-in mapping, but you can override or extend it using this
+# tag. The format is ext=language, where ext is a file extension, and language
+# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C,
+# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make
+# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
+# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions
+# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also makes the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = YES
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and
+# unions are shown inside the group in which they are included (e.g. using
+# @ingroup) instead of on a separate page (for HTML and Man pages) or
+# section (for LaTeX and RTF).
+
+INLINE_GROUPED_CLASSES = NO
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penalty.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will roughly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols
+
+SYMBOL_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespaces are hidden.
+
+EXTRACT_ANON_NSPACES = YES
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
+# rather than with sharp brackets.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
+# will sort the (brief and detailed) documentation of class members so that
+# constructors and destructors are listed first. If set to NO (the default)
+# the constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
+# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
+# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to
+# do proper type resolution of all parameters of a function it will reject a
+# match between the prototype and the implementation of a member function even
+# if there is only one candidate or it is obvious which candidate to choose
+# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen
+# will still accept a match between prototype and implementation in such cases.
+
+STRICT_PROTO_MATCHING = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or macro consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and macros in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES = NO
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. The create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option.
+# You can optionally specify a file name after the option, if omitted
+# DoxygenLayout.xml will be used as the name of the layout file.
+
+LAYOUT_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = NO
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# The WARN_NO_PARAMDOC option can be enabled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = api/ core/ util/
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh
+# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py
+# *.f90 *.f *.for *.vhd *.vhdl
+
+FILE_PATTERNS =
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty or if
+# non of the patterns match the file name, INPUT_FILTER is applied.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any)
+# and it is also possible to disable source filtering for a specific pattern
+# using *.ext= (so without naming a filter). This option only has effect when
+# FILTER_SOURCE_FILES is enabled.
+
+FILTER_SOURCE_PATTERNS =
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.
+# Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = YES
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header. Note that when using a custom header you are responsible
+# for the proper inclusion of any scripts and style sheets that doxygen
+# needs, which is dependent on the configuration options used.
+# It is adviced to generate a default header using "doxygen -w html
+# header.html footer.html stylesheet.css YourConfigFile" and then modify
+# that header. Note that the header is subject to change so you typically
+# have to redo this when upgrading to a newer version of doxygen or when changing the value of configuration settings such as GENERATE_TREEVIEW!
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that
+# the files will be copied as-is; there are no commands or markers available.
+
+HTML_EXTRA_FILES =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
+# Doxygen will adjust the colors in the stylesheet and background images
+# according to this color. Hue is specified as an angle on a colorwheel,
+# see http://en.wikipedia.org/wiki/Hue for more information.
+# For instance the value 0 represents red, 60 is yellow, 120 is green,
+# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
+# The allowed range is 0 to 359.
+
+HTML_COLORSTYLE_HUE = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
+# the colors in the HTML output. For a value of 0 the output will use
+# grayscales only. A value of 255 will produce the most vivid colors.
+
+HTML_COLORSTYLE_SAT = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
+# the luminance component of the colors in the HTML output. Values below
+# 100 gradually make the output lighter, whereas values above 100 make
+# the output darker. The value divided by 100 is the actual gamma applied,
+# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
+# and 100 does not change the gamma.
+
+HTML_COLORSTYLE_GAMMA = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
+# this to NO can help when comparing the output of multiple runs.
+
+HTML_TIMESTAMP = YES
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+
+GENERATE_DOCSET = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
+# that can be used as input for Qt's qhelpgenerator to generate a
+# Qt Compressed Help (.qch) of the generated HTML documentation.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
+# add. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
+# Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
+# Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+# will be generated, which together with the HTML files, form an Eclipse help
+# plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
+# the help appears.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
+# this name.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values
+# (range [0,1..20]) that doxygen will group on one line in the generated HTML
+# documentation. Note that a value of 0 will completely suppress the enum
+# values from appearing in the overview section.
+
+ENUM_VALUES_PER_LINE = 4
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW = NO
+
+# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
+# and Class Hierarchy pages using a tree view instead of an ordered list.
+
+USE_INLINE_TREES = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
+# links to external symbols imported via tag files in a separate window.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are
+# not supported properly for IE 6.0, but are supported on all modern browsers.
+# Note that when changing this option you need to delete any form_*.png files
+# in the HTML output before the changes have effect.
+
+FORMULA_TRANSPARENT = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax
+# (see http://www.mathjax.org) which uses client side Javascript for the
+# rendering instead of using prerendered bitmaps. Use this if you do not
+# have LaTeX installed or if you want to formulas look prettier in the HTML
+# output. When enabled you also need to install MathJax separately and
+# configure the path to it using the MATHJAX_RELPATH option.
+
+USE_MATHJAX = NO
+
+# When MathJax is enabled you need to specify the location relative to the
+# HTML output directory using the MATHJAX_RELPATH option. The destination
+# directory should contain the MathJax.js script. For instance, if the mathjax
+# directory is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the
+# mathjax.org site, so you can quickly see the result without installing
+# MathJax, but it is strongly recommended to install a local copy of MathJax
+# before deployment.
+
+MATHJAX_RELPATH = http://www.mathjax.org/mathjax
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box
+# for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using
+# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
+# (GENERATE_DOCSET) there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
+# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+
+SEARCHENGINE = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a PHP enabled web server instead of at the web client
+# using Javascript. Doxygen will generate the search PHP script and index
+# file to put on the web server. The advantage of the server
+# based approach is that it scales better to large projects and allows
+# full text search. The disadvantages are that it is more difficult to setup
+# and does not have live searching capabilities.
+
+SERVER_BASED_SEARCH = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = YES
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
+# Makefile that is written to the output directory.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for
+# the generated latex document. The footer should contain everything after
+# the last chapter. If it is left blank doxygen will generate a
+# standard footer. Notice: only use this tag if you know what you are doing!
+
+LATEX_FOOTER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = YES
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include
+# source code with syntax highlighting in the LaTeX output.
+# Note that which sources are shown also depends on other settings
+# such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# pointed to by INCLUDE_PATH will be searched when a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition that
+# overrules the definition found in the source code.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all references to function-like macros
+# that are alone on a line, have an all uppercase name, and do not end with a
+# semicolon, because these will confuse the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+#
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option also works with HAVE_DOT disabled, but it is recommended to
+# install and use dot, since it yields more powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
+# allowed to run in parallel. When set to 0 (the default) doxygen will
+# base this on the number of processors available in the system. You can set it
+# explicitly to a value larger than 0 to get control over the balance
+# between CPU load and processing speed.
+
+DOT_NUM_THREADS = 0
+
+# By default doxygen will write a font called Helvetica to the output
+# directory and reference it in all dot files that doxygen generates.
+# When you want a differently looking font you can specify the font name
+# using DOT_FONTNAME. You need to make sure dot is able to find the font,
+# which can be done by putting it in a standard location or by setting the
+# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
+# containing the font.
+
+DOT_FONTNAME = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the output directory to look for the
+# FreeSans.ttf font (which doxygen will put there itself). If you specify a
+# different font using DOT_FONTNAME you can set the path where dot
+# can find it using this tag.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will generate a graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are svg, png, jpg, or gif.
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the
+# \mscfile command).
+
+MSCFILE_DIRS =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
diff --git a/src/gallium/frontends/clover/Makefile.sources b/src/gallium/frontends/clover/Makefile.sources
new file mode 100644
index 00000000000..38f94981fb6
--- /dev/null
+++ b/src/gallium/frontends/clover/Makefile.sources
@@ -0,0 +1,68 @@
+CPP_SOURCES := \
+ api/context.cpp \
+ api/device.cpp \
+ api/dispatch.cpp \
+ api/dispatch.hpp \
+ api/event.cpp \
+ api/interop.cpp \
+ api/kernel.cpp \
+ api/memory.cpp \
+ api/platform.cpp \
+ api/program.cpp \
+ api/queue.cpp \
+ api/sampler.cpp \
+ api/transfer.cpp \
+ api/util.hpp \
+ core/context.cpp \
+ core/context.hpp \
+ core/device.cpp \
+ core/device.hpp \
+ core/error.hpp \
+ core/event.cpp \
+ core/event.hpp \
+ core/format.cpp \
+ core/format.hpp \
+ core/kernel.cpp \
+ core/kernel.hpp \
+ core/memory.cpp \
+ core/memory.hpp \
+ core/module.cpp \
+ core/module.hpp \
+ core/object.hpp \
+ core/platform.cpp \
+ core/platform.hpp \
+ core/program.cpp \
+ core/program.hpp \
+ core/property.hpp \
+ core/queue.cpp \
+ core/queue.hpp \
+ core/resource.cpp \
+ core/resource.hpp \
+ core/sampler.cpp \
+ core/sampler.hpp \
+ core/timestamp.cpp \
+ core/timestamp.hpp \
+ util/adaptor.hpp \
+ util/algebra.hpp \
+ util/algorithm.hpp \
+ util/factor.hpp \
+ util/functional.hpp \
+ util/lazy.hpp \
+ util/pointer.hpp \
+ util/range.hpp \
+ util/tuple.hpp
+
+LLVM_SOURCES := \
+ llvm/codegen/bitcode.cpp \
+ llvm/codegen/common.cpp \
+ llvm/codegen/native.cpp \
+ llvm/codegen.hpp \
+ llvm/compat.hpp \
+ llvm/invocation.cpp \
+ llvm/invocation.hpp \
+ llvm/metadata.hpp \
+ llvm/util.hpp
+
+SPIRV_SOURCES := \
+ spirv/invocation.cpp \
+ spirv/invocation.hpp
diff --git a/src/gallium/frontends/clover/api/context.cpp b/src/gallium/frontends/clover/api/context.cpp
new file mode 100644
index 00000000000..c0cd2d32b95
--- /dev/null
+++ b/src/gallium/frontends/clover/api/context.cpp
@@ -0,0 +1,144 @@
+//
+// Copyright 2012 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#include "api/util.hpp"
+#include "core/context.hpp"
+#include "core/platform.hpp"
+
+using namespace clover;
+
+CLOVER_API cl_context
+clCreateContext(const cl_context_properties *d_props, cl_uint num_devs,
+ const cl_device_id *d_devs,
+ void (CL_CALLBACK *pfn_notify)(const char *, const void *,
+ size_t, void *),
+ void *user_data, cl_int *r_errcode) try {
+ auto props = obj<property_list_tag>(d_props);
+ auto devs = objs(d_devs, num_devs);
+
+ if (!pfn_notify && user_data)
+ throw error(CL_INVALID_VALUE);
+
+ for (auto &prop : props) {
+ if (prop.first == CL_CONTEXT_PLATFORM)
+ obj(prop.second.as<cl_platform_id>());
+ else
+ throw error(CL_INVALID_PROPERTY);
+ }
+
+ const auto notify = (!pfn_notify ? context::notify_action() :
+ [=](const char *s) {
+ pfn_notify(s, NULL, 0, user_data);
+ });
+
+ ret_error(r_errcode, CL_SUCCESS);
+ return desc(new context(props, devs, notify));
+
+} catch (error &e) {
+ ret_error(r_errcode, e);
+ return NULL;
+}
+
+CLOVER_API cl_context
+clCreateContextFromType(const cl_context_properties *d_props,
+ cl_device_type type,
+ void (CL_CALLBACK *pfn_notify)(
+ const char *, const void *, size_t, void *),
+ void *user_data, cl_int *r_errcode) try {
+ cl_platform_id d_platform;
+ cl_uint num_platforms;
+ cl_int ret;
+ std::vector<cl_device_id> devs;
+ cl_uint num_devices;
+
+ ret = clGetPlatformIDs(1, &d_platform, &num_platforms);
+ if (ret || !num_platforms)
+ throw error(CL_INVALID_PLATFORM);
+
+ ret = clGetDeviceIDs(d_platform, type, 0, NULL, &num_devices);
+ if (ret)
+ throw error(CL_DEVICE_NOT_FOUND);
+ devs.resize(num_devices);
+ ret = clGetDeviceIDs(d_platform, type, num_devices, devs.data(), 0);
+ if (ret)
+ throw error(CL_DEVICE_NOT_FOUND);
+
+ return clCreateContext(d_props, num_devices, devs.data(), pfn_notify,
+ user_data, r_errcode);
+
+} catch (error &e) {
+ ret_error(r_errcode, e);
+ return NULL;
+}
+
+CLOVER_API cl_int
+clRetainContext(cl_context d_ctx) try {
+ obj(d_ctx).retain();
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clReleaseContext(cl_context d_ctx) try {
+ if (obj(d_ctx).release())
+ delete pobj(d_ctx);
+
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clGetContextInfo(cl_context d_ctx, cl_context_info param,
+ size_t size, void *r_buf, size_t *r_size) try {
+ property_buffer buf { r_buf, size, r_size };
+ auto &ctx = obj(d_ctx);
+
+ switch (param) {
+ case CL_CONTEXT_REFERENCE_COUNT:
+ buf.as_scalar<cl_uint>() = ctx.ref_count();
+ break;
+
+ case CL_CONTEXT_NUM_DEVICES:
+ buf.as_scalar<cl_uint>() = ctx.devices().size();
+ break;
+
+ case CL_CONTEXT_DEVICES:
+ buf.as_vector<cl_device_id>() = descs(ctx.devices());
+ break;
+
+ case CL_CONTEXT_PROPERTIES:
+ buf.as_vector<cl_context_properties>() = desc(ctx.properties());
+ break;
+
+ default:
+ throw error(CL_INVALID_VALUE);
+ }
+
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
diff --git a/src/gallium/frontends/clover/api/device.cpp b/src/gallium/frontends/clover/api/device.cpp
new file mode 100644
index 00000000000..042f2eda21c
--- /dev/null
+++ b/src/gallium/frontends/clover/api/device.cpp
@@ -0,0 +1,421 @@
+//
+// Copyright 2012 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#include "api/util.hpp"
+#include "core/platform.hpp"
+#include "core/device.hpp"
+#include "git_sha1.h"
+
+using namespace clover;
+
+CLOVER_API cl_int
+clGetDeviceIDs(cl_platform_id d_platform, cl_device_type device_type,
+ cl_uint num_entries, cl_device_id *rd_devices,
+ cl_uint *rnum_devices) try {
+ auto &platform = obj(d_platform);
+ std::vector<cl_device_id> d_devs;
+
+ if ((!num_entries && rd_devices) ||
+ (!rnum_devices && !rd_devices))
+ throw error(CL_INVALID_VALUE);
+
+ // Collect matching devices
+ for (device &dev : platform) {
+ if (((device_type & CL_DEVICE_TYPE_DEFAULT) &&
+ dev == platform.front()) ||
+ (device_type & dev.type()))
+ d_devs.push_back(desc(dev));
+ }
+
+ if (d_devs.empty())
+ throw error(CL_DEVICE_NOT_FOUND);
+
+ // ...and return the requested data.
+ if (rnum_devices)
+ *rnum_devices = d_devs.size();
+ if (rd_devices)
+ copy(range(d_devs.begin(),
+ std::min((unsigned)d_devs.size(), num_entries)),
+ rd_devices);
+
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clCreateSubDevices(cl_device_id d_dev,
+ const cl_device_partition_property *props,
+ cl_uint num_devs, cl_device_id *rd_devs,
+ cl_uint *rnum_devs) {
+ // There are no currently supported partitioning schemes.
+ return CL_INVALID_VALUE;
+}
+
+CLOVER_API cl_int
+clRetainDevice(cl_device_id d_dev) try {
+ obj(d_dev);
+
+ // The reference count doesn't change for root devices.
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clReleaseDevice(cl_device_id d_dev) try {
+ obj(d_dev);
+
+ // The reference count doesn't change for root devices.
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clGetDeviceInfo(cl_device_id d_dev, cl_device_info param,
+ size_t size, void *r_buf, size_t *r_size) try {
+ property_buffer buf { r_buf, size, r_size };
+ auto &dev = obj(d_dev);
+
+ switch (param) {
+ case CL_DEVICE_TYPE:
+ buf.as_scalar<cl_device_type>() = dev.type();
+ break;
+
+ case CL_DEVICE_VENDOR_ID:
+ buf.as_scalar<cl_uint>() = dev.vendor_id();
+ break;
+
+ case CL_DEVICE_MAX_COMPUTE_UNITS:
+ buf.as_scalar<cl_uint>() = dev.max_compute_units();
+ break;
+
+ case CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS:
+ buf.as_scalar<cl_uint>() = dev.max_block_size().size();
+ break;
+
+ case CL_DEVICE_MAX_WORK_ITEM_SIZES:
+ buf.as_vector<size_t>() = dev.max_block_size();
+ break;
+
+ case CL_DEVICE_MAX_WORK_GROUP_SIZE:
+ buf.as_scalar<size_t>() = dev.max_threads_per_block();
+ break;
+
+ case CL_DEVICE_PREFERRED_VECTOR_WIDTH_CHAR:
+ buf.as_scalar<cl_uint>() = 16;
+ break;
+
+ case CL_DEVICE_PREFERRED_VECTOR_WIDTH_SHORT:
+ buf.as_scalar<cl_uint>() = 8;
+ break;
+
+ case CL_DEVICE_PREFERRED_VECTOR_WIDTH_INT:
+ buf.as_scalar<cl_uint>() = 4;
+ break;
+
+ case CL_DEVICE_PREFERRED_VECTOR_WIDTH_LONG:
+ buf.as_scalar<cl_uint>() = 2;
+ break;
+
+ case CL_DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT:
+ buf.as_scalar<cl_uint>() = 4;
+ break;
+
+ case CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE:
+ buf.as_scalar<cl_uint>() = dev.has_doubles() ? 2 : 0;
+ break;
+
+ case CL_DEVICE_PREFERRED_VECTOR_WIDTH_HALF:
+ buf.as_scalar<cl_uint>() = dev.has_halves() ? 8 : 0;
+ break;
+
+ case CL_DEVICE_MAX_CLOCK_FREQUENCY:
+ buf.as_scalar<cl_uint>() = dev.max_clock_frequency();
+ break;
+
+ case CL_DEVICE_ADDRESS_BITS:
+ buf.as_scalar<cl_uint>() = dev.address_bits();
+ break;
+
+ case CL_DEVICE_MAX_READ_IMAGE_ARGS:
+ buf.as_scalar<cl_uint>() = dev.max_images_read();
+ break;
+
+ case CL_DEVICE_MAX_WRITE_IMAGE_ARGS:
+ buf.as_scalar<cl_uint>() = dev.max_images_write();
+ break;
+
+ case CL_DEVICE_MAX_MEM_ALLOC_SIZE:
+ buf.as_scalar<cl_ulong>() = dev.max_mem_alloc_size();
+ break;
+
+ case CL_DEVICE_IMAGE2D_MAX_WIDTH:
+ case CL_DEVICE_IMAGE2D_MAX_HEIGHT:
+ buf.as_scalar<size_t>() = 1 << dev.max_image_levels_2d();
+ break;
+
+ case CL_DEVICE_IMAGE3D_MAX_WIDTH:
+ case CL_DEVICE_IMAGE3D_MAX_HEIGHT:
+ case CL_DEVICE_IMAGE3D_MAX_DEPTH:
+ buf.as_scalar<size_t>() = 1 << dev.max_image_levels_3d();
+ break;
+
+ case CL_DEVICE_IMAGE_MAX_BUFFER_SIZE:
+ buf.as_scalar<size_t>() = dev.max_image_buffer_size();
+ break;
+
+ case CL_DEVICE_IMAGE_MAX_ARRAY_SIZE:
+ buf.as_scalar<size_t>() = dev.max_image_array_number();
+ break;
+
+ case CL_DEVICE_IMAGE_SUPPORT:
+ buf.as_scalar<cl_bool>() = dev.image_support();
+ break;
+
+ case CL_DEVICE_MAX_PARAMETER_SIZE:
+ buf.as_scalar<size_t>() = dev.max_mem_input();
+ break;
+
+ case CL_DEVICE_MAX_SAMPLERS:
+ buf.as_scalar<cl_uint>() = dev.max_samplers();
+ break;
+
+ case CL_DEVICE_MEM_BASE_ADDR_ALIGN:
+ buf.as_scalar<cl_uint>() = 8 *
+ std::max(dev.mem_base_addr_align(), (cl_uint) sizeof(cl_long) * 16);
+ break;
+
+ case CL_DEVICE_MIN_DATA_TYPE_ALIGN_SIZE:
+ buf.as_scalar<cl_uint>() = 128;
+ break;
+
+ case CL_DEVICE_HALF_FP_CONFIG:
+ // This is the "mandated minimum half precision floating-point
+ // capability" for OpenCL 1.x.
+ buf.as_scalar<cl_device_fp_config>() =
+ CL_FP_INF_NAN | CL_FP_ROUND_TO_NEAREST;
+ break;
+
+ case CL_DEVICE_SINGLE_FP_CONFIG:
+ // This is the "mandated minimum single precision floating-point
+ // capability" for OpenCL 1.1. In OpenCL 1.2, nothing is required for
+ // custom devices.
+ buf.as_scalar<cl_device_fp_config>() =
+ CL_FP_INF_NAN | CL_FP_ROUND_TO_NEAREST;
+ break;
+
+ case CL_DEVICE_DOUBLE_FP_CONFIG:
+ if (dev.has_doubles())
+ // This is the "mandated minimum double precision floating-point
+ // capability"
+ buf.as_scalar<cl_device_fp_config>() =
+ CL_FP_FMA
+ | CL_FP_ROUND_TO_NEAREST
+ | CL_FP_ROUND_TO_ZERO
+ | CL_FP_ROUND_TO_INF
+ | CL_FP_INF_NAN
+ | CL_FP_DENORM;
+ else
+ buf.as_scalar<cl_device_fp_config>() = 0;
+ break;
+
+ case CL_DEVICE_GLOBAL_MEM_CACHE_TYPE:
+ buf.as_scalar<cl_device_mem_cache_type>() = CL_NONE;
+ break;
+
+ case CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE:
+ buf.as_scalar<cl_uint>() = 0;
+ break;
+
+ case CL_DEVICE_GLOBAL_MEM_CACHE_SIZE:
+ buf.as_scalar<cl_ulong>() = 0;
+ break;
+
+ case CL_DEVICE_GLOBAL_MEM_SIZE:
+ buf.as_scalar<cl_ulong>() = dev.max_mem_global();
+ break;
+
+ case CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE:
+ buf.as_scalar<cl_ulong>() = dev.max_const_buffer_size();
+ break;
+
+ case CL_DEVICE_MAX_CONSTANT_ARGS:
+ buf.as_scalar<cl_uint>() = dev.max_const_buffers();
+ break;
+
+ case CL_DEVICE_LOCAL_MEM_TYPE:
+ buf.as_scalar<cl_device_local_mem_type>() = CL_LOCAL;
+ break;
+
+ case CL_DEVICE_LOCAL_MEM_SIZE:
+ buf.as_scalar<cl_ulong>() = dev.max_mem_local();
+ break;
+
+ case CL_DEVICE_ERROR_CORRECTION_SUPPORT:
+ buf.as_scalar<cl_bool>() = CL_FALSE;
+ break;
+
+ case CL_DEVICE_PROFILING_TIMER_RESOLUTION:
+ buf.as_scalar<size_t>() = 0;
+ break;
+
+ case CL_DEVICE_ENDIAN_LITTLE:
+ buf.as_scalar<cl_bool>() = (dev.endianness() == PIPE_ENDIAN_LITTLE);
+ break;
+
+ case CL_DEVICE_AVAILABLE:
+ case CL_DEVICE_COMPILER_AVAILABLE:
+ case CL_DEVICE_LINKER_AVAILABLE:
+ buf.as_scalar<cl_bool>() = CL_TRUE;
+ break;
+
+ case CL_DEVICE_EXECUTION_CAPABILITIES:
+ buf.as_scalar<cl_device_exec_capabilities>() = CL_EXEC_KERNEL;
+ break;
+
+ case CL_DEVICE_QUEUE_PROPERTIES:
+ buf.as_scalar<cl_command_queue_properties>() = CL_QUEUE_PROFILING_ENABLE;
+ break;
+
+ case CL_DEVICE_BUILT_IN_KERNELS:
+ buf.as_string() = "";
+ break;
+
+ case CL_DEVICE_NAME:
+ buf.as_string() = dev.device_name();
+ break;
+
+ case CL_DEVICE_VENDOR:
+ buf.as_string() = dev.vendor_name();
+ break;
+
+ case CL_DRIVER_VERSION:
+ buf.as_string() = PACKAGE_VERSION;
+ break;
+
+ case CL_DEVICE_PROFILE:
+ buf.as_string() = "FULL_PROFILE";
+ break;
+
+ case CL_DEVICE_VERSION:
+ buf.as_string() = "OpenCL " + dev.device_version() + " Mesa " PACKAGE_VERSION MESA_GIT_SHA1;
+ break;
+
+ case CL_DEVICE_EXTENSIONS:
+ buf.as_string() = dev.supported_extensions();
+ break;
+
+ case CL_DEVICE_PLATFORM:
+ buf.as_scalar<cl_platform_id>() = desc(dev.platform);
+ break;
+
+ case CL_DEVICE_HOST_UNIFIED_MEMORY:
+ buf.as_scalar<cl_bool>() = dev.has_unified_memory();
+ break;
+
+ case CL_DEVICE_NATIVE_VECTOR_WIDTH_CHAR:
+ buf.as_scalar<cl_uint>() = 16;
+ break;
+
+ case CL_DEVICE_NATIVE_VECTOR_WIDTH_SHORT:
+ buf.as_scalar<cl_uint>() = 8;
+ break;
+
+ case CL_DEVICE_NATIVE_VECTOR_WIDTH_INT:
+ buf.as_scalar<cl_uint>() = 4;
+ break;
+
+ case CL_DEVICE_NATIVE_VECTOR_WIDTH_LONG:
+ buf.as_scalar<cl_uint>() = 2;
+ break;
+
+ case CL_DEVICE_NATIVE_VECTOR_WIDTH_FLOAT:
+ buf.as_scalar<cl_uint>() = 4;
+ break;
+
+ case CL_DEVICE_NATIVE_VECTOR_WIDTH_DOUBLE:
+ buf.as_scalar<cl_uint>() = dev.has_doubles() ? 2 : 0;
+ break;
+
+ case CL_DEVICE_NATIVE_VECTOR_WIDTH_HALF:
+ buf.as_scalar<cl_uint>() = dev.has_halves() ? 8 : 0;
+ break;
+
+ case CL_DEVICE_OPENCL_C_VERSION:
+ buf.as_string() = "OpenCL C " + dev.device_clc_version() + " ";
+ break;
+
+ case CL_DEVICE_PRINTF_BUFFER_SIZE:
+ // Per the spec, the minimum value for the FULL profile is 1 MB.
+ // However, clover is not ready yet to support it
+ buf.as_scalar<size_t>() = 0 /* 1024 */;
+ break;
+
+ case CL_DEVICE_PREFERRED_INTEROP_USER_SYNC:
+ buf.as_scalar<cl_bool>() = CL_TRUE;
+ break;
+
+ case CL_DEVICE_PARENT_DEVICE:
+ buf.as_scalar<cl_device_id>() = NULL;
+ break;
+
+ case CL_DEVICE_PARTITION_MAX_SUB_DEVICES:
+ buf.as_scalar<cl_uint>() = 0;
+ break;
+
+ case CL_DEVICE_PARTITION_PROPERTIES:
+ buf.as_vector<cl_device_partition_property>() =
+ desc(property_list<cl_device_partition_property>());
+ break;
+
+ case CL_DEVICE_PARTITION_AFFINITY_DOMAIN:
+ buf.as_scalar<cl_device_affinity_domain>() = 0;
+ break;
+
+ case CL_DEVICE_PARTITION_TYPE:
+ buf.as_vector<cl_device_partition_property>() =
+ desc(property_list<cl_device_partition_property>());
+ break;
+
+ case CL_DEVICE_REFERENCE_COUNT:
+ buf.as_scalar<cl_uint>() = 1;
+ break;
+
+ case CL_DEVICE_SVM_CAPABILITIES:
+ case CL_DEVICE_SVM_CAPABILITIES_ARM:
+ buf.as_scalar<cl_device_svm_capabilities>() = dev.svm_support();
+ break;
+
+ default:
+ throw error(CL_INVALID_VALUE);
+ }
+
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
diff --git a/src/gallium/frontends/clover/api/dispatch.cpp b/src/gallium/frontends/clover/api/dispatch.cpp
new file mode 100644
index 00000000000..6e1b0351afa
--- /dev/null
+++ b/src/gallium/frontends/clover/api/dispatch.cpp
@@ -0,0 +1,174 @@
+//
+// Copyright 2013 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#include "api/dispatch.hpp"
+
+namespace clover {
+ const cl_icd_dispatch _dispatch = {
+ clGetPlatformIDs,
+ GetPlatformInfo,
+ clGetDeviceIDs,
+ clGetDeviceInfo,
+ clCreateContext,
+ clCreateContextFromType,
+ clRetainContext,
+ clReleaseContext,
+ clGetContextInfo,
+ clCreateCommandQueue,
+ clRetainCommandQueue,
+ clReleaseCommandQueue,
+ clGetCommandQueueInfo,
+ NULL, // clSetCommandQueueProperty
+ clCreateBuffer,
+ clCreateImage2D,
+ clCreateImage3D,
+ clRetainMemObject,
+ clReleaseMemObject,
+ clGetSupportedImageFormats,
+ clGetMemObjectInfo,
+ clGetImageInfo,
+ clCreateSampler,
+ clRetainSampler,
+ clReleaseSampler,
+ clGetSamplerInfo,
+ clCreateProgramWithSource,
+ clCreateProgramWithBinary,
+ clRetainProgram,
+ clReleaseProgram,
+ clBuildProgram,
+ clUnloadCompiler,
+ clGetProgramInfo,
+ clGetProgramBuildInfo,
+ clCreateKernel,
+ clCreateKernelsInProgram,
+ clRetainKernel,
+ clReleaseKernel,
+ clSetKernelArg,
+ clGetKernelInfo,
+ clGetKernelWorkGroupInfo,
+ clWaitForEvents,
+ clGetEventInfo,
+ clRetainEvent,
+ clReleaseEvent,
+ clGetEventProfilingInfo,
+ clFlush,
+ clFinish,
+ clEnqueueReadBuffer,
+ clEnqueueWriteBuffer,
+ clEnqueueCopyBuffer,
+ clEnqueueReadImage,
+ clEnqueueWriteImage,
+ clEnqueueCopyImage,
+ clEnqueueCopyImageToBuffer,
+ clEnqueueCopyBufferToImage,
+ clEnqueueMapBuffer,
+ clEnqueueMapImage,
+ clEnqueueUnmapMemObject,
+ clEnqueueNDRangeKernel,
+ clEnqueueTask,
+ clEnqueueNativeKernel,
+ clEnqueueMarker,
+ clEnqueueWaitForEvents,
+ clEnqueueBarrier,
+ GetExtensionFunctionAddress,
+ NULL, // clCreateFromGLBuffer
+ NULL, // clCreateFromGLTexture2D
+ NULL, // clCreateFromGLTexture3D
+ NULL, // clCreateFromGLRenderbuffer
+ NULL, // clGetGLObjectInfo
+ NULL, // clGetGLTextureInfo
+ NULL, // clEnqueueAcquireGLObjects
+ NULL, // clEnqueueReleaseGLObjects
+ NULL, // clGetGLContextInfoKHR
+ NULL, // clGetDeviceIDsFromD3D10KHR
+ NULL, // clCreateFromD3D10BufferKHR
+ NULL, // clCreateFromD3D10Texture2DKHR
+ NULL, // clCreateFromD3D10Texture3DKHR
+ NULL, // clEnqueueAcquireD3D10ObjectsKHR
+ NULL, // clEnqueueReleaseD3D10ObjectsKHR
+ clSetEventCallback,
+ clCreateSubBuffer,
+ clSetMemObjectDestructorCallback,
+ clCreateUserEvent,
+ clSetUserEventStatus,
+ clEnqueueReadBufferRect,
+ clEnqueueWriteBufferRect,
+ clEnqueueCopyBufferRect,
+ NULL, // clCreateSubDevicesEXT
+ NULL, // clRetainDeviceEXT
+ NULL, // clReleaseDeviceEXT
+ NULL, // clCreateEventFromGLsyncKHR
+ clCreateSubDevices,
+ clRetainDevice,
+ clReleaseDevice,
+ clCreateImage,
+ clCreateProgramWithBuiltInKernels,
+ clCompileProgram,
+ clLinkProgram,
+ clUnloadPlatformCompiler,
+ clGetKernelArgInfo,
+ clEnqueueFillBuffer,
+ clEnqueueFillImage,
+ clEnqueueMigrateMemObjects,
+ clEnqueueMarkerWithWaitList,
+ clEnqueueBarrierWithWaitList,
+ GetExtensionFunctionAddressForPlatform,
+ NULL, // clCreateFromGLTexture
+ NULL, // clGetDeviceIDsFromD3D11KHR
+ NULL, // clCreateFromD3D11BufferKHR
+ NULL, // clCreateFromD3D11Texture2DKHR
+ NULL, // clCreateFromD3D11Texture3DKHR
+ NULL, // clCreateFromDX9MediaSurfaceKHR
+ NULL, // clEnqueueAcquireD3D11ObjectsKHR
+ NULL, // clEnqueueReleaseD3D11ObjectsKHR
+ NULL, // clGetDeviceIDsFromDX9MediaAdapterKHR
+ NULL, // clEnqueueAcquireDX9MediaSurfacesKHR
+ NULL, // clEnqueueReleaseDX9MediaSurfacesKHR
+ NULL, // clCreateFromEGLImageKHR
+ NULL, // clEnqueueAcquireEGLObjectsKHR
+ NULL, // clEnqueueReleaseEGLObjectsKHR
+ NULL, // clCreateEventFromEGLSyncKHR
+ clCreateCommandQueueWithProperties,
+ NULL, // clCreatePipe
+ NULL, // clGetPipeInfo
+ clSVMAlloc,
+ clSVMFree,
+ clEnqueueSVMFree,
+ clEnqueueSVMMemcpy,
+ clEnqueueSVMMemFill,
+ clEnqueueSVMMap,
+ clEnqueueSVMUnmap,
+ NULL, // clCreateSamplerWithProperties
+ clSetKernelArgSVMPointer,
+ clSetKernelExecInfo,
+ NULL, // clGetKernelSubGroupInfoKHR
+ NULL, // clCloneKernel
+ NULL, // clCreateProgramWithIL
+ clEnqueueSVMMigrateMem,
+ NULL, // clGetDeviceAndHostTimer
+ NULL, // clGetHostTimer
+ NULL, // clGetKernelSubGroupInfo
+ NULL, // clSetDefaultDeviceCommandQueue
+ NULL, // clSetProgramReleaseCallback
+ NULL, // clSetProgramSpecializationConstant
+ };
+}
diff --git a/src/gallium/frontends/clover/api/dispatch.hpp b/src/gallium/frontends/clover/api/dispatch.hpp
new file mode 100644
index 00000000000..ea835ed6da4
--- /dev/null
+++ b/src/gallium/frontends/clover/api/dispatch.hpp
@@ -0,0 +1,105 @@
+//
+// Copyright 2013 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#ifndef API_DISPATCH_HPP
+#define API_DISPATCH_HPP
+
+#include "CL/cl.h"
+#include "CL/cl_ext.h"
+#include "CL/cl_egl.h"
+#include "CL/cl_gl.h"
+#include "CL/cl_icd.h"
+
+namespace clover {
+ extern const cl_icd_dispatch _dispatch;
+
+ cl_int
+ GetPlatformInfo(cl_platform_id d_platform, cl_platform_info param,
+ size_t size, void *r_buf, size_t *r_size);
+
+ void *
+ GetExtensionFunctionAddress(const char *p_name);
+
+ void *
+ GetExtensionFunctionAddressForPlatform(cl_platform_id d_platform,
+ const char *p_name);
+
+ cl_int
+ IcdGetPlatformIDsKHR(cl_uint num_entries, cl_platform_id *rd_platforms,
+ cl_uint *rnum_platforms);
+
+ cl_int
+ EnqueueSVMFree(cl_command_queue command_queue,
+ cl_uint num_svm_pointers,
+ void *svm_pointers[],
+ void (CL_CALLBACK *pfn_free_func) (
+ cl_command_queue queue, cl_uint num_svm_pointers,
+ void *svm_pointers[], void *user_data),
+ void *user_data,
+ cl_uint num_events_in_wait_list,
+ const cl_event *event_wait_list,
+ cl_event *event,
+ cl_int cmd);
+
+ cl_int
+ EnqueueSVMMemcpy(cl_command_queue command_queue,
+ cl_bool blocking_copy,
+ void *dst_ptr,
+ const void *src_ptr,
+ size_t size,
+ cl_uint num_events_in_wait_list,
+ const cl_event *event_wait_list,
+ cl_event *event,
+ cl_int cmd);
+
+ cl_int
+ EnqueueSVMMap(cl_command_queue command_queue,
+ cl_bool blocking_map,
+ cl_map_flags map_flags,
+ void *svm_ptr,
+ size_t size,
+ cl_uint num_events_in_wait_list,
+ const cl_event *event_wait_list,
+ cl_event *event,
+ cl_int cmd);
+
+ cl_int
+ EnqueueSVMMemFill(cl_command_queue command_queue,
+ void *svm_ptr,
+ const void *pattern,
+ size_t pattern_size,
+ size_t size,
+ cl_uint num_events_in_wait_list,
+ const cl_event *event_wait_list,
+ cl_event *event,
+ cl_int cmd);
+
+ cl_int
+ EnqueueSVMUnmap(cl_command_queue command_queue,
+ void *svm_ptr,
+ cl_uint num_events_in_wait_list,
+ const cl_event *event_wait_list,
+ cl_event *event,
+ cl_int cmd);
+}
+
+#endif
diff --git a/src/gallium/frontends/clover/api/event.cpp b/src/gallium/frontends/clover/api/event.cpp
new file mode 100644
index 00000000000..3f89644d0a4
--- /dev/null
+++ b/src/gallium/frontends/clover/api/event.cpp
@@ -0,0 +1,309 @@
+//
+// Copyright 2012 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#include "api/util.hpp"
+#include "core/event.hpp"
+
+using namespace clover;
+
+CLOVER_API cl_event
+clCreateUserEvent(cl_context d_ctx, cl_int *r_errcode) try {
+ auto &ctx = obj(d_ctx);
+
+ ret_error(r_errcode, CL_SUCCESS);
+ return desc(new soft_event(ctx, {}, false));
+
+} catch (error &e) {
+ ret_error(r_errcode, e);
+ return NULL;
+}
+
+CLOVER_API cl_int
+clSetUserEventStatus(cl_event d_ev, cl_int status) try {
+ auto &sev = obj<soft_event>(d_ev);
+
+ if (status > 0)
+ return CL_INVALID_VALUE;
+
+ if (sev.status() <= 0)
+ return CL_INVALID_OPERATION;
+
+ if (status)
+ sev.abort(status);
+ else
+ sev.trigger();
+
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clWaitForEvents(cl_uint num_evs, const cl_event *d_evs) try {
+ auto evs = objs(d_evs, num_evs);
+
+ for (auto &ev : evs) {
+ if (ev.context() != evs.front().context())
+ throw error(CL_INVALID_CONTEXT);
+
+ if (ev.status() < 0)
+ throw error(CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST);
+ }
+
+ // Create a temporary soft event that depends on all the events in
+ // the wait list
+ auto sev = create<soft_event>(evs.front().context(), evs, true);
+
+ // ...and wait on it.
+ sev().wait();
+
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clGetEventInfo(cl_event d_ev, cl_event_info param,
+ size_t size, void *r_buf, size_t *r_size) try {
+ property_buffer buf { r_buf, size, r_size };
+ auto &ev = obj(d_ev);
+
+ switch (param) {
+ case CL_EVENT_COMMAND_QUEUE:
+ buf.as_scalar<cl_command_queue>() = desc(ev.queue());
+ break;
+
+ case CL_EVENT_CONTEXT:
+ buf.as_scalar<cl_context>() = desc(ev.context());
+ break;
+
+ case CL_EVENT_COMMAND_TYPE:
+ buf.as_scalar<cl_command_type>() = ev.command();
+ break;
+
+ case CL_EVENT_COMMAND_EXECUTION_STATUS:
+ buf.as_scalar<cl_int>() = ev.status();
+ break;
+
+ case CL_EVENT_REFERENCE_COUNT:
+ buf.as_scalar<cl_uint>() = ev.ref_count();
+ break;
+
+ default:
+ throw error(CL_INVALID_VALUE);
+ }
+
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clSetEventCallback(cl_event d_ev, cl_int type,
+ void (CL_CALLBACK *pfn_notify)(cl_event, cl_int, void *),
+ void *user_data) try {
+ auto &ev = obj(d_ev);
+
+ if (!pfn_notify ||
+ (type != CL_COMPLETE && type != CL_SUBMITTED && type != CL_RUNNING))
+ throw error(CL_INVALID_VALUE);
+
+ // Create a temporary soft event that depends on ev, with
+ // pfn_notify as completion action.
+ create<soft_event>(ev.context(), ref_vector<event> { ev }, true,
+ [=, &ev](event &) {
+ ev.wait();
+ pfn_notify(desc(ev), ev.status(), user_data);
+ });
+
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clRetainEvent(cl_event d_ev) try {
+ obj(d_ev).retain();
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clReleaseEvent(cl_event d_ev) try {
+ if (obj(d_ev).release())
+ delete pobj(d_ev);
+
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clEnqueueMarker(cl_command_queue d_q, cl_event *rd_ev) try {
+ auto &q = obj(d_q);
+
+ if (!rd_ev)
+ throw error(CL_INVALID_VALUE);
+
+ *rd_ev = desc(new hard_event(q, CL_COMMAND_MARKER, {}));
+
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clEnqueueMarkerWithWaitList(cl_command_queue d_q, cl_uint num_deps,
+ const cl_event *d_deps, cl_event *rd_ev) try {
+ auto &q = obj(d_q);
+ auto deps = objs<wait_list_tag>(d_deps, num_deps);
+
+ for (auto &ev : deps) {
+ if (ev.context() != q.context())
+ throw error(CL_INVALID_CONTEXT);
+ }
+
+ // Create a hard event that depends on the events in the wait list:
+ // previous commands in the same queue are implicitly serialized
+ // with respect to it -- hard events always are.
+ auto hev = create<hard_event>(q, CL_COMMAND_MARKER, deps);
+
+ ret_object(rd_ev, hev);
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clEnqueueBarrier(cl_command_queue d_q) try {
+ obj(d_q);
+
+ // No need to do anything, q preserves data ordering strictly.
+
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clEnqueueBarrierWithWaitList(cl_command_queue d_q, cl_uint num_deps,
+ const cl_event *d_deps, cl_event *rd_ev) try {
+ auto &q = obj(d_q);
+ auto deps = objs<wait_list_tag>(d_deps, num_deps);
+
+ for (auto &ev : deps) {
+ if (ev.context() != q.context())
+ throw error(CL_INVALID_CONTEXT);
+ }
+
+ // Create a hard event that depends on the events in the wait list:
+ // subsequent commands in the same queue will be implicitly
+ // serialized with respect to it -- hard events always are.
+ auto hev = create<hard_event>(q, CL_COMMAND_BARRIER, deps);
+
+ ret_object(rd_ev, hev);
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clEnqueueWaitForEvents(cl_command_queue d_q, cl_uint num_evs,
+ const cl_event *d_evs) try {
+ // The wait list is mandatory for clEnqueueWaitForEvents().
+ objs(d_evs, num_evs);
+
+ return clEnqueueBarrierWithWaitList(d_q, num_evs, d_evs, NULL);
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clGetEventProfilingInfo(cl_event d_ev, cl_profiling_info param,
+ size_t size, void *r_buf, size_t *r_size) try {
+ property_buffer buf { r_buf, size, r_size };
+ hard_event &hev = dynamic_cast<hard_event &>(obj(d_ev));
+
+ if (hev.status() != CL_COMPLETE)
+ throw error(CL_PROFILING_INFO_NOT_AVAILABLE);
+
+ switch (param) {
+ case CL_PROFILING_COMMAND_QUEUED:
+ buf.as_scalar<cl_ulong>() = hev.time_queued();
+ break;
+
+ case CL_PROFILING_COMMAND_SUBMIT:
+ buf.as_scalar<cl_ulong>() = hev.time_submit();
+ break;
+
+ case CL_PROFILING_COMMAND_START:
+ buf.as_scalar<cl_ulong>() = hev.time_start();
+ break;
+
+ case CL_PROFILING_COMMAND_END:
+ buf.as_scalar<cl_ulong>() = hev.time_end();
+ break;
+
+ default:
+ throw error(CL_INVALID_VALUE);
+ }
+
+ return CL_SUCCESS;
+
+} catch (std::bad_cast &e) {
+ return CL_PROFILING_INFO_NOT_AVAILABLE;
+
+} catch (lazy<cl_ulong>::undefined_error &e) {
+ return CL_PROFILING_INFO_NOT_AVAILABLE;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clFinish(cl_command_queue d_q) try {
+ auto &q = obj(d_q);
+
+ // Create a temporary hard event -- it implicitly depends on all
+ // the previously queued hard events.
+ auto hev = create<hard_event>(q, 0, ref_vector<event> {});
+
+ // And wait on it.
+ hev().wait();
+
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
diff --git a/src/gallium/frontends/clover/api/interop.cpp b/src/gallium/frontends/clover/api/interop.cpp
new file mode 100644
index 00000000000..b96069f5167
--- /dev/null
+++ b/src/gallium/frontends/clover/api/interop.cpp
@@ -0,0 +1,69 @@
+//
+// Copyright 2015 Advanced Micro Devices, Inc.
+// All Rights Reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#include "core/event.hpp"
+#include "api/util.hpp"
+
+using namespace clover;
+
+extern "C" {
+
+PUBLIC bool
+opencl_dri_event_add_ref(cl_event event)
+{
+ /* This should fail if the event hasn't been created by
+ * clEnqueueReleaseGLObjects or clEnqueueReleaseEGLObjects.
+ *
+ * TODO: implement the CL functions
+ */
+ return false; /*return clRetainEvent(event) == CL_SUCCESS;*/
+}
+
+PUBLIC bool
+opencl_dri_event_release(cl_event event)
+{
+ return clReleaseEvent(event) == CL_SUCCESS;
+}
+
+PUBLIC bool
+opencl_dri_event_wait(cl_event event, uint64_t timeout) try {
+ if (!timeout) {
+ return obj(event).status() == CL_COMPLETE;
+ }
+
+ obj(event).wait();
+ return true;
+
+} catch (error &) {
+ return false;
+}
+
+PUBLIC struct pipe_fence_handle *
+opencl_dri_event_get_fence(cl_event event) try {
+ return obj(event).fence();
+
+} catch (error &) {
+ return NULL;
+}
+
+}
diff --git a/src/gallium/frontends/clover/api/kernel.cpp b/src/gallium/frontends/clover/api/kernel.cpp
new file mode 100644
index 00000000000..31a87b63868
--- /dev/null
+++ b/src/gallium/frontends/clover/api/kernel.cpp
@@ -0,0 +1,390 @@
+//
+// Copyright 2012 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#include "api/util.hpp"
+#include "core/kernel.hpp"
+#include "core/event.hpp"
+
+using namespace clover;
+
+CLOVER_API cl_kernel
+clCreateKernel(cl_program d_prog, const char *name, cl_int *r_errcode) try {
+ auto &prog = obj(d_prog);
+
+ if (!name)
+ throw error(CL_INVALID_VALUE);
+
+ auto &sym = find(name_equals(name), prog.symbols());
+
+ ret_error(r_errcode, CL_SUCCESS);
+ return new kernel(prog, name, range(sym.args));
+
+} catch (std::out_of_range &e) {
+ ret_error(r_errcode, CL_INVALID_KERNEL_NAME);
+ return NULL;
+
+} catch (error &e) {
+ ret_error(r_errcode, e);
+ return NULL;
+}
+
+CLOVER_API cl_int
+clCreateKernelsInProgram(cl_program d_prog, cl_uint count,
+ cl_kernel *rd_kerns, cl_uint *r_count) try {
+ auto &prog = obj(d_prog);
+ auto &syms = prog.symbols();
+
+ if (rd_kerns && count < syms.size())
+ throw error(CL_INVALID_VALUE);
+
+ if (rd_kerns)
+ copy(map([&](const module::symbol &sym) {
+ return desc(new kernel(prog,
+ std::string(sym.name.begin(),
+ sym.name.end()),
+ range(sym.args)));
+ }, syms),
+ rd_kerns);
+
+ if (r_count)
+ *r_count = syms.size();
+
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clRetainKernel(cl_kernel d_kern) try {
+ obj(d_kern).retain();
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clReleaseKernel(cl_kernel d_kern) try {
+ if (obj(d_kern).release())
+ delete pobj(d_kern);
+
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clSetKernelArg(cl_kernel d_kern, cl_uint idx, size_t size,
+ const void *value) try {
+ obj(d_kern).args().at(idx).set(size, value);
+ return CL_SUCCESS;
+
+} catch (std::out_of_range &e) {
+ return CL_INVALID_ARG_INDEX;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clGetKernelInfo(cl_kernel d_kern, cl_kernel_info param,
+ size_t size, void *r_buf, size_t *r_size) try {
+ property_buffer buf { r_buf, size, r_size };
+ auto &kern = obj(d_kern);
+
+ switch (param) {
+ case CL_KERNEL_FUNCTION_NAME:
+ buf.as_string() = kern.name();
+ break;
+
+ case CL_KERNEL_NUM_ARGS:
+ buf.as_scalar<cl_uint>() = kern.args().size();
+ break;
+
+ case CL_KERNEL_REFERENCE_COUNT:
+ buf.as_scalar<cl_uint>() = kern.ref_count();
+ break;
+
+ case CL_KERNEL_CONTEXT:
+ buf.as_scalar<cl_context>() = desc(kern.program().context());
+ break;
+
+ case CL_KERNEL_PROGRAM:
+ buf.as_scalar<cl_program>() = desc(kern.program());
+ break;
+
+ default:
+ throw error(CL_INVALID_VALUE);
+ }
+
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clGetKernelWorkGroupInfo(cl_kernel d_kern, cl_device_id d_dev,
+ cl_kernel_work_group_info param,
+ size_t size, void *r_buf, size_t *r_size) try {
+ property_buffer buf { r_buf, size, r_size };
+ auto &kern = obj(d_kern);
+ auto &dev = (d_dev ? *pobj(d_dev) : unique(kern.program().devices()));
+
+ if (!count(dev, kern.program().devices()))
+ throw error(CL_INVALID_DEVICE);
+
+ switch (param) {
+ case CL_KERNEL_WORK_GROUP_SIZE:
+ buf.as_scalar<size_t>() = dev.max_threads_per_block();
+ break;
+
+ case CL_KERNEL_COMPILE_WORK_GROUP_SIZE:
+ buf.as_vector<size_t>() = kern.required_block_size();
+ break;
+
+ case CL_KERNEL_LOCAL_MEM_SIZE:
+ buf.as_scalar<cl_ulong>() = kern.mem_local();
+ break;
+
+ case CL_KERNEL_PREFERRED_WORK_GROUP_SIZE_MULTIPLE:
+ buf.as_scalar<size_t>() = dev.subgroup_size();
+ break;
+
+ case CL_KERNEL_PRIVATE_MEM_SIZE:
+ buf.as_scalar<cl_ulong>() = kern.mem_private();
+ break;
+
+ default:
+ throw error(CL_INVALID_VALUE);
+ }
+
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+
+} catch (std::out_of_range &e) {
+ return CL_INVALID_DEVICE;
+}
+
+CLOVER_API cl_int
+clGetKernelArgInfo(cl_kernel d_kern,
+ cl_uint idx, cl_kernel_arg_info param,
+ size_t size, void *r_buf, size_t *r_size) {
+ CLOVER_NOT_SUPPORTED_UNTIL("1.2");
+ return CL_KERNEL_ARG_INFO_NOT_AVAILABLE;
+}
+
+namespace {
+ ///
+ /// Common argument checking shared by kernel invocation commands.
+ ///
+ void
+ validate_common(const command_queue &q, kernel &kern,
+ const ref_vector<event> &deps) {
+ if (kern.program().context() != q.context() ||
+ any_of([&](const event &ev) {
+ return ev.context() != q.context();
+ }, deps))
+ throw error(CL_INVALID_CONTEXT);
+
+ if (any_of([](kernel::argument &arg) {
+ return !arg.set();
+ }, kern.args()))
+ throw error(CL_INVALID_KERNEL_ARGS);
+
+ // If the command queue's device is not associated to the program, we get
+ // a module, with no sections, which will also fail the following test.
+ auto &m = kern.program().build(q.device()).binary;
+ if (!any_of(type_equals(module::section::text_executable), m.secs))
+ throw error(CL_INVALID_PROGRAM_EXECUTABLE);
+ }
+
+ std::vector<size_t>
+ validate_grid_size(const command_queue &q, cl_uint dims,
+ const size_t *d_grid_size) {
+ auto grid_size = range(d_grid_size, dims);
+
+ if (dims < 1 || dims > q.device().max_block_size().size())
+ throw error(CL_INVALID_WORK_DIMENSION);
+
+ if (!d_grid_size || any_of(is_zero(), grid_size))
+ throw error(CL_INVALID_GLOBAL_WORK_SIZE);
+
+ return grid_size;
+ }
+
+ std::vector<size_t>
+ validate_grid_offset(const command_queue &q, cl_uint dims,
+ const size_t *d_grid_offset) {
+ if (d_grid_offset)
+ return range(d_grid_offset, dims);
+ else
+ return std::vector<size_t>(dims, 0);
+ }
+
+ std::vector<size_t>
+ validate_block_size(const command_queue &q, const kernel &kern,
+ cl_uint dims, const size_t *d_grid_size,
+ const size_t *d_block_size) {
+ auto grid_size = range(d_grid_size, dims);
+
+ if (d_block_size) {
+ auto block_size = range(d_block_size, dims);
+
+ if (any_of(is_zero(), block_size) ||
+ any_of(greater(), block_size, q.device().max_block_size()))
+ throw error(CL_INVALID_WORK_ITEM_SIZE);
+
+ if (any_of(modulus(), grid_size, block_size))
+ throw error(CL_INVALID_WORK_GROUP_SIZE);
+
+ if (fold(multiplies(), 1u, block_size) >
+ q.device().max_threads_per_block())
+ throw error(CL_INVALID_WORK_GROUP_SIZE);
+
+ return block_size;
+
+ } else {
+ return kern.optimal_block_size(q, grid_size);
+ }
+ }
+}
+
+CLOVER_API cl_int
+clEnqueueNDRangeKernel(cl_command_queue d_q, cl_kernel d_kern,
+ cl_uint dims, const size_t *d_grid_offset,
+ const size_t *d_grid_size, const size_t *d_block_size,
+ cl_uint num_deps, const cl_event *d_deps,
+ cl_event *rd_ev) try {
+ auto &q = obj(d_q);
+ auto &kern = obj(d_kern);
+ auto deps = objs<wait_list_tag>(d_deps, num_deps);
+ auto grid_size = validate_grid_size(q, dims, d_grid_size);
+ auto grid_offset = validate_grid_offset(q, dims, d_grid_offset);
+ auto block_size = validate_block_size(q, kern, dims,
+ d_grid_size, d_block_size);
+
+ validate_common(q, kern, deps);
+
+ auto hev = create<hard_event>(
+ q, CL_COMMAND_NDRANGE_KERNEL, deps,
+ [=, &kern, &q](event &) {
+ kern.launch(q, grid_offset, grid_size, block_size);
+ });
+
+ ret_object(rd_ev, hev);
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clEnqueueTask(cl_command_queue d_q, cl_kernel d_kern,
+ cl_uint num_deps, const cl_event *d_deps,
+ cl_event *rd_ev) try {
+ auto &q = obj(d_q);
+ auto &kern = obj(d_kern);
+ auto deps = objs<wait_list_tag>(d_deps, num_deps);
+
+ validate_common(q, kern, deps);
+
+ auto hev = create<hard_event>(
+ q, CL_COMMAND_TASK, deps,
+ [=, &kern, &q](event &) {
+ kern.launch(q, { 0 }, { 1 }, { 1 });
+ });
+
+ ret_object(rd_ev, hev);
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clEnqueueNativeKernel(cl_command_queue d_q, void (*func)(void *),
+ void *args, size_t args_size,
+ cl_uint num_mems, const cl_mem *d_mems,
+ const void **mem_handles, cl_uint num_deps,
+ const cl_event *d_deps, cl_event *rd_ev) {
+ return CL_INVALID_OPERATION;
+}
+
+CLOVER_API cl_int
+clSetKernelArgSVMPointer(cl_kernel d_kern,
+ cl_uint arg_index,
+ const void *arg_value) try {
+ obj(d_kern).args().at(arg_index).set_svm(arg_value);
+ return CL_SUCCESS;
+
+} catch (std::out_of_range &e) {
+ return CL_INVALID_ARG_INDEX;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clSetKernelExecInfo(cl_kernel d_kern,
+ cl_kernel_exec_info param_name,
+ size_t param_value_size,
+ const void *param_value) try {
+ auto &kern = obj(d_kern);
+ const bool has_system_svm = all_of(std::mem_fn(&device::has_system_svm),
+ kern.program().context().devices());
+
+ if (!param_value)
+ return CL_INVALID_VALUE;
+
+ switch (param_name) {
+ case CL_KERNEL_EXEC_INFO_SVM_FINE_GRAIN_SYSTEM:
+ case CL_KERNEL_EXEC_INFO_SVM_FINE_GRAIN_SYSTEM_ARM: {
+ if (param_value_size != sizeof(cl_bool))
+ return CL_INVALID_VALUE;
+
+ cl_bool val = *static_cast<const cl_bool*>(param_value);
+ if (val == CL_TRUE && !has_system_svm)
+ return CL_INVALID_OPERATION;
+ else
+ return CL_SUCCESS;
+ }
+
+ case CL_KERNEL_EXEC_INFO_SVM_PTRS:
+ case CL_KERNEL_EXEC_INFO_SVM_PTRS_ARM:
+ if (has_system_svm)
+ return CL_SUCCESS;
+
+ CLOVER_NOT_SUPPORTED_UNTIL("2.0");
+ return CL_INVALID_VALUE;
+
+ default:
+ return CL_INVALID_VALUE;
+ }
+
+} catch (error &e) {
+ return e.get();
+}
diff --git a/src/gallium/frontends/clover/api/memory.cpp b/src/gallium/frontends/clover/api/memory.cpp
new file mode 100644
index 00000000000..e03793339c1
--- /dev/null
+++ b/src/gallium/frontends/clover/api/memory.cpp
@@ -0,0 +1,497 @@
+//
+// Copyright 2012 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#include "util/u_math.h"
+#include "api/util.hpp"
+#include "core/memory.hpp"
+#include "core/format.hpp"
+
+using namespace clover;
+
+namespace {
+ cl_mem_flags
+ validate_flags(cl_mem d_parent, cl_mem_flags d_flags, bool svm) {
+ const cl_mem_flags dev_access_flags =
+ CL_MEM_READ_WRITE | CL_MEM_WRITE_ONLY | CL_MEM_READ_ONLY;
+ const cl_mem_flags host_ptr_flags =
+ CL_MEM_USE_HOST_PTR | CL_MEM_ALLOC_HOST_PTR | CL_MEM_COPY_HOST_PTR;
+ const cl_mem_flags host_access_flags =
+ CL_MEM_HOST_WRITE_ONLY | CL_MEM_HOST_READ_ONLY | CL_MEM_HOST_NO_ACCESS;
+ const cl_mem_flags svm_flags =
+ CL_MEM_SVM_FINE_GRAIN_BUFFER | CL_MEM_SVM_ATOMICS;
+
+ const cl_mem_flags valid_flags =
+ dev_access_flags
+ | (svm || d_parent ? 0 : host_ptr_flags)
+ | (svm ? svm_flags : host_access_flags);
+
+ if ((d_flags & ~valid_flags) ||
+ util_bitcount(d_flags & dev_access_flags) > 1 ||
+ util_bitcount(d_flags & host_access_flags) > 1)
+ throw error(CL_INVALID_VALUE);
+
+ if ((d_flags & CL_MEM_USE_HOST_PTR) &&
+ (d_flags & (CL_MEM_COPY_HOST_PTR | CL_MEM_ALLOC_HOST_PTR)))
+ throw error(CL_INVALID_VALUE);
+
+ if ((d_flags & CL_MEM_SVM_ATOMICS) &&
+ !(d_flags & CL_MEM_SVM_FINE_GRAIN_BUFFER))
+ throw error(CL_INVALID_VALUE);
+
+ if (d_parent) {
+ const auto &parent = obj(d_parent);
+ const cl_mem_flags flags = (d_flags |
+ (d_flags & dev_access_flags ? 0 :
+ parent.flags() & dev_access_flags) |
+ (d_flags & host_access_flags ? 0 :
+ parent.flags() & host_access_flags) |
+ (parent.flags() & host_ptr_flags));
+
+ if (~flags & parent.flags() & (dev_access_flags & ~CL_MEM_READ_WRITE))
+ throw error(CL_INVALID_VALUE);
+
+ // Check if new host access flags cause a mismatch between
+ // host-read/write-only.
+ if (!(flags & CL_MEM_HOST_NO_ACCESS) &&
+ (~flags & parent.flags() & host_access_flags))
+ throw error(CL_INVALID_VALUE);
+
+ return flags;
+
+ } else {
+ return d_flags | (d_flags & dev_access_flags ? 0 : CL_MEM_READ_WRITE);
+ }
+ }
+}
+
+CLOVER_API cl_mem
+clCreateBuffer(cl_context d_ctx, cl_mem_flags d_flags, size_t size,
+ void *host_ptr, cl_int *r_errcode) try {
+ const cl_mem_flags flags = validate_flags(NULL, d_flags, false);
+ auto &ctx = obj(d_ctx);
+
+ if (bool(host_ptr) != bool(flags & (CL_MEM_USE_HOST_PTR |
+ CL_MEM_COPY_HOST_PTR)))
+ throw error(CL_INVALID_HOST_PTR);
+
+ if (!size ||
+ size > fold(maximum(), cl_ulong(0),
+ map(std::mem_fn(&device::max_mem_alloc_size), ctx.devices())
+ ))
+ throw error(CL_INVALID_BUFFER_SIZE);
+
+ ret_error(r_errcode, CL_SUCCESS);
+ return new root_buffer(ctx, flags, size, host_ptr);
+
+} catch (error &e) {
+ ret_error(r_errcode, e);
+ return NULL;
+}
+
+CLOVER_API cl_mem
+clCreateSubBuffer(cl_mem d_mem, cl_mem_flags d_flags,
+ cl_buffer_create_type op,
+ const void *op_info, cl_int *r_errcode) try {
+ auto &parent = obj<root_buffer>(d_mem);
+ const cl_mem_flags flags = validate_flags(d_mem, d_flags, false);
+
+ if (op == CL_BUFFER_CREATE_TYPE_REGION) {
+ auto reg = reinterpret_cast<const cl_buffer_region *>(op_info);
+
+ if (!reg ||
+ reg->origin > parent.size() ||
+ reg->origin + reg->size > parent.size())
+ throw error(CL_INVALID_VALUE);
+
+ if (!reg->size)
+ throw error(CL_INVALID_BUFFER_SIZE);
+
+ ret_error(r_errcode, CL_SUCCESS);
+ return new sub_buffer(parent, flags, reg->origin, reg->size);
+
+ } else {
+ throw error(CL_INVALID_VALUE);
+ }
+
+} catch (error &e) {
+ ret_error(r_errcode, e);
+ return NULL;
+}
+
+CLOVER_API cl_mem
+clCreateImage(cl_context d_ctx, cl_mem_flags d_flags,
+ const cl_image_format *format,
+ const cl_image_desc *desc,
+ void *host_ptr, cl_int *r_errcode) try {
+ auto &ctx = obj(d_ctx);
+
+ if (!any_of(std::mem_fn(&device::image_support), ctx.devices()))
+ throw error(CL_INVALID_OPERATION);
+
+ if (!format)
+ throw error(CL_INVALID_IMAGE_FORMAT_DESCRIPTOR);
+
+ if (!desc)
+ throw error(CL_INVALID_IMAGE_DESCRIPTOR);
+
+ if (desc->image_array_size == 0 &&
+ (desc->image_type == CL_MEM_OBJECT_IMAGE1D_ARRAY ||
+ desc->image_type == CL_MEM_OBJECT_IMAGE2D_ARRAY))
+ throw error(CL_INVALID_IMAGE_DESCRIPTOR);
+
+ if (!host_ptr &&
+ (desc->image_row_pitch || desc->image_slice_pitch))
+ throw error(CL_INVALID_IMAGE_DESCRIPTOR);
+
+ if (desc->num_mip_levels || desc->num_samples)
+ throw error(CL_INVALID_IMAGE_DESCRIPTOR);
+
+ if (bool(desc->buffer) != (desc->image_type == CL_MEM_OBJECT_IMAGE1D_BUFFER))
+ throw error(CL_INVALID_IMAGE_DESCRIPTOR);
+
+ if (bool(host_ptr) != bool(d_flags & (CL_MEM_USE_HOST_PTR |
+ CL_MEM_COPY_HOST_PTR)))
+ throw error(CL_INVALID_HOST_PTR);
+
+ const cl_mem_flags flags = validate_flags(desc->buffer, d_flags, false);
+
+ if (!supported_formats(ctx, desc->image_type).count(*format))
+ throw error(CL_IMAGE_FORMAT_NOT_SUPPORTED);
+
+ ret_error(r_errcode, CL_SUCCESS);
+
+ switch (desc->image_type) {
+ case CL_MEM_OBJECT_IMAGE2D:
+ if (!desc->image_width || !desc->image_height)
+ throw error(CL_INVALID_IMAGE_SIZE);
+
+ if (all_of([=](const device &dev) {
+ const size_t max = 1 << dev.max_image_levels_2d();
+ return (desc->image_width > max ||
+ desc->image_height > max);
+ }, ctx.devices()))
+ throw error(CL_INVALID_IMAGE_SIZE);
+
+ return new image2d(ctx, flags, format,
+ desc->image_width, desc->image_height,
+ desc->image_row_pitch, host_ptr);
+
+ case CL_MEM_OBJECT_IMAGE3D:
+ if (!desc->image_width || !desc->image_height || !desc->image_depth)
+ throw error(CL_INVALID_IMAGE_SIZE);
+
+ if (all_of([=](const device &dev) {
+ const size_t max = 1 << dev.max_image_levels_3d();
+ return (desc->image_width > max ||
+ desc->image_height > max ||
+ desc->image_depth > max);
+ }, ctx.devices()))
+ throw error(CL_INVALID_IMAGE_SIZE);
+
+ return new image3d(ctx, flags, format,
+ desc->image_width, desc->image_height,
+ desc->image_depth, desc->image_row_pitch,
+ desc->image_slice_pitch, host_ptr);
+
+ case CL_MEM_OBJECT_IMAGE1D:
+ case CL_MEM_OBJECT_IMAGE1D_ARRAY:
+ case CL_MEM_OBJECT_IMAGE1D_BUFFER:
+ case CL_MEM_OBJECT_IMAGE2D_ARRAY:
+ // XXX - Not implemented.
+ throw error(CL_IMAGE_FORMAT_NOT_SUPPORTED);
+
+ default:
+ throw error(CL_INVALID_IMAGE_DESCRIPTOR);
+ }
+
+} catch (error &e) {
+ ret_error(r_errcode, e);
+ return NULL;
+}
+
+CLOVER_API cl_mem
+clCreateImage2D(cl_context d_ctx, cl_mem_flags d_flags,
+ const cl_image_format *format,
+ size_t width, size_t height, size_t row_pitch,
+ void *host_ptr, cl_int *r_errcode) {
+ const cl_image_desc desc = { CL_MEM_OBJECT_IMAGE2D, width, height, 0, 0,
+ row_pitch, 0, 0, 0, NULL };
+
+ return clCreateImage(d_ctx, d_flags, format, &desc, host_ptr, r_errcode);
+}
+
+CLOVER_API cl_mem
+clCreateImage3D(cl_context d_ctx, cl_mem_flags d_flags,
+ const cl_image_format *format,
+ size_t width, size_t height, size_t depth,
+ size_t row_pitch, size_t slice_pitch,
+ void *host_ptr, cl_int *r_errcode) {
+ const cl_image_desc desc = { CL_MEM_OBJECT_IMAGE3D, width, height, depth, 0,
+ row_pitch, slice_pitch, 0, 0, NULL };
+
+ return clCreateImage(d_ctx, d_flags, format, &desc, host_ptr, r_errcode);
+}
+
+CLOVER_API cl_int
+clGetSupportedImageFormats(cl_context d_ctx, cl_mem_flags flags,
+ cl_mem_object_type type, cl_uint count,
+ cl_image_format *r_buf, cl_uint *r_count) try {
+ auto &ctx = obj(d_ctx);
+ auto formats = supported_formats(ctx, type);
+
+ validate_flags(NULL, flags, false);
+
+ if (r_buf && !r_count)
+ throw error(CL_INVALID_VALUE);
+
+ if (r_buf)
+ std::copy_n(formats.begin(),
+ std::min((cl_uint)formats.size(), count),
+ r_buf);
+
+ if (r_count)
+ *r_count = formats.size();
+
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clGetMemObjectInfo(cl_mem d_mem, cl_mem_info param,
+ size_t size, void *r_buf, size_t *r_size) try {
+ property_buffer buf { r_buf, size, r_size };
+ auto &mem = obj(d_mem);
+
+ switch (param) {
+ case CL_MEM_TYPE:
+ buf.as_scalar<cl_mem_object_type>() = mem.type();
+ break;
+
+ case CL_MEM_FLAGS:
+ buf.as_scalar<cl_mem_flags>() = mem.flags();
+ break;
+
+ case CL_MEM_SIZE:
+ buf.as_scalar<size_t>() = mem.size();
+ break;
+
+ case CL_MEM_HOST_PTR:
+ buf.as_scalar<void *>() = mem.host_ptr();
+ break;
+
+ case CL_MEM_MAP_COUNT:
+ buf.as_scalar<cl_uint>() = 0;
+ break;
+
+ case CL_MEM_REFERENCE_COUNT:
+ buf.as_scalar<cl_uint>() = mem.ref_count();
+ break;
+
+ case CL_MEM_CONTEXT:
+ buf.as_scalar<cl_context>() = desc(mem.context());
+ break;
+
+ case CL_MEM_ASSOCIATED_MEMOBJECT: {
+ sub_buffer *sub = dynamic_cast<sub_buffer *>(&mem);
+ buf.as_scalar<cl_mem>() = (sub ? desc(sub->parent()) : NULL);
+ break;
+ }
+ case CL_MEM_OFFSET: {
+ sub_buffer *sub = dynamic_cast<sub_buffer *>(&mem);
+ buf.as_scalar<size_t>() = (sub ? sub->offset() : 0);
+ break;
+ }
+ case CL_MEM_USES_SVM_POINTER:
+ case CL_MEM_USES_SVM_POINTER_ARM: {
+ // with system SVM all host ptrs are SVM pointers
+ // TODO: once we support devices with lower levels of SVM, we have to
+ // check the ptr in more detail
+ const bool system_svm = all_of(std::mem_fn(&device::has_system_svm),
+ mem.context().devices());
+ buf.as_scalar<cl_bool>() = mem.host_ptr() && system_svm;
+ break;
+ }
+ default:
+ throw error(CL_INVALID_VALUE);
+ }
+
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clGetImageInfo(cl_mem d_mem, cl_image_info param,
+ size_t size, void *r_buf, size_t *r_size) try {
+ property_buffer buf { r_buf, size, r_size };
+ auto &img = obj<image>(d_mem);
+
+ switch (param) {
+ case CL_IMAGE_FORMAT:
+ buf.as_scalar<cl_image_format>() = img.format();
+ break;
+
+ case CL_IMAGE_ELEMENT_SIZE:
+ buf.as_scalar<size_t>() = 0;
+ break;
+
+ case CL_IMAGE_ROW_PITCH:
+ buf.as_scalar<size_t>() = img.row_pitch();
+ break;
+
+ case CL_IMAGE_SLICE_PITCH:
+ buf.as_scalar<size_t>() = img.slice_pitch();
+ break;
+
+ case CL_IMAGE_WIDTH:
+ buf.as_scalar<size_t>() = img.width();
+ break;
+
+ case CL_IMAGE_HEIGHT:
+ buf.as_scalar<size_t>() = img.height();
+ break;
+
+ case CL_IMAGE_DEPTH:
+ buf.as_scalar<size_t>() = img.depth();
+ break;
+
+ default:
+ throw error(CL_INVALID_VALUE);
+ }
+
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clRetainMemObject(cl_mem d_mem) try {
+ obj(d_mem).retain();
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clReleaseMemObject(cl_mem d_mem) try {
+ if (obj(d_mem).release())
+ delete pobj(d_mem);
+
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clSetMemObjectDestructorCallback(cl_mem d_mem,
+ void (CL_CALLBACK *pfn_notify)(cl_mem, void *),
+ void *user_data) try {
+ auto &mem = obj(d_mem);
+
+ if (!pfn_notify)
+ return CL_INVALID_VALUE;
+
+ mem.destroy_notify([=]{ pfn_notify(d_mem, user_data); });
+
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clEnqueueFillBuffer(cl_command_queue command_queue, cl_mem buffer,
+ const void *pattern, size_t pattern_size,
+ size_t offset, size_t size,
+ cl_uint num_events_in_wait_list,
+ const cl_event *event_wait_list,
+ cl_event *event) {
+ CLOVER_NOT_SUPPORTED_UNTIL("1.2");
+ return CL_INVALID_VALUE;
+}
+
+CLOVER_API cl_int
+clEnqueueFillImage(cl_command_queue command_queue, cl_mem image,
+ const void *fill_color,
+ const size_t *origin, const size_t *region,
+ cl_uint num_events_in_wait_list,
+ const cl_event *event_wait_list,
+ cl_event *event) {
+ CLOVER_NOT_SUPPORTED_UNTIL("1.2");
+ return CL_INVALID_VALUE;
+}
+
+CLOVER_API void *
+clSVMAlloc(cl_context d_ctx,
+ cl_svm_mem_flags flags,
+ size_t size,
+ unsigned int alignment) try {
+ auto &ctx = obj(d_ctx);
+ validate_flags(NULL, flags, true);
+
+ if (!size ||
+ size > fold(minimum(), cl_ulong(ULONG_MAX),
+ map(std::mem_fn(&device::max_mem_alloc_size), ctx.devices())))
+ return nullptr;
+
+ if (!util_is_power_of_two_or_zero(alignment))
+ return nullptr;
+
+ if (!alignment)
+ alignment = 0x80; // sizeof(long16)
+
+ bool can_emulate = all_of(std::mem_fn(&device::has_system_svm), ctx.devices());
+ if (can_emulate) {
+ // we can ignore all the flags as it's not required to honor them.
+ void *ptr = nullptr;
+ if (alignment < sizeof(void*))
+ alignment = sizeof(void*);
+ posix_memalign(&ptr, alignment, size);
+ return ptr;
+ }
+
+ CLOVER_NOT_SUPPORTED_UNTIL("2.0");
+ return nullptr;
+
+} catch (error &e) {
+ return nullptr;
+}
+
+CLOVER_API void
+clSVMFree(cl_context d_ctx,
+ void *svm_pointer) try {
+ auto &ctx = obj(d_ctx);
+ bool can_emulate = all_of(std::mem_fn(&device::has_system_svm), ctx.devices());
+
+ if (can_emulate)
+ return free(svm_pointer);
+
+ CLOVER_NOT_SUPPORTED_UNTIL("2.0");
+
+} catch (error &e) {
+}
diff --git a/src/gallium/frontends/clover/api/platform.cpp b/src/gallium/frontends/clover/api/platform.cpp
new file mode 100644
index 00000000000..7360461e62f
--- /dev/null
+++ b/src/gallium/frontends/clover/api/platform.cpp
@@ -0,0 +1,235 @@
+//
+// Copyright 2012 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#include <unordered_map>
+
+#include "api/dispatch.hpp"
+#include "api/util.hpp"
+#include "core/platform.hpp"
+#include "git_sha1.h"
+#include "util/u_debug.h"
+
+using namespace clover;
+
+namespace {
+ platform _clover_platform;
+}
+
+CLOVER_API cl_int
+clGetPlatformIDs(cl_uint num_entries, cl_platform_id *rd_platforms,
+ cl_uint *rnum_platforms) {
+ if ((!num_entries && rd_platforms) ||
+ (!rnum_platforms && !rd_platforms))
+ return CL_INVALID_VALUE;
+
+ if (rnum_platforms)
+ *rnum_platforms = 1;
+ if (rd_platforms)
+ *rd_platforms = desc(_clover_platform);
+
+ return CL_SUCCESS;
+}
+
+cl_int
+clover::GetPlatformInfo(cl_platform_id d_platform, cl_platform_info param,
+ size_t size, void *r_buf, size_t *r_size) try {
+ property_buffer buf { r_buf, size, r_size };
+
+ auto &platform = obj(d_platform);
+
+ switch (param) {
+ case CL_PLATFORM_PROFILE:
+ buf.as_string() = "FULL_PROFILE";
+ break;
+
+ case CL_PLATFORM_VERSION: {
+ static const std::string version_string =
+ debug_get_option("CLOVER_PLATFORM_VERSION_OVERRIDE", "1.1");
+
+ buf.as_string() = "OpenCL " + version_string + " Mesa " PACKAGE_VERSION MESA_GIT_SHA1;
+ break;
+ }
+ case CL_PLATFORM_NAME:
+ buf.as_string() = "Clover";
+ break;
+
+ case CL_PLATFORM_VENDOR:
+ buf.as_string() = "Mesa";
+ break;
+
+ case CL_PLATFORM_EXTENSIONS:
+ buf.as_string() = platform.supported_extensions();
+ break;
+
+ case CL_PLATFORM_ICD_SUFFIX_KHR:
+ buf.as_string() = "MESA";
+ break;
+
+ default:
+ throw error(CL_INVALID_VALUE);
+ }
+
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
+
+void *
+clover::GetExtensionFunctionAddressForPlatform(cl_platform_id d_platform,
+ const char *p_name) try {
+ obj(d_platform);
+ return GetExtensionFunctionAddress(p_name);
+
+} catch (error &e) {
+ return NULL;
+}
+
+namespace {
+
+cl_int
+enqueueSVMFreeARM(cl_command_queue command_queue,
+ cl_uint num_svm_pointers,
+ void *svm_pointers[],
+ void (CL_CALLBACK *pfn_free_func) (
+ cl_command_queue queue, cl_uint num_svm_pointers,
+ void *svm_pointers[], void *user_data),
+ void *user_data,
+ cl_uint num_events_in_wait_list,
+ const cl_event *event_wait_list,
+ cl_event *event) {
+
+ return EnqueueSVMFree(command_queue, num_svm_pointers, svm_pointers,
+ pfn_free_func, user_data, num_events_in_wait_list,
+ event_wait_list, event, CL_COMMAND_SVM_FREE_ARM);
+}
+
+cl_int
+enqueueSVMMapARM(cl_command_queue command_queue,
+ cl_bool blocking_map,
+ cl_map_flags map_flags,
+ void *svm_ptr,
+ size_t size,
+ cl_uint num_events_in_wait_list,
+ const cl_event *event_wait_list,
+ cl_event *event) {
+
+ return EnqueueSVMMap(command_queue, blocking_map, map_flags, svm_ptr, size,
+ num_events_in_wait_list, event_wait_list, event,
+ CL_COMMAND_SVM_MAP_ARM);
+}
+
+cl_int
+enqueueSVMMemcpyARM(cl_command_queue command_queue,
+ cl_bool blocking_copy,
+ void *dst_ptr,
+ const void *src_ptr,
+ size_t size,
+ cl_uint num_events_in_wait_list,
+ const cl_event *event_wait_list,
+ cl_event *event) {
+
+ return EnqueueSVMMemcpy(command_queue, blocking_copy, dst_ptr, src_ptr,
+ size, num_events_in_wait_list, event_wait_list,
+ event, CL_COMMAND_SVM_MEMCPY_ARM);
+}
+
+cl_int
+enqueueSVMMemFillARM(cl_command_queue command_queue,
+ void *svm_ptr,
+ const void *pattern,
+ size_t pattern_size,
+ size_t size,
+ cl_uint num_events_in_wait_list,
+ const cl_event *event_wait_list,
+ cl_event *event) {
+
+ return EnqueueSVMMemFill(command_queue, svm_ptr, pattern, pattern_size,
+ size, num_events_in_wait_list, event_wait_list,
+ event, CL_COMMAND_SVM_MEMFILL_ARM);
+}
+
+cl_int
+enqueueSVMUnmapARM(cl_command_queue command_queue,
+ void *svm_ptr,
+ cl_uint num_events_in_wait_list,
+ const cl_event *event_wait_list,
+ cl_event *event) {
+
+ return EnqueueSVMUnmap(command_queue, svm_ptr, num_events_in_wait_list,
+ event_wait_list, event, CL_COMMAND_SVM_UNMAP_ARM);
+}
+
+const std::unordered_map<std::string, void *>
+ext_funcs = {
+ // cl_arm_shared_virtual_memory
+ { "clEnqueueSVMFreeARM", reinterpret_cast<void *>(enqueueSVMFreeARM) },
+ { "clEnqueueSVMMapARM", reinterpret_cast<void *>(enqueueSVMMapARM) },
+ { "clEnqueueSVMMemcpyARM", reinterpret_cast<void *>(enqueueSVMMemcpyARM) },
+ { "clEnqueueSVMMemFillARM", reinterpret_cast<void *>(enqueueSVMMemFillARM) },
+ { "clEnqueueSVMUnmapARM", reinterpret_cast<void *>(enqueueSVMUnmapARM) },
+ { "clSetKernelArgSVMPointerARM", reinterpret_cast<void *>(clSetKernelArgSVMPointer) },
+ { "clSetKernelExecInfoARM", reinterpret_cast<void *>(clSetKernelExecInfo) },
+ { "clSVMAllocARM", reinterpret_cast<void *>(clSVMAlloc) },
+ { "clSVMFreeARM", reinterpret_cast<void *>(clSVMFree) },
+
+ // cl_khr_icd
+ { "clIcdGetPlatformIDsKHR", reinterpret_cast<void *>(IcdGetPlatformIDsKHR) },
+};
+
+} // anonymous namespace
+
+void *
+clover::GetExtensionFunctionAddress(const char *p_name) try {
+ return ext_funcs.at(p_name);
+} catch (...) {
+ return nullptr;
+}
+
+cl_int
+clover::IcdGetPlatformIDsKHR(cl_uint num_entries, cl_platform_id *rd_platforms,
+ cl_uint *rnum_platforms) {
+ return clGetPlatformIDs(num_entries, rd_platforms, rnum_platforms);
+}
+
+CLOVER_ICD_API cl_int
+clGetPlatformInfo(cl_platform_id d_platform, cl_platform_info param,
+ size_t size, void *r_buf, size_t *r_size) {
+ return GetPlatformInfo(d_platform, param, size, r_buf, r_size);
+}
+
+CLOVER_ICD_API void *
+clGetExtensionFunctionAddress(const char *p_name) {
+ return GetExtensionFunctionAddress(p_name);
+}
+
+CLOVER_ICD_API void *
+clGetExtensionFunctionAddressForPlatform(cl_platform_id d_platform,
+ const char *p_name) {
+ return GetExtensionFunctionAddressForPlatform(d_platform, p_name);
+}
+
+CLOVER_ICD_API cl_int
+clIcdGetPlatformIDsKHR(cl_uint num_entries, cl_platform_id *rd_platforms,
+ cl_uint *rnum_platforms) {
+ return IcdGetPlatformIDsKHR(num_entries, rd_platforms, rnum_platforms);
+}
diff --git a/src/gallium/frontends/clover/api/program.cpp b/src/gallium/frontends/clover/api/program.cpp
new file mode 100644
index 00000000000..33f843e9c87
--- /dev/null
+++ b/src/gallium/frontends/clover/api/program.cpp
@@ -0,0 +1,479 @@
+//
+// Copyright 2012 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#include "api/util.hpp"
+#include "core/program.hpp"
+#include "util/u_debug.h"
+
+#include <sstream>
+
+using namespace clover;
+
+namespace {
+ void
+ validate_build_common(const program &prog, cl_uint num_devs,
+ const cl_device_id *d_devs,
+ void (*pfn_notify)(cl_program, void *),
+ void *user_data) {
+ if (!pfn_notify && user_data)
+ throw error(CL_INVALID_VALUE);
+
+ if (prog.kernel_ref_count())
+ throw error(CL_INVALID_OPERATION);
+
+ if (any_of([&](const device &dev) {
+ return !count(dev, prog.devices());
+ }, objs<allow_empty_tag>(d_devs, num_devs)))
+ throw error(CL_INVALID_DEVICE);
+ }
+}
+
+CLOVER_API cl_program
+clCreateProgramWithSource(cl_context d_ctx, cl_uint count,
+ const char **strings, const size_t *lengths,
+ cl_int *r_errcode) try {
+ auto &ctx = obj(d_ctx);
+ std::string source;
+
+ if (!count || !strings ||
+ any_of(is_zero(), range(strings, count)))
+ throw error(CL_INVALID_VALUE);
+
+ // Concatenate all the provided fragments together
+ for (unsigned i = 0; i < count; ++i)
+ source += (lengths && lengths[i] ?
+ std::string(strings[i], strings[i] + lengths[i]) :
+ std::string(strings[i]));
+
+ // ...and create a program object for them.
+ ret_error(r_errcode, CL_SUCCESS);
+ return new program(ctx, source);
+
+} catch (error &e) {
+ ret_error(r_errcode, e);
+ return NULL;
+}
+
+CLOVER_API cl_program
+clCreateProgramWithBinary(cl_context d_ctx, cl_uint n,
+ const cl_device_id *d_devs,
+ const size_t *lengths,
+ const unsigned char **binaries,
+ cl_int *r_status, cl_int *r_errcode) try {
+ auto &ctx = obj(d_ctx);
+ auto devs = objs(d_devs, n);
+
+ if (!lengths || !binaries)
+ throw error(CL_INVALID_VALUE);
+
+ if (any_of([&](const device &dev) {
+ return !count(dev, ctx.devices());
+ }, devs))
+ throw error(CL_INVALID_DEVICE);
+
+ // Deserialize the provided binaries,
+ std::vector<std::pair<cl_int, module>> result = map(
+ [](const unsigned char *p, size_t l) -> std::pair<cl_int, module> {
+ if (!p || !l)
+ return { CL_INVALID_VALUE, {} };
+
+ try {
+ std::stringbuf bin( { (char*)p, l } );
+ std::istream s(&bin);
+
+ return { CL_SUCCESS, module::deserialize(s) };
+
+ } catch (std::istream::failure &e) {
+ return { CL_INVALID_BINARY, {} };
+ }
+ },
+ range(binaries, n),
+ range(lengths, n));
+
+ // update the status array,
+ if (r_status)
+ copy(map(keys(), result), r_status);
+
+ if (any_of(key_equals(CL_INVALID_VALUE), result))
+ throw error(CL_INVALID_VALUE);
+
+ if (any_of(key_equals(CL_INVALID_BINARY), result))
+ throw error(CL_INVALID_BINARY);
+
+ // initialize a program object with them.
+ ret_error(r_errcode, CL_SUCCESS);
+ return new program(ctx, devs, map(values(), result));
+
+} catch (error &e) {
+ ret_error(r_errcode, e);
+ return NULL;
+}
+
+CLOVER_API cl_program
+clCreateProgramWithBuiltInKernels(cl_context d_ctx, cl_uint n,
+ const cl_device_id *d_devs,
+ const char *kernel_names,
+ cl_int *r_errcode) try {
+ auto &ctx = obj(d_ctx);
+ auto devs = objs(d_devs, n);
+
+ if (any_of([&](const device &dev) {
+ return !count(dev, ctx.devices());
+ }, devs))
+ throw error(CL_INVALID_DEVICE);
+
+ // No currently supported built-in kernels.
+ throw error(CL_INVALID_VALUE);
+
+} catch (error &e) {
+ ret_error(r_errcode, e);
+ return NULL;
+}
+
+
+CLOVER_API cl_int
+clRetainProgram(cl_program d_prog) try {
+ obj(d_prog).retain();
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clReleaseProgram(cl_program d_prog) try {
+ if (obj(d_prog).release())
+ delete pobj(d_prog);
+
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clBuildProgram(cl_program d_prog, cl_uint num_devs,
+ const cl_device_id *d_devs, const char *p_opts,
+ void (*pfn_notify)(cl_program, void *),
+ void *user_data) try {
+ auto &prog = obj(d_prog);
+ auto devs =
+ (d_devs ? objs(d_devs, num_devs) : ref_vector<device>(prog.devices()));
+ const auto opts = std::string(p_opts ? p_opts : "") + " " +
+ debug_get_option("CLOVER_EXTRA_BUILD_OPTIONS", "");
+
+ validate_build_common(prog, num_devs, d_devs, pfn_notify, user_data);
+
+ if (prog.has_source) {
+ prog.compile(devs, opts);
+ prog.link(devs, opts, { prog });
+ } else if (any_of([&](const device &dev){
+ return prog.build(dev).binary_type() != CL_PROGRAM_BINARY_TYPE_EXECUTABLE;
+ }, devs)) {
+ // According to the OpenCL 1.2 specification, “if program is created
+ // with clCreateProgramWithBinary, then the program binary must be an
+ // executable binary (not a compiled binary or library).”
+ throw error(CL_INVALID_BINARY);
+ }
+
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clCompileProgram(cl_program d_prog, cl_uint num_devs,
+ const cl_device_id *d_devs, const char *p_opts,
+ cl_uint num_headers, const cl_program *d_header_progs,
+ const char **header_names,
+ void (*pfn_notify)(cl_program, void *),
+ void *user_data) try {
+ auto &prog = obj(d_prog);
+ auto devs =
+ (d_devs ? objs(d_devs, num_devs) : ref_vector<device>(prog.devices()));
+ const auto opts = std::string(p_opts ? p_opts : "") + " " +
+ debug_get_option("CLOVER_EXTRA_COMPILE_OPTIONS", "");
+ header_map headers;
+
+ validate_build_common(prog, num_devs, d_devs, pfn_notify, user_data);
+
+ if (bool(num_headers) != bool(header_names))
+ throw error(CL_INVALID_VALUE);
+
+ if (!prog.has_source)
+ throw error(CL_INVALID_OPERATION);
+
+ for_each([&](const char *name, const program &header) {
+ if (!header.has_source)
+ throw error(CL_INVALID_OPERATION);
+
+ if (!any_of(key_equals(name), headers))
+ headers.push_back(std::pair<std::string, std::string>(
+ name, header.source()));
+ },
+ range(header_names, num_headers),
+ objs<allow_empty_tag>(d_header_progs, num_headers));
+
+ prog.compile(devs, opts, headers);
+ return CL_SUCCESS;
+
+} catch (invalid_build_options_error &e) {
+ return CL_INVALID_COMPILER_OPTIONS;
+
+} catch (build_error &e) {
+ return CL_COMPILE_PROGRAM_FAILURE;
+
+} catch (error &e) {
+ return e.get();
+}
+
+namespace {
+ ref_vector<device>
+ validate_link_devices(const ref_vector<program> &progs,
+ const ref_vector<device> &all_devs,
+ const std::string &opts) {
+ std::vector<device *> devs;
+ const bool create_library =
+ opts.find("-create-library") != std::string::npos;
+ const bool enable_link_options =
+ opts.find("-enable-link-options") != std::string::npos;
+ const bool has_link_options =
+ opts.find("-cl-denorms-are-zero") != std::string::npos ||
+ opts.find("-cl-no-signed-zeroes") != std::string::npos ||
+ opts.find("-cl-unsafe-math-optimizations") != std::string::npos ||
+ opts.find("-cl-finite-math-only") != std::string::npos ||
+ opts.find("-cl-fast-relaxed-math") != std::string::npos ||
+ opts.find("-cl-no-subgroup-ifp") != std::string::npos;
+
+ // According to the OpenCL 1.2 specification, "[the
+ // -enable-link-options] option must be specified with the
+ // create-library option".
+ if (enable_link_options && !create_library)
+ throw error(CL_INVALID_LINKER_OPTIONS);
+
+ // According to the OpenCL 1.2 specification, "the
+ // [program linking options] can be specified when linking a program
+ // executable".
+ if (has_link_options && create_library)
+ throw error(CL_INVALID_LINKER_OPTIONS);
+
+ for (auto &dev : all_devs) {
+ const auto has_binary = [&](const program &prog) {
+ const auto t = prog.build(dev).binary_type();
+ return t == CL_PROGRAM_BINARY_TYPE_COMPILED_OBJECT ||
+ t == CL_PROGRAM_BINARY_TYPE_LIBRARY;
+ };
+
+ // According to the OpenCL 1.2 specification, a library is made of
+ // “compiled binaries specified in input_programs argument to
+ // clLinkProgram“; compiled binaries does not refer to libraries:
+ // “input_programs is an array of program objects that are compiled
+ // binaries or libraries that are to be linked to create the program
+ // executable”.
+ if (create_library && any_of([&](const program &prog) {
+ const auto t = prog.build(dev).binary_type();
+ return t != CL_PROGRAM_BINARY_TYPE_COMPILED_OBJECT;
+ }, progs))
+ throw error(CL_INVALID_OPERATION);
+
+ // According to the CL 1.2 spec, when "all programs specified [..]
+ // contain a compiled binary or library for the device [..] a link is
+ // performed",
+ else if (all_of(has_binary, progs))
+ devs.push_back(&dev);
+
+ // otherwise if "none of the programs contain a compiled binary or
+ // library for that device [..] no link is performed. All other
+ // cases will return a CL_INVALID_OPERATION error."
+ else if (any_of(has_binary, progs))
+ throw error(CL_INVALID_OPERATION);
+
+ // According to the OpenCL 1.2 specification, "[t]he linker may apply
+ // [program linking options] to all compiled program objects
+ // specified to clLinkProgram. The linker may apply these options
+ // only to libraries which were created with the
+ // -enable-link-option."
+ else if (has_link_options && any_of([&](const program &prog) {
+ const auto t = prog.build(dev).binary_type();
+ return !(t == CL_PROGRAM_BINARY_TYPE_COMPILED_OBJECT ||
+ (t == CL_PROGRAM_BINARY_TYPE_LIBRARY &&
+ prog.build(dev).opts.find("-enable-link-options") !=
+ std::string::npos));
+ }, progs))
+ throw error(CL_INVALID_LINKER_OPTIONS);
+ }
+
+ return map(derefs(), devs);
+ }
+}
+
+CLOVER_API cl_program
+clLinkProgram(cl_context d_ctx, cl_uint num_devs, const cl_device_id *d_devs,
+ const char *p_opts, cl_uint num_progs, const cl_program *d_progs,
+ void (*pfn_notify) (cl_program, void *), void *user_data,
+ cl_int *r_errcode) try {
+ auto &ctx = obj(d_ctx);
+ const auto opts = std::string(p_opts ? p_opts : "") + " " +
+ debug_get_option("CLOVER_EXTRA_LINK_OPTIONS", "");
+ auto progs = objs(d_progs, num_progs);
+ auto all_devs =
+ (d_devs ? objs(d_devs, num_devs) : ref_vector<device>(ctx.devices()));
+ auto prog = create<program>(ctx, all_devs);
+ auto devs = validate_link_devices(progs, all_devs, opts);
+
+ validate_build_common(prog, num_devs, d_devs, pfn_notify, user_data);
+
+ try {
+ prog().link(devs, opts, progs);
+ ret_error(r_errcode, CL_SUCCESS);
+
+ } catch (build_error &e) {
+ ret_error(r_errcode, CL_LINK_PROGRAM_FAILURE);
+ }
+
+ return ret_object(prog);
+
+} catch (invalid_build_options_error &e) {
+ ret_error(r_errcode, CL_INVALID_LINKER_OPTIONS);
+ return NULL;
+
+} catch (error &e) {
+ ret_error(r_errcode, e);
+ return NULL;
+}
+
+CLOVER_API cl_int
+clUnloadCompiler() {
+ return CL_SUCCESS;
+}
+
+CLOVER_API cl_int
+clUnloadPlatformCompiler(cl_platform_id d_platform) {
+ return CL_SUCCESS;
+}
+
+CLOVER_API cl_int
+clGetProgramInfo(cl_program d_prog, cl_program_info param,
+ size_t size, void *r_buf, size_t *r_size) try {
+ property_buffer buf { r_buf, size, r_size };
+ auto &prog = obj(d_prog);
+
+ switch (param) {
+ case CL_PROGRAM_REFERENCE_COUNT:
+ buf.as_scalar<cl_uint>() = prog.ref_count();
+ break;
+
+ case CL_PROGRAM_CONTEXT:
+ buf.as_scalar<cl_context>() = desc(prog.context());
+ break;
+
+ case CL_PROGRAM_NUM_DEVICES:
+ buf.as_scalar<cl_uint>() = (prog.devices().size() ?
+ prog.devices().size() :
+ prog.context().devices().size());
+ break;
+
+ case CL_PROGRAM_DEVICES:
+ buf.as_vector<cl_device_id>() = (prog.devices().size() ?
+ descs(prog.devices()) :
+ descs(prog.context().devices()));
+ break;
+
+ case CL_PROGRAM_SOURCE:
+ buf.as_string() = prog.source();
+ break;
+
+ case CL_PROGRAM_BINARY_SIZES:
+ buf.as_vector<size_t>() = map([&](const device &dev) {
+ return prog.build(dev).binary.size();
+ },
+ prog.devices());
+ break;
+
+ case CL_PROGRAM_BINARIES:
+ buf.as_matrix<unsigned char>() = map([&](const device &dev) {
+ std::stringbuf bin;
+ std::ostream s(&bin);
+ prog.build(dev).binary.serialize(s);
+ return bin.str();
+ },
+ prog.devices());
+ break;
+
+ case CL_PROGRAM_NUM_KERNELS:
+ buf.as_scalar<cl_uint>() = prog.symbols().size();
+ break;
+
+ case CL_PROGRAM_KERNEL_NAMES:
+ buf.as_string() = fold([](const std::string &a, const module::symbol &s) {
+ return ((a.empty() ? "" : a + ";") + s.name);
+ }, std::string(), prog.symbols());
+ break;
+
+ default:
+ throw error(CL_INVALID_VALUE);
+ }
+
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clGetProgramBuildInfo(cl_program d_prog, cl_device_id d_dev,
+ cl_program_build_info param,
+ size_t size, void *r_buf, size_t *r_size) try {
+ property_buffer buf { r_buf, size, r_size };
+ auto &prog = obj(d_prog);
+ auto &dev = obj(d_dev);
+
+ if (!count(dev, prog.context().devices()))
+ return CL_INVALID_DEVICE;
+
+ switch (param) {
+ case CL_PROGRAM_BUILD_STATUS:
+ buf.as_scalar<cl_build_status>() = prog.build(dev).status();
+ break;
+
+ case CL_PROGRAM_BUILD_OPTIONS:
+ buf.as_string() = prog.build(dev).opts;
+ break;
+
+ case CL_PROGRAM_BUILD_LOG:
+ buf.as_string() = prog.build(dev).log;
+ break;
+
+ case CL_PROGRAM_BINARY_TYPE:
+ buf.as_scalar<cl_program_binary_type>() = prog.build(dev).binary_type();
+ break;
+
+ default:
+ throw error(CL_INVALID_VALUE);
+ }
+
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
diff --git a/src/gallium/frontends/clover/api/queue.cpp b/src/gallium/frontends/clover/api/queue.cpp
new file mode 100644
index 00000000000..65b271b216f
--- /dev/null
+++ b/src/gallium/frontends/clover/api/queue.cpp
@@ -0,0 +1,135 @@
+//
+// Copyright 2012 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#include "api/util.hpp"
+#include "core/queue.hpp"
+
+using namespace clover;
+
+CLOVER_API cl_command_queue
+clCreateCommandQueue(cl_context d_ctx, cl_device_id d_dev,
+ cl_command_queue_properties props,
+ cl_int *r_errcode) try {
+ auto &ctx = obj(d_ctx);
+ auto &dev = obj(d_dev);
+
+ if (!count(dev, ctx.devices()))
+ throw error(CL_INVALID_DEVICE);
+
+ if (props & ~(CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE |
+ CL_QUEUE_PROFILING_ENABLE))
+ throw error(CL_INVALID_VALUE);
+
+ ret_error(r_errcode, CL_SUCCESS);
+ return new command_queue(ctx, dev, props);
+
+} catch (error &e) {
+ ret_error(r_errcode, e);
+ return NULL;
+}
+
+CLOVER_API cl_int
+clRetainCommandQueue(cl_command_queue d_q) try {
+ obj(d_q).retain();
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clReleaseCommandQueue(cl_command_queue d_q) try {
+ auto &q = obj(d_q);
+
+ q.flush();
+
+ if (q.release())
+ delete pobj(d_q);
+
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clGetCommandQueueInfo(cl_command_queue d_q, cl_command_queue_info param,
+ size_t size, void *r_buf, size_t *r_size) try {
+ property_buffer buf { r_buf, size, r_size };
+ auto &q = obj(d_q);
+
+ switch (param) {
+ case CL_QUEUE_CONTEXT:
+ buf.as_scalar<cl_context>() = desc(q.context());
+ break;
+
+ case CL_QUEUE_DEVICE:
+ buf.as_scalar<cl_device_id>() = desc(q.device());
+ break;
+
+ case CL_QUEUE_REFERENCE_COUNT:
+ buf.as_scalar<cl_uint>() = q.ref_count();
+ break;
+
+ case CL_QUEUE_PROPERTIES:
+ buf.as_scalar<cl_command_queue_properties>() = q.properties();
+ break;
+
+ default:
+ throw error(CL_INVALID_VALUE);
+ }
+
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clFlush(cl_command_queue d_q) try {
+ obj(d_q).flush();
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_command_queue
+clCreateCommandQueueWithProperties(cl_context context, cl_device_id device,
+ const cl_queue_properties *properties,
+ cl_int *errcode_ret) try {
+ cl_command_queue_properties props = 0;
+ if (properties) {
+ for (auto idx = 0; properties[idx]; idx += 2) {
+ if (properties[idx] == CL_QUEUE_PROPERTIES)
+ props |= properties[idx + 1];
+ else
+ throw error(CL_INVALID_VALUE);
+ }
+ }
+
+ return clCreateCommandQueue(context, device, props, errcode_ret);
+
+} catch (error &e) {
+ ret_error(errcode_ret, e);
+ return NULL;
+}
diff --git a/src/gallium/frontends/clover/api/sampler.cpp b/src/gallium/frontends/clover/api/sampler.cpp
new file mode 100644
index 00000000000..482e55a9ce9
--- /dev/null
+++ b/src/gallium/frontends/clover/api/sampler.cpp
@@ -0,0 +1,100 @@
+//
+// Copyright 2012 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#include "api/util.hpp"
+#include "core/sampler.hpp"
+
+using namespace clover;
+
+CLOVER_API cl_sampler
+clCreateSampler(cl_context d_ctx, cl_bool norm_mode,
+ cl_addressing_mode addr_mode, cl_filter_mode filter_mode,
+ cl_int *r_errcode) try {
+ auto &ctx = obj(d_ctx);
+
+ if (!any_of(std::mem_fn(&device::image_support), ctx.devices()))
+ throw error(CL_INVALID_OPERATION);
+
+ ret_error(r_errcode, CL_SUCCESS);
+ return new sampler(ctx, norm_mode, addr_mode, filter_mode);
+
+} catch (error &e) {
+ ret_error(r_errcode, e);
+ return NULL;
+}
+
+CLOVER_API cl_int
+clRetainSampler(cl_sampler d_s) try {
+ obj(d_s).retain();
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clReleaseSampler(cl_sampler d_s) try {
+ if (obj(d_s).release())
+ delete pobj(d_s);
+
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clGetSamplerInfo(cl_sampler d_s, cl_sampler_info param,
+ size_t size, void *r_buf, size_t *r_size) try {
+ property_buffer buf { r_buf, size, r_size };
+ auto &s = obj(d_s);
+
+ switch (param) {
+ case CL_SAMPLER_REFERENCE_COUNT:
+ buf.as_scalar<cl_uint>() = s.ref_count();
+ break;
+
+ case CL_SAMPLER_CONTEXT:
+ buf.as_scalar<cl_context>() = desc(s.context());
+ break;
+
+ case CL_SAMPLER_NORMALIZED_COORDS:
+ buf.as_scalar<cl_bool>() = s.norm_mode();
+ break;
+
+ case CL_SAMPLER_ADDRESSING_MODE:
+ buf.as_scalar<cl_addressing_mode>() = s.addr_mode();
+ break;
+
+ case CL_SAMPLER_FILTER_MODE:
+ buf.as_scalar<cl_filter_mode>() = s.filter_mode();
+ break;
+
+ default:
+ throw error(CL_INVALID_VALUE);
+ }
+
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
diff --git a/src/gallium/frontends/clover/api/transfer.cpp b/src/gallium/frontends/clover/api/transfer.cpp
new file mode 100644
index 00000000000..fa8741e02b4
--- /dev/null
+++ b/src/gallium/frontends/clover/api/transfer.cpp
@@ -0,0 +1,1059 @@
+//
+// Copyright 2012 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#include <cstring>
+
+#include "util/bitscan.h"
+
+#include "api/dispatch.hpp"
+#include "api/util.hpp"
+#include "core/event.hpp"
+#include "core/memory.hpp"
+
+using namespace clover;
+
+namespace {
+ typedef resource::vector vector_t;
+
+ vector_t
+ vector(const size_t *p) {
+ return range(p, 3);
+ }
+
+ vector_t
+ pitch(const vector_t &region, vector_t pitch) {
+ for (auto x : zip(tail(pitch),
+ map(multiplies(), region, pitch))) {
+ // The spec defines a value of zero as the natural pitch,
+ // i.e. the unaligned size of the previous dimension.
+ if (std::get<0>(x) == 0)
+ std::get<0>(x) = std::get<1>(x);
+ }
+
+ return pitch;
+ }
+
+ ///
+ /// Size of a region in bytes.
+ ///
+ size_t
+ size(const vector_t &pitch, const vector_t &region) {
+ if (any_of(is_zero(), region))
+ return 0;
+ else
+ return dot(pitch, region - vector_t{ 0, 1, 1 });
+ }
+
+ ///
+ /// Common argument checking shared by memory transfer commands.
+ ///
+ void
+ validate_common(command_queue &q,
+ const ref_vector<event> &deps) {
+ if (any_of([&](const event &ev) {
+ return ev.context() != q.context();
+ }, deps))
+ throw error(CL_INVALID_CONTEXT);
+ }
+
+ ///
+ /// Common error checking for a buffer object argument.
+ ///
+ void
+ validate_object(command_queue &q, buffer &mem, const vector_t &origin,
+ const vector_t &pitch, const vector_t &region) {
+ if (mem.context() != q.context())
+ throw error(CL_INVALID_CONTEXT);
+
+ // The region must fit within the specified pitch,
+ if (any_of(greater(), map(multiplies(), pitch, region), tail(pitch)))
+ throw error(CL_INVALID_VALUE);
+
+ // ...and within the specified object.
+ if (dot(pitch, origin) + size(pitch, region) > mem.size())
+ throw error(CL_INVALID_VALUE);
+
+ if (any_of(is_zero(), region))
+ throw error(CL_INVALID_VALUE);
+ }
+
+ ///
+ /// Common error checking for an image argument.
+ ///
+ void
+ validate_object(command_queue &q, image &img,
+ const vector_t &orig, const vector_t &region) {
+ vector_t size = { img.width(), img.height(), img.depth() };
+
+ if (!q.device().image_support())
+ throw error(CL_INVALID_OPERATION);
+
+ if (img.context() != q.context())
+ throw error(CL_INVALID_CONTEXT);
+
+ if (any_of(greater(), orig + region, size))
+ throw error(CL_INVALID_VALUE);
+
+ if (any_of(is_zero(), region))
+ throw error(CL_INVALID_VALUE);
+ }
+
+ ///
+ /// Common error checking for a host pointer argument.
+ ///
+ void
+ validate_object(command_queue &q, const void *ptr, const vector_t &orig,
+ const vector_t &pitch, const vector_t &region) {
+ if (!ptr)
+ throw error(CL_INVALID_VALUE);
+
+ // The region must fit within the specified pitch.
+ if (any_of(greater(), map(multiplies(), pitch, region), tail(pitch)))
+ throw error(CL_INVALID_VALUE);
+ }
+
+ ///
+ /// Common argument checking for a copy between two buffer objects.
+ ///
+ void
+ validate_copy(command_queue &q, buffer &dst_mem,
+ const vector_t &dst_orig, const vector_t &dst_pitch,
+ buffer &src_mem,
+ const vector_t &src_orig, const vector_t &src_pitch,
+ const vector_t &region) {
+ if (dst_mem == src_mem) {
+ auto dst_offset = dot(dst_pitch, dst_orig);
+ auto src_offset = dot(src_pitch, src_orig);
+
+ if (interval_overlaps()(
+ dst_offset, dst_offset + size(dst_pitch, region),
+ src_offset, src_offset + size(src_pitch, region)))
+ throw error(CL_MEM_COPY_OVERLAP);
+ }
+ }
+
+ ///
+ /// Common argument checking for a copy between two image objects.
+ ///
+ void
+ validate_copy(command_queue &q,
+ image &dst_img, const vector_t &dst_orig,
+ image &src_img, const vector_t &src_orig,
+ const vector_t &region) {
+ if (dst_img.format() != src_img.format())
+ throw error(CL_IMAGE_FORMAT_MISMATCH);
+
+ if (dst_img == src_img) {
+ if (all_of(interval_overlaps(),
+ dst_orig, dst_orig + region,
+ src_orig, src_orig + region))
+ throw error(CL_MEM_COPY_OVERLAP);
+ }
+ }
+
+ ///
+ /// Checks that the host access flags of the memory object are
+ /// within the allowed set \a flags.
+ ///
+ void
+ validate_object_access(const memory_obj &mem, const cl_mem_flags flags) {
+ if (mem.flags() & ~flags &
+ (CL_MEM_HOST_READ_ONLY | CL_MEM_HOST_WRITE_ONLY |
+ CL_MEM_HOST_NO_ACCESS))
+ throw error(CL_INVALID_OPERATION);
+ }
+
+ ///
+ /// Checks that the mapping flags are correct.
+ ///
+ void
+ validate_map_flags(const memory_obj &mem, const cl_map_flags flags) {
+ if ((flags & (CL_MAP_WRITE | CL_MAP_READ)) &&
+ (flags & CL_MAP_WRITE_INVALIDATE_REGION))
+ throw error(CL_INVALID_VALUE);
+
+ if (flags & CL_MAP_READ)
+ validate_object_access(mem, CL_MEM_HOST_READ_ONLY);
+
+ if (flags & (CL_MAP_WRITE | CL_MAP_WRITE_INVALIDATE_REGION))
+ validate_object_access(mem, CL_MEM_HOST_WRITE_ONLY);
+ }
+
+ ///
+ /// Class that encapsulates the task of mapping an object of type
+ /// \a T. The return value of get() should be implicitly
+ /// convertible to \a void *.
+ ///
+ template<typename T>
+ struct _map {
+ static mapping
+ get(command_queue &q, T obj, cl_map_flags flags,
+ size_t offset, size_t size) {
+ return { q, obj->resource(q), flags, true,
+ {{ offset }}, {{ size, 1, 1 }} };
+ }
+ };
+
+ template<>
+ struct _map<void *> {
+ static void *
+ get(command_queue &q, void *obj, cl_map_flags flags,
+ size_t offset, size_t size) {
+ return (char *)obj + offset;
+ }
+ };
+
+ template<>
+ struct _map<const void *> {
+ static const void *
+ get(command_queue &q, const void *obj, cl_map_flags flags,
+ size_t offset, size_t size) {
+ return (const char *)obj + offset;
+ }
+ };
+
+ ///
+ /// Software copy from \a src_obj to \a dst_obj. They can be
+ /// either pointers or memory objects.
+ ///
+ template<typename T, typename S>
+ std::function<void (event &)>
+ soft_copy_op(command_queue &q,
+ T dst_obj, const vector_t &dst_orig, const vector_t &dst_pitch,
+ S src_obj, const vector_t &src_orig, const vector_t &src_pitch,
+ const vector_t &region) {
+ return [=, &q](event &) {
+ auto dst = _map<T>::get(q, dst_obj, CL_MAP_WRITE,
+ dot(dst_pitch, dst_orig),
+ size(dst_pitch, region));
+ auto src = _map<S>::get(q, src_obj, CL_MAP_READ,
+ dot(src_pitch, src_orig),
+ size(src_pitch, region));
+ vector_t v = {};
+
+ for (v[2] = 0; v[2] < region[2]; ++v[2]) {
+ for (v[1] = 0; v[1] < region[1]; ++v[1]) {
+ std::memcpy(
+ static_cast<char *>(dst) + dot(dst_pitch, v),
+ static_cast<const char *>(src) + dot(src_pitch, v),
+ src_pitch[0] * region[0]);
+ }
+ }
+ };
+ }
+
+ ///
+ /// Hardware copy from \a src_obj to \a dst_obj.
+ ///
+ template<typename T, typename S>
+ std::function<void (event &)>
+ hard_copy_op(command_queue &q, T dst_obj, const vector_t &dst_orig,
+ S src_obj, const vector_t &src_orig, const vector_t &region) {
+ return [=, &q](event &) {
+ dst_obj->resource(q).copy(q, dst_orig, region,
+ src_obj->resource(q), src_orig);
+ };
+ }
+}
+
+CLOVER_API cl_int
+clEnqueueReadBuffer(cl_command_queue d_q, cl_mem d_mem, cl_bool blocking,
+ size_t offset, size_t size, void *ptr,
+ cl_uint num_deps, const cl_event *d_deps,
+ cl_event *rd_ev) try {
+ auto &q = obj(d_q);
+ auto &mem = obj<buffer>(d_mem);
+ auto deps = objs<wait_list_tag>(d_deps, num_deps);
+ vector_t region = { size, 1, 1 };
+ vector_t obj_origin = { offset };
+ auto obj_pitch = pitch(region, {{ 1 }});
+
+ validate_common(q, deps);
+ validate_object(q, ptr, {}, obj_pitch, region);
+ validate_object(q, mem, obj_origin, obj_pitch, region);
+ validate_object_access(mem, CL_MEM_HOST_READ_ONLY);
+
+ auto hev = create<hard_event>(
+ q, CL_COMMAND_READ_BUFFER, deps,
+ soft_copy_op(q, ptr, {}, obj_pitch,
+ &mem, obj_origin, obj_pitch,
+ region));
+
+ if (blocking)
+ hev().wait_signalled();
+
+ ret_object(rd_ev, hev);
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clEnqueueWriteBuffer(cl_command_queue d_q, cl_mem d_mem, cl_bool blocking,
+ size_t offset, size_t size, const void *ptr,
+ cl_uint num_deps, const cl_event *d_deps,
+ cl_event *rd_ev) try {
+ auto &q = obj(d_q);
+ auto &mem = obj<buffer>(d_mem);
+ auto deps = objs<wait_list_tag>(d_deps, num_deps);
+ vector_t region = { size, 1, 1 };
+ vector_t obj_origin = { offset };
+ auto obj_pitch = pitch(region, {{ 1 }});
+
+ validate_common(q, deps);
+ validate_object(q, mem, obj_origin, obj_pitch, region);
+ validate_object(q, ptr, {}, obj_pitch, region);
+ validate_object_access(mem, CL_MEM_HOST_WRITE_ONLY);
+
+ auto hev = create<hard_event>(
+ q, CL_COMMAND_WRITE_BUFFER, deps,
+ soft_copy_op(q, &mem, obj_origin, obj_pitch,
+ ptr, {}, obj_pitch,
+ region));
+
+ if (blocking)
+ hev().wait_signalled();
+
+ ret_object(rd_ev, hev);
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clEnqueueReadBufferRect(cl_command_queue d_q, cl_mem d_mem, cl_bool blocking,
+ const size_t *p_obj_origin,
+ const size_t *p_host_origin,
+ const size_t *p_region,
+ size_t obj_row_pitch, size_t obj_slice_pitch,
+ size_t host_row_pitch, size_t host_slice_pitch,
+ void *ptr,
+ cl_uint num_deps, const cl_event *d_deps,
+ cl_event *rd_ev) try {
+ auto &q = obj(d_q);
+ auto &mem = obj<buffer>(d_mem);
+ auto deps = objs<wait_list_tag>(d_deps, num_deps);
+ auto region = vector(p_region);
+ auto obj_origin = vector(p_obj_origin);
+ auto obj_pitch = pitch(region, {{ 1, obj_row_pitch, obj_slice_pitch }});
+ auto host_origin = vector(p_host_origin);
+ auto host_pitch = pitch(region, {{ 1, host_row_pitch, host_slice_pitch }});
+
+ validate_common(q, deps);
+ validate_object(q, ptr, host_origin, host_pitch, region);
+ validate_object(q, mem, obj_origin, obj_pitch, region);
+ validate_object_access(mem, CL_MEM_HOST_READ_ONLY);
+
+ auto hev = create<hard_event>(
+ q, CL_COMMAND_READ_BUFFER_RECT, deps,
+ soft_copy_op(q, ptr, host_origin, host_pitch,
+ &mem, obj_origin, obj_pitch,
+ region));
+
+ if (blocking)
+ hev().wait_signalled();
+
+ ret_object(rd_ev, hev);
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clEnqueueWriteBufferRect(cl_command_queue d_q, cl_mem d_mem, cl_bool blocking,
+ const size_t *p_obj_origin,
+ const size_t *p_host_origin,
+ const size_t *p_region,
+ size_t obj_row_pitch, size_t obj_slice_pitch,
+ size_t host_row_pitch, size_t host_slice_pitch,
+ const void *ptr,
+ cl_uint num_deps, const cl_event *d_deps,
+ cl_event *rd_ev) try {
+ auto &q = obj(d_q);
+ auto &mem = obj<buffer>(d_mem);
+ auto deps = objs<wait_list_tag>(d_deps, num_deps);
+ auto region = vector(p_region);
+ auto obj_origin = vector(p_obj_origin);
+ auto obj_pitch = pitch(region, {{ 1, obj_row_pitch, obj_slice_pitch }});
+ auto host_origin = vector(p_host_origin);
+ auto host_pitch = pitch(region, {{ 1, host_row_pitch, host_slice_pitch }});
+
+ validate_common(q, deps);
+ validate_object(q, mem, obj_origin, obj_pitch, region);
+ validate_object(q, ptr, host_origin, host_pitch, region);
+ validate_object_access(mem, CL_MEM_HOST_WRITE_ONLY);
+
+ auto hev = create<hard_event>(
+ q, CL_COMMAND_WRITE_BUFFER_RECT, deps,
+ soft_copy_op(q, &mem, obj_origin, obj_pitch,
+ ptr, host_origin, host_pitch,
+ region));
+
+ if (blocking)
+ hev().wait_signalled();
+
+ ret_object(rd_ev, hev);
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clEnqueueCopyBuffer(cl_command_queue d_q, cl_mem d_src_mem, cl_mem d_dst_mem,
+ size_t src_offset, size_t dst_offset, size_t size,
+ cl_uint num_deps, const cl_event *d_deps,
+ cl_event *rd_ev) try {
+ auto &q = obj(d_q);
+ auto &src_mem = obj<buffer>(d_src_mem);
+ auto &dst_mem = obj<buffer>(d_dst_mem);
+ auto deps = objs<wait_list_tag>(d_deps, num_deps);
+ vector_t region = { size, 1, 1 };
+ vector_t dst_origin = { dst_offset };
+ auto dst_pitch = pitch(region, {{ 1 }});
+ vector_t src_origin = { src_offset };
+ auto src_pitch = pitch(region, {{ 1 }});
+
+ validate_common(q, deps);
+ validate_object(q, dst_mem, dst_origin, dst_pitch, region);
+ validate_object(q, src_mem, src_origin, src_pitch, region);
+ validate_copy(q, dst_mem, dst_origin, dst_pitch,
+ src_mem, src_origin, src_pitch, region);
+
+ auto hev = create<hard_event>(
+ q, CL_COMMAND_COPY_BUFFER, deps,
+ hard_copy_op(q, &dst_mem, dst_origin,
+ &src_mem, src_origin, region));
+
+ ret_object(rd_ev, hev);
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clEnqueueCopyBufferRect(cl_command_queue d_q, cl_mem d_src_mem,
+ cl_mem d_dst_mem,
+ const size_t *p_src_origin, const size_t *p_dst_origin,
+ const size_t *p_region,
+ size_t src_row_pitch, size_t src_slice_pitch,
+ size_t dst_row_pitch, size_t dst_slice_pitch,
+ cl_uint num_deps, const cl_event *d_deps,
+ cl_event *rd_ev) try {
+ auto &q = obj(d_q);
+ auto &src_mem = obj<buffer>(d_src_mem);
+ auto &dst_mem = obj<buffer>(d_dst_mem);
+ auto deps = objs<wait_list_tag>(d_deps, num_deps);
+ auto region = vector(p_region);
+ auto dst_origin = vector(p_dst_origin);
+ auto dst_pitch = pitch(region, {{ 1, dst_row_pitch, dst_slice_pitch }});
+ auto src_origin = vector(p_src_origin);
+ auto src_pitch = pitch(region, {{ 1, src_row_pitch, src_slice_pitch }});
+
+ validate_common(q, deps);
+ validate_object(q, dst_mem, dst_origin, dst_pitch, region);
+ validate_object(q, src_mem, src_origin, src_pitch, region);
+ validate_copy(q, dst_mem, dst_origin, dst_pitch,
+ src_mem, src_origin, src_pitch, region);
+
+ auto hev = create<hard_event>(
+ q, CL_COMMAND_COPY_BUFFER_RECT, deps,
+ soft_copy_op(q, &dst_mem, dst_origin, dst_pitch,
+ &src_mem, src_origin, src_pitch,
+ region));
+
+ ret_object(rd_ev, hev);
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clEnqueueReadImage(cl_command_queue d_q, cl_mem d_mem, cl_bool blocking,
+ const size_t *p_origin, const size_t *p_region,
+ size_t row_pitch, size_t slice_pitch, void *ptr,
+ cl_uint num_deps, const cl_event *d_deps,
+ cl_event *rd_ev) try {
+ auto &q = obj(d_q);
+ auto &img = obj<image>(d_mem);
+ auto deps = objs<wait_list_tag>(d_deps, num_deps);
+ auto region = vector(p_region);
+ auto dst_pitch = pitch(region, {{ img.pixel_size(),
+ row_pitch, slice_pitch }});
+ auto src_origin = vector(p_origin);
+ auto src_pitch = pitch(region, {{ img.pixel_size(),
+ img.row_pitch(), img.slice_pitch() }});
+
+ validate_common(q, deps);
+ validate_object(q, ptr, {}, dst_pitch, region);
+ validate_object(q, img, src_origin, region);
+ validate_object_access(img, CL_MEM_HOST_READ_ONLY);
+
+ auto hev = create<hard_event>(
+ q, CL_COMMAND_READ_IMAGE, deps,
+ soft_copy_op(q, ptr, {}, dst_pitch,
+ &img, src_origin, src_pitch,
+ region));
+
+ if (blocking)
+ hev().wait_signalled();
+
+ ret_object(rd_ev, hev);
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clEnqueueWriteImage(cl_command_queue d_q, cl_mem d_mem, cl_bool blocking,
+ const size_t *p_origin, const size_t *p_region,
+ size_t row_pitch, size_t slice_pitch, const void *ptr,
+ cl_uint num_deps, const cl_event *d_deps,
+ cl_event *rd_ev) try {
+ auto &q = obj(d_q);
+ auto &img = obj<image>(d_mem);
+ auto deps = objs<wait_list_tag>(d_deps, num_deps);
+ auto region = vector(p_region);
+ auto dst_origin = vector(p_origin);
+ auto dst_pitch = pitch(region, {{ img.pixel_size(),
+ img.row_pitch(), img.slice_pitch() }});
+ auto src_pitch = pitch(region, {{ img.pixel_size(),
+ row_pitch, slice_pitch }});
+
+ validate_common(q, deps);
+ validate_object(q, img, dst_origin, region);
+ validate_object(q, ptr, {}, src_pitch, region);
+ validate_object_access(img, CL_MEM_HOST_WRITE_ONLY);
+
+ auto hev = create<hard_event>(
+ q, CL_COMMAND_WRITE_IMAGE, deps,
+ soft_copy_op(q, &img, dst_origin, dst_pitch,
+ ptr, {}, src_pitch,
+ region));
+
+ if (blocking)
+ hev().wait_signalled();
+
+ ret_object(rd_ev, hev);
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clEnqueueCopyImage(cl_command_queue d_q, cl_mem d_src_mem, cl_mem d_dst_mem,
+ const size_t *p_src_origin, const size_t *p_dst_origin,
+ const size_t *p_region,
+ cl_uint num_deps, const cl_event *d_deps,
+ cl_event *rd_ev) try {
+ auto &q = obj(d_q);
+ auto &src_img = obj<image>(d_src_mem);
+ auto &dst_img = obj<image>(d_dst_mem);
+ auto deps = objs<wait_list_tag>(d_deps, num_deps);
+ auto region = vector(p_region);
+ auto dst_origin = vector(p_dst_origin);
+ auto src_origin = vector(p_src_origin);
+
+ validate_common(q, deps);
+ validate_object(q, dst_img, dst_origin, region);
+ validate_object(q, src_img, src_origin, region);
+ validate_copy(q, dst_img, dst_origin, src_img, src_origin, region);
+
+ auto hev = create<hard_event>(
+ q, CL_COMMAND_COPY_IMAGE, deps,
+ hard_copy_op(q, &dst_img, dst_origin,
+ &src_img, src_origin,
+ region));
+
+ ret_object(rd_ev, hev);
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clEnqueueCopyImageToBuffer(cl_command_queue d_q,
+ cl_mem d_src_mem, cl_mem d_dst_mem,
+ const size_t *p_src_origin, const size_t *p_region,
+ size_t dst_offset,
+ cl_uint num_deps, const cl_event *d_deps,
+ cl_event *rd_ev) try {
+ auto &q = obj(d_q);
+ auto &src_img = obj<image>(d_src_mem);
+ auto &dst_mem = obj<buffer>(d_dst_mem);
+ auto deps = objs<wait_list_tag>(d_deps, num_deps);
+ auto region = vector(p_region);
+ vector_t dst_origin = { dst_offset };
+ auto dst_pitch = pitch(region, {{ src_img.pixel_size() }});
+ auto src_origin = vector(p_src_origin);
+ auto src_pitch = pitch(region, {{ src_img.pixel_size(),
+ src_img.row_pitch(),
+ src_img.slice_pitch() }});
+
+ validate_common(q, deps);
+ validate_object(q, dst_mem, dst_origin, dst_pitch, region);
+ validate_object(q, src_img, src_origin, region);
+
+ auto hev = create<hard_event>(
+ q, CL_COMMAND_COPY_IMAGE_TO_BUFFER, deps,
+ soft_copy_op(q, &dst_mem, dst_origin, dst_pitch,
+ &src_img, src_origin, src_pitch,
+ region));
+
+ ret_object(rd_ev, hev);
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clEnqueueCopyBufferToImage(cl_command_queue d_q,
+ cl_mem d_src_mem, cl_mem d_dst_mem,
+ size_t src_offset,
+ const size_t *p_dst_origin, const size_t *p_region,
+ cl_uint num_deps, const cl_event *d_deps,
+ cl_event *rd_ev) try {
+ auto &q = obj(d_q);
+ auto &src_mem = obj<buffer>(d_src_mem);
+ auto &dst_img = obj<image>(d_dst_mem);
+ auto deps = objs<wait_list_tag>(d_deps, num_deps);
+ auto region = vector(p_region);
+ auto dst_origin = vector(p_dst_origin);
+ auto dst_pitch = pitch(region, {{ dst_img.pixel_size(),
+ dst_img.row_pitch(),
+ dst_img.slice_pitch() }});
+ vector_t src_origin = { src_offset };
+ auto src_pitch = pitch(region, {{ dst_img.pixel_size() }});
+
+ validate_common(q, deps);
+ validate_object(q, dst_img, dst_origin, region);
+ validate_object(q, src_mem, src_origin, src_pitch, region);
+
+ auto hev = create<hard_event>(
+ q, CL_COMMAND_COPY_BUFFER_TO_IMAGE, deps,
+ soft_copy_op(q, &dst_img, dst_origin, dst_pitch,
+ &src_mem, src_origin, src_pitch,
+ region));
+
+ ret_object(rd_ev, hev);
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API void *
+clEnqueueMapBuffer(cl_command_queue d_q, cl_mem d_mem, cl_bool blocking,
+ cl_map_flags flags, size_t offset, size_t size,
+ cl_uint num_deps, const cl_event *d_deps,
+ cl_event *rd_ev, cl_int *r_errcode) try {
+ auto &q = obj(d_q);
+ auto &mem = obj<buffer>(d_mem);
+ auto deps = objs<wait_list_tag>(d_deps, num_deps);
+ vector_t region = { size, 1, 1 };
+ vector_t obj_origin = { offset };
+ auto obj_pitch = pitch(region, {{ 1 }});
+
+ validate_common(q, deps);
+ validate_object(q, mem, obj_origin, obj_pitch, region);
+ validate_map_flags(mem, flags);
+
+ void *map = mem.resource(q).add_map(q, flags, blocking, obj_origin, region);
+
+ auto hev = create<hard_event>(q, CL_COMMAND_MAP_BUFFER, deps);
+ if (blocking)
+ hev().wait_signalled();
+
+ ret_object(rd_ev, hev);
+ ret_error(r_errcode, CL_SUCCESS);
+ return map;
+
+} catch (error &e) {
+ ret_error(r_errcode, e);
+ return NULL;
+}
+
+CLOVER_API void *
+clEnqueueMapImage(cl_command_queue d_q, cl_mem d_mem, cl_bool blocking,
+ cl_map_flags flags,
+ const size_t *p_origin, const size_t *p_region,
+ size_t *row_pitch, size_t *slice_pitch,
+ cl_uint num_deps, const cl_event *d_deps,
+ cl_event *rd_ev, cl_int *r_errcode) try {
+ auto &q = obj(d_q);
+ auto &img = obj<image>(d_mem);
+ auto deps = objs<wait_list_tag>(d_deps, num_deps);
+ auto region = vector(p_region);
+ auto origin = vector(p_origin);
+
+ validate_common(q, deps);
+ validate_object(q, img, origin, region);
+ validate_map_flags(img, flags);
+
+ void *map = img.resource(q).add_map(q, flags, blocking, origin, region);
+
+ auto hev = create<hard_event>(q, CL_COMMAND_MAP_IMAGE, deps);
+ if (blocking)
+ hev().wait_signalled();
+
+ ret_object(rd_ev, hev);
+ ret_error(r_errcode, CL_SUCCESS);
+ return map;
+
+} catch (error &e) {
+ ret_error(r_errcode, e);
+ return NULL;
+}
+
+CLOVER_API cl_int
+clEnqueueUnmapMemObject(cl_command_queue d_q, cl_mem d_mem, void *ptr,
+ cl_uint num_deps, const cl_event *d_deps,
+ cl_event *rd_ev) try {
+ auto &q = obj(d_q);
+ auto &mem = obj(d_mem);
+ auto deps = objs<wait_list_tag>(d_deps, num_deps);
+
+ validate_common(q, deps);
+
+ auto hev = create<hard_event>(
+ q, CL_COMMAND_UNMAP_MEM_OBJECT, deps,
+ [=, &q, &mem](event &) {
+ mem.resource(q).del_map(ptr);
+ });
+
+ ret_object(rd_ev, hev);
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clEnqueueMigrateMemObjects(cl_command_queue command_queue,
+ cl_uint num_mem_objects,
+ const cl_mem *mem_objects,
+ cl_mem_migration_flags flags,
+ cl_uint num_events_in_wait_list,
+ const cl_event *event_wait_list,
+ cl_event *event) {
+ CLOVER_NOT_SUPPORTED_UNTIL("1.2");
+ return CL_INVALID_VALUE;
+}
+
+cl_int
+clover::EnqueueSVMFree(cl_command_queue d_q,
+ cl_uint num_svm_pointers,
+ void *svm_pointers[],
+ void (CL_CALLBACK *pfn_free_func) (
+ cl_command_queue queue, cl_uint num_svm_pointers,
+ void *svm_pointers[], void *user_data),
+ void *user_data,
+ cl_uint num_events_in_wait_list,
+ const cl_event *event_wait_list,
+ cl_event *event,
+ cl_int cmd) try {
+
+ if (bool(num_svm_pointers) != bool(svm_pointers))
+ return CL_INVALID_VALUE;
+
+ auto &q = obj(d_q);
+ bool can_emulate = q.device().has_system_svm();
+ auto deps = objs<wait_list_tag>(event_wait_list, num_events_in_wait_list);
+
+ validate_common(q, deps);
+
+ std::vector<void *> svm_pointers_cpy(svm_pointers,
+ svm_pointers + num_svm_pointers);
+ if (!pfn_free_func) {
+ if (!can_emulate) {
+ CLOVER_NOT_SUPPORTED_UNTIL("2.0");
+ return CL_INVALID_VALUE;
+ }
+ pfn_free_func = [](cl_command_queue, cl_uint num_svm_pointers,
+ void *svm_pointers[], void *) {
+ for (void *p : range(svm_pointers, num_svm_pointers))
+ free(p);
+ };
+ }
+
+ auto hev = create<hard_event>(q, cmd, deps,
+ [=](clover::event &) mutable {
+ pfn_free_func(d_q, num_svm_pointers, svm_pointers_cpy.data(),
+ user_data);
+ });
+
+ ret_object(event, hev);
+ return CL_SUCCESS;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clEnqueueSVMFree(cl_command_queue d_q,
+ cl_uint num_svm_pointers,
+ void *svm_pointers[],
+ void (CL_CALLBACK *pfn_free_func) (
+ cl_command_queue queue, cl_uint num_svm_pointers,
+ void *svm_pointers[], void *user_data),
+ void *user_data,
+ cl_uint num_events_in_wait_list,
+ const cl_event *event_wait_list,
+ cl_event *event) {
+
+ return EnqueueSVMFree(d_q, num_svm_pointers, svm_pointers,
+ pfn_free_func, user_data, num_events_in_wait_list,
+ event_wait_list, event, CL_COMMAND_SVM_FREE);
+}
+
+cl_int
+clover::EnqueueSVMMemcpy(cl_command_queue d_q,
+ cl_bool blocking_copy,
+ void *dst_ptr,
+ const void *src_ptr,
+ size_t size,
+ cl_uint num_events_in_wait_list,
+ const cl_event *event_wait_list,
+ cl_event *event,
+ cl_int cmd) try {
+
+ if (dst_ptr == nullptr || src_ptr == nullptr)
+ return CL_INVALID_VALUE;
+
+ if (static_cast<size_t>(abs(reinterpret_cast<ptrdiff_t>(dst_ptr) -
+ reinterpret_cast<ptrdiff_t>(src_ptr))) < size)
+ return CL_MEM_COPY_OVERLAP;
+
+ auto &q = obj(d_q);
+ bool can_emulate = q.device().has_system_svm();
+ auto deps = objs<wait_list_tag>(event_wait_list, num_events_in_wait_list);
+
+ validate_common(q, deps);
+
+ if (can_emulate) {
+ auto hev = create<hard_event>(q, cmd, deps,
+ [=](clover::event &) {
+ memcpy(dst_ptr, src_ptr, size);
+ });
+
+ if (blocking_copy)
+ hev().wait();
+ ret_object(event, hev);
+ return CL_SUCCESS;
+ }
+
+ CLOVER_NOT_SUPPORTED_UNTIL("2.0");
+ return CL_INVALID_VALUE;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clEnqueueSVMMemcpy(cl_command_queue d_q,
+ cl_bool blocking_copy,
+ void *dst_ptr,
+ const void *src_ptr,
+ size_t size,
+ cl_uint num_events_in_wait_list,
+ const cl_event *event_wait_list,
+ cl_event *event) {
+
+ return EnqueueSVMMemcpy(d_q, blocking_copy, dst_ptr, src_ptr,
+ size, num_events_in_wait_list, event_wait_list,
+ event, CL_COMMAND_SVM_MEMCPY);
+}
+
+cl_int
+clover::EnqueueSVMMemFill(cl_command_queue d_q,
+ void *svm_ptr,
+ const void *pattern,
+ size_t pattern_size,
+ size_t size,
+ cl_uint num_events_in_wait_list,
+ const cl_event *event_wait_list,
+ cl_event *event,
+ cl_int cmd) try {
+
+ if (svm_ptr == nullptr || pattern == nullptr ||
+ !util_is_power_of_two_nonzero(pattern_size) ||
+ pattern_size > 128 ||
+ !ptr_is_aligned(svm_ptr, pattern_size) ||
+ size % pattern_size)
+ return CL_INVALID_VALUE;
+
+ auto &q = obj(d_q);
+ bool can_emulate = q.device().has_system_svm();
+ auto deps = objs<wait_list_tag>(event_wait_list, num_events_in_wait_list);
+
+ validate_common(q, deps);
+
+ if (can_emulate) {
+ auto hev = create<hard_event>(q, cmd, deps,
+ [=](clover::event &) {
+ void *ptr = svm_ptr;
+ for (size_t s = size; s; s -= pattern_size) {
+ memcpy(ptr, pattern, pattern_size);
+ ptr = static_cast<uint8_t*>(ptr) + pattern_size;
+ }
+ });
+
+ ret_object(event, hev);
+ return CL_SUCCESS;
+ }
+
+ CLOVER_NOT_SUPPORTED_UNTIL("2.0");
+ return CL_INVALID_VALUE;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clEnqueueSVMMemFill(cl_command_queue d_q,
+ void *svm_ptr,
+ const void *pattern,
+ size_t pattern_size,
+ size_t size,
+ cl_uint num_events_in_wait_list,
+ const cl_event *event_wait_list,
+ cl_event *event) {
+
+ return EnqueueSVMMemFill(d_q, svm_ptr, pattern, pattern_size,
+ size, num_events_in_wait_list, event_wait_list,
+ event, CL_COMMAND_SVM_MEMFILL);
+}
+
+cl_int
+clover::EnqueueSVMMap(cl_command_queue d_q,
+ cl_bool blocking_map,
+ cl_map_flags map_flags,
+ void *svm_ptr,
+ size_t size,
+ cl_uint num_events_in_wait_list,
+ const cl_event *event_wait_list,
+ cl_event *event,
+ cl_int cmd) try {
+
+ if (svm_ptr == nullptr || size == 0)
+ return CL_INVALID_VALUE;
+
+ auto &q = obj(d_q);
+ bool can_emulate = q.device().has_system_svm();
+ auto deps = objs<wait_list_tag>(event_wait_list, num_events_in_wait_list);
+
+ validate_common(q, deps);
+
+ if (can_emulate) {
+ auto hev = create<hard_event>(q, cmd, deps,
+ [](clover::event &) { });
+
+ ret_object(event, hev);
+ return CL_SUCCESS;
+ }
+
+ CLOVER_NOT_SUPPORTED_UNTIL("2.0");
+ return CL_INVALID_VALUE;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clEnqueueSVMMap(cl_command_queue d_q,
+ cl_bool blocking_map,
+ cl_map_flags map_flags,
+ void *svm_ptr,
+ size_t size,
+ cl_uint num_events_in_wait_list,
+ const cl_event *event_wait_list,
+ cl_event *event) {
+
+ return EnqueueSVMMap(d_q, blocking_map, map_flags, svm_ptr, size,
+ num_events_in_wait_list, event_wait_list, event,
+ CL_COMMAND_SVM_MAP);
+}
+
+cl_int
+clover::EnqueueSVMUnmap(cl_command_queue d_q,
+ void *svm_ptr,
+ cl_uint num_events_in_wait_list,
+ const cl_event *event_wait_list,
+ cl_event *event,
+ cl_int cmd) try {
+
+ if (svm_ptr == nullptr)
+ return CL_INVALID_VALUE;
+
+ auto &q = obj(d_q);
+ bool can_emulate = q.device().has_system_svm();
+ auto deps = objs<wait_list_tag>(event_wait_list, num_events_in_wait_list);
+
+ validate_common(q, deps);
+
+ if (can_emulate) {
+ auto hev = create<hard_event>(q, cmd, deps,
+ [](clover::event &) { });
+
+ ret_object(event, hev);
+ return CL_SUCCESS;
+ }
+
+ CLOVER_NOT_SUPPORTED_UNTIL("2.0");
+ return CL_INVALID_VALUE;
+
+} catch (error &e) {
+ return e.get();
+}
+
+CLOVER_API cl_int
+clEnqueueSVMUnmap(cl_command_queue d_q,
+ void *svm_ptr,
+ cl_uint num_events_in_wait_list,
+ const cl_event *event_wait_list,
+ cl_event *event) {
+
+ return EnqueueSVMUnmap(d_q, svm_ptr, num_events_in_wait_list,
+ event_wait_list, event, CL_COMMAND_SVM_UNMAP);
+}
+
+CLOVER_API cl_int
+clEnqueueSVMMigrateMem(cl_command_queue d_q,
+ cl_uint num_svm_pointers,
+ const void **svm_pointers,
+ const size_t *sizes,
+ const cl_mem_migration_flags flags,
+ cl_uint num_events_in_wait_list,
+ const cl_event *event_wait_list,
+ cl_event *event) {
+ CLOVER_NOT_SUPPORTED_UNTIL("2.1");
+ return CL_INVALID_VALUE;
+}
diff --git a/src/gallium/frontends/clover/api/util.hpp b/src/gallium/frontends/clover/api/util.hpp
new file mode 100644
index 00000000000..66bd12597c6
--- /dev/null
+++ b/src/gallium/frontends/clover/api/util.hpp
@@ -0,0 +1,84 @@
+//
+// Copyright 2012 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#ifndef CLOVER_API_UTIL_HPP
+#define CLOVER_API_UTIL_HPP
+
+#include <cassert>
+#include <iostream>
+
+#include "core/error.hpp"
+#include "core/property.hpp"
+#include "util/algorithm.hpp"
+
+#ifdef HAVE_CLOVER_ICD
+#define CLOVER_API
+#define CLOVER_ICD_API PUBLIC
+#else
+#define CLOVER_API PUBLIC
+#define CLOVER_ICD_API PUBLIC
+#endif
+
+#define CLOVER_NOT_SUPPORTED_UNTIL(version) \
+ do { \
+ std::cerr << "CL user error: " << __func__ \
+ << "() requires OpenCL version " << (version) \
+ << " or greater." << std::endl; \
+ } while (0)
+
+namespace clover {
+ ///
+ /// Return an error code in \a p if non-zero.
+ ///
+ inline void
+ ret_error(cl_int *p, const clover::error &e) {
+ if (p)
+ *p = e.get();
+ }
+
+ ///
+ /// Return a clover object in \a p if non-zero incrementing the
+ /// reference count of the object.
+ ///
+ template<typename T>
+ void
+ ret_object(typename T::descriptor_type **p,
+ const intrusive_ref<T> &v) {
+ if (p) {
+ v().retain();
+ *p = desc(v());
+ }
+ }
+
+ ///
+ /// Return an API object from an intrusive reference to a Clover object,
+ /// incrementing the reference count of the object.
+ ///
+ template<typename T>
+ typename T::descriptor_type *
+ ret_object(const intrusive_ref<T> &v) {
+ v().retain();
+ return desc(v());
+ }
+}
+
+#endif
diff --git a/src/gallium/frontends/clover/core/compiler.hpp b/src/gallium/frontends/clover/core/compiler.hpp
new file mode 100644
index 00000000000..6ef30df9b7f
--- /dev/null
+++ b/src/gallium/frontends/clover/core/compiler.hpp
@@ -0,0 +1,68 @@
+//
+// Copyright 2019 Red Hat, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#ifndef CLOVER_CORE_COMPILER_HPP
+#define CLOVER_CORE_COMPILER_HPP
+
+#include "core/device.hpp"
+#include "core/module.hpp"
+#include "llvm/invocation.hpp"
+#include "nir/invocation.hpp"
+#include "spirv/invocation.hpp"
+
+namespace clover {
+ namespace compiler {
+ static inline module
+ compile_program(const std::string &source, const header_map &headers,
+ const device &dev, const std::string &opts,
+ std::string &log) {
+ switch (dev.ir_format()) {
+#ifdef HAVE_CLOVER_SPIRV
+ case PIPE_SHADER_IR_NIR_SERIALIZED:
+ return llvm::compile_to_spirv(source, headers, dev, opts, log);
+#endif
+ case PIPE_SHADER_IR_NATIVE:
+ return llvm::compile_program(source, headers, dev, opts, log);
+ default:
+ unreachable("device with unsupported IR");
+ throw error(CL_INVALID_VALUE);
+ }
+ }
+
+ static inline module
+ link_program(const std::vector<module> &ms, const device &dev,
+ const std::string &opts, std::string &log) {
+ switch (dev.ir_format()) {
+ case PIPE_SHADER_IR_NIR_SERIALIZED:
+ return nir::spirv_to_nir(spirv::link_program(ms, dev, opts, log),
+ dev, log);
+ case PIPE_SHADER_IR_NATIVE:
+ return llvm::link_program(ms, dev, opts, log);
+ default:
+ unreachable("device with unsupported IR");
+ throw error(CL_INVALID_VALUE);
+ }
+ }
+ }
+}
+
+#endif
diff --git a/src/gallium/frontends/clover/core/context.cpp b/src/gallium/frontends/clover/core/context.cpp
new file mode 100644
index 00000000000..c3e20829384
--- /dev/null
+++ b/src/gallium/frontends/clover/core/context.cpp
@@ -0,0 +1,51 @@
+//
+// Copyright 2012 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#include "core/context.hpp"
+
+using namespace clover;
+
+context::context(const property_list &props,
+ const ref_vector<device> &devs,
+ const notify_action &notify) :
+ notify(notify), props(props), devs(devs) {
+}
+
+bool
+context::operator==(const context &ctx) const {
+ return this == &ctx;
+}
+
+bool
+context::operator!=(const context &ctx) const {
+ return this != &ctx;
+}
+
+const context::property_list &
+context::properties() const {
+ return props;
+}
+
+context::device_range
+context::devices() const {
+ return map(evals(), devs);
+}
diff --git a/src/gallium/frontends/clover/core/context.hpp b/src/gallium/frontends/clover/core/context.hpp
new file mode 100644
index 00000000000..7b22ccae78f
--- /dev/null
+++ b/src/gallium/frontends/clover/core/context.hpp
@@ -0,0 +1,67 @@
+//
+// Copyright 2012 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#ifndef CLOVER_CORE_CONTEXT_HPP
+#define CLOVER_CORE_CONTEXT_HPP
+
+#include "core/object.hpp"
+#include "core/device.hpp"
+#include "core/property.hpp"
+
+namespace clover {
+ class context : public ref_counter, public _cl_context {
+ private:
+ typedef adaptor_range<
+ evals, const std::vector<intrusive_ref<device>> &
+ > device_range;
+ typedef clover::property_list<cl_context_properties> property_list;
+
+ public:
+ typedef std::function<void (const char *)> notify_action;
+
+ context(const property_list &props, const ref_vector<device> &devs,
+ const notify_action &notify);
+
+ context(const context &ctx) = delete;
+ context &
+ operator=(const context &ctx) = delete;
+
+ bool
+ operator==(const context &ctx) const;
+ bool
+ operator!=(const context &ctx) const;
+
+ const property_list &
+ properties() const;
+
+ device_range
+ devices() const;
+
+ const notify_action notify;
+
+ private:
+ property_list props;
+ const std::vector<intrusive_ref<device>> devs;
+ };
+}
+
+#endif
diff --git a/src/gallium/frontends/clover/core/device.cpp b/src/gallium/frontends/clover/core/device.cpp
new file mode 100644
index 00000000000..609885c21bc
--- /dev/null
+++ b/src/gallium/frontends/clover/core/device.cpp
@@ -0,0 +1,337 @@
+//
+// Copyright 2012 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#include <unistd.h>
+#include "core/device.hpp"
+#include "core/platform.hpp"
+#include "pipe/p_screen.h"
+#include "pipe/p_state.h"
+#include "util/bitscan.h"
+#include "util/u_debug.h"
+
+using namespace clover;
+
+namespace {
+ template<typename T>
+ std::vector<T>
+ get_compute_param(pipe_screen *pipe, pipe_shader_ir ir_format,
+ pipe_compute_cap cap) {
+ int sz = pipe->get_compute_param(pipe, ir_format, cap, NULL);
+ std::vector<T> v(sz / sizeof(T));
+
+ pipe->get_compute_param(pipe, ir_format, cap, &v.front());
+ return v;
+ }
+}
+
+device::device(clover::platform &platform, pipe_loader_device *ldev) :
+ platform(platform), ldev(ldev) {
+ pipe = pipe_loader_create_screen(ldev);
+ if (pipe && pipe->get_param(pipe, PIPE_CAP_COMPUTE)) {
+ if (supports_ir(PIPE_SHADER_IR_NATIVE))
+ return;
+#ifdef HAVE_CLOVER_SPIRV
+ if (supports_ir(PIPE_SHADER_IR_NIR_SERIALIZED))
+ return;
+#endif
+ }
+ if (pipe)
+ pipe->destroy(pipe);
+ throw error(CL_INVALID_DEVICE);
+}
+
+device::~device() {
+ if (pipe)
+ pipe->destroy(pipe);
+ if (ldev)
+ pipe_loader_release(&ldev, 1);
+}
+
+bool
+device::operator==(const device &dev) const {
+ return this == &dev;
+}
+
+cl_device_type
+device::type() const {
+ switch (ldev->type) {
+ case PIPE_LOADER_DEVICE_SOFTWARE:
+ return CL_DEVICE_TYPE_CPU;
+ case PIPE_LOADER_DEVICE_PCI:
+ case PIPE_LOADER_DEVICE_PLATFORM:
+ return CL_DEVICE_TYPE_GPU;
+ default:
+ unreachable("Unknown device type.");
+ }
+}
+
+cl_uint
+device::vendor_id() const {
+ switch (ldev->type) {
+ case PIPE_LOADER_DEVICE_SOFTWARE:
+ case PIPE_LOADER_DEVICE_PLATFORM:
+ return 0;
+ case PIPE_LOADER_DEVICE_PCI:
+ return ldev->u.pci.vendor_id;
+ default:
+ unreachable("Unknown device type.");
+ }
+}
+
+size_t
+device::max_images_read() const {
+ return PIPE_MAX_SHADER_IMAGES;
+}
+
+size_t
+device::max_images_write() const {
+ return PIPE_MAX_SHADER_IMAGES;
+}
+
+size_t
+device::max_image_buffer_size() const {
+ return pipe->get_param(pipe, PIPE_CAP_MAX_TEXTURE_BUFFER_SIZE);
+}
+
+cl_uint
+device::max_image_levels_2d() const {
+ return util_last_bit(pipe->get_param(pipe, PIPE_CAP_MAX_TEXTURE_2D_SIZE));
+}
+
+cl_uint
+device::max_image_levels_3d() const {
+ return pipe->get_param(pipe, PIPE_CAP_MAX_TEXTURE_3D_LEVELS);
+}
+
+size_t
+device::max_image_array_number() const {
+ return pipe->get_param(pipe, PIPE_CAP_MAX_TEXTURE_ARRAY_LAYERS);
+}
+
+cl_uint
+device::max_samplers() const {
+ return pipe->get_shader_param(pipe, PIPE_SHADER_COMPUTE,
+ PIPE_SHADER_CAP_MAX_TEXTURE_SAMPLERS);
+}
+
+cl_ulong
+device::max_mem_global() const {
+ return get_compute_param<uint64_t>(pipe, ir_format(),
+ PIPE_COMPUTE_CAP_MAX_GLOBAL_SIZE)[0];
+}
+
+cl_ulong
+device::max_mem_local() const {
+ return get_compute_param<uint64_t>(pipe, ir_format(),
+ PIPE_COMPUTE_CAP_MAX_LOCAL_SIZE)[0];
+}
+
+cl_ulong
+device::max_mem_input() const {
+ return get_compute_param<uint64_t>(pipe, ir_format(),
+ PIPE_COMPUTE_CAP_MAX_INPUT_SIZE)[0];
+}
+
+cl_ulong
+device::max_const_buffer_size() const {
+ return pipe->get_shader_param(pipe, PIPE_SHADER_COMPUTE,
+ PIPE_SHADER_CAP_MAX_CONST_BUFFER_SIZE);
+}
+
+cl_uint
+device::max_const_buffers() const {
+ return pipe->get_shader_param(pipe, PIPE_SHADER_COMPUTE,
+ PIPE_SHADER_CAP_MAX_CONST_BUFFERS);
+}
+
+size_t
+device::max_threads_per_block() const {
+ return get_compute_param<uint64_t>(
+ pipe, ir_format(), PIPE_COMPUTE_CAP_MAX_THREADS_PER_BLOCK)[0];
+}
+
+cl_ulong
+device::max_mem_alloc_size() const {
+ return get_compute_param<uint64_t>(pipe, ir_format(),
+ PIPE_COMPUTE_CAP_MAX_MEM_ALLOC_SIZE)[0];
+}
+
+cl_uint
+device::max_clock_frequency() const {
+ return get_compute_param<uint32_t>(pipe, ir_format(),
+ PIPE_COMPUTE_CAP_MAX_CLOCK_FREQUENCY)[0];
+}
+
+cl_uint
+device::max_compute_units() const {
+ return get_compute_param<uint32_t>(pipe, ir_format(),
+ PIPE_COMPUTE_CAP_MAX_COMPUTE_UNITS)[0];
+}
+
+bool
+device::image_support() const {
+ return get_compute_param<uint32_t>(pipe, ir_format(),
+ PIPE_COMPUTE_CAP_IMAGES_SUPPORTED)[0];
+}
+
+bool
+device::has_doubles() const {
+ return pipe->get_param(pipe, PIPE_CAP_DOUBLES);
+}
+
+bool
+device::has_halves() const {
+ return pipe->get_shader_param(pipe, PIPE_SHADER_COMPUTE,
+ PIPE_SHADER_CAP_FP16);
+}
+
+bool
+device::has_int64_atomics() const {
+ return pipe->get_shader_param(pipe, PIPE_SHADER_COMPUTE,
+ PIPE_SHADER_CAP_INT64_ATOMICS);
+}
+
+bool
+device::has_unified_memory() const {
+ return pipe->get_param(pipe, PIPE_CAP_UMA);
+}
+
+cl_uint
+device::mem_base_addr_align() const {
+ return sysconf(_SC_PAGESIZE);
+}
+
+cl_device_svm_capabilities
+device::svm_support() const {
+ // Without CAP_RESOURCE_FROM_USER_MEMORY SVM and CL_MEM_USE_HOST_PTR
+ // interactions won't work according to spec as clover manages a GPU side
+ // copy of the host data.
+ //
+ // The biggest problem are memory buffers created with CL_MEM_USE_HOST_PTR,
+ // but the application and/or the kernel updates the memory via SVM and not
+ // the cl_mem buffer.
+ // We can't even do proper tracking on what memory might have been accessed
+ // as the host ptr to the buffer could be within a SVM region, where through
+ // the CL API there is no reliable way of knowing if a certain cl_mem buffer
+ // was accessed by a kernel or not and the runtime can't reliably know from
+ // which side the GPU buffer content needs to be updated.
+ //
+ // Another unsolvable scenario is a cl_mem object passed by cl_mem reference
+ // and SVM pointer into the same kernel at the same time.
+ if (pipe->get_param(pipe, PIPE_CAP_RESOURCE_FROM_USER_MEMORY) &&
+ pipe->get_param(pipe, PIPE_CAP_SYSTEM_SVM))
+ // we can emulate all lower levels if we support fine grain system
+ return CL_DEVICE_SVM_FINE_GRAIN_SYSTEM |
+ CL_DEVICE_SVM_COARSE_GRAIN_BUFFER |
+ CL_DEVICE_SVM_FINE_GRAIN_BUFFER;
+ return 0;
+}
+
+std::vector<size_t>
+device::max_block_size() const {
+ auto v = get_compute_param<uint64_t>(pipe, ir_format(),
+ PIPE_COMPUTE_CAP_MAX_BLOCK_SIZE);
+ return { v.begin(), v.end() };
+}
+
+cl_uint
+device::subgroup_size() const {
+ return get_compute_param<uint32_t>(pipe, ir_format(),
+ PIPE_COMPUTE_CAP_SUBGROUP_SIZE)[0];
+}
+
+cl_uint
+device::address_bits() const {
+ return get_compute_param<uint32_t>(pipe, ir_format(),
+ PIPE_COMPUTE_CAP_ADDRESS_BITS)[0];
+}
+
+std::string
+device::device_name() const {
+ return pipe->get_name(pipe);
+}
+
+std::string
+device::vendor_name() const {
+ return pipe->get_device_vendor(pipe);
+}
+
+enum pipe_shader_ir
+device::ir_format() const {
+ if (supports_ir(PIPE_SHADER_IR_NATIVE))
+ return PIPE_SHADER_IR_NATIVE;
+
+ assert(supports_ir(PIPE_SHADER_IR_NIR_SERIALIZED));
+ return PIPE_SHADER_IR_NIR_SERIALIZED;
+}
+
+std::string
+device::ir_target() const {
+ std::vector<char> target = get_compute_param<char>(
+ pipe, ir_format(), PIPE_COMPUTE_CAP_IR_TARGET);
+ return { target.data() };
+}
+
+enum pipe_endian
+device::endianness() const {
+ return (enum pipe_endian)pipe->get_param(pipe, PIPE_CAP_ENDIANNESS);
+}
+
+std::string
+device::device_version() const {
+ static const std::string device_version =
+ debug_get_option("CLOVER_DEVICE_VERSION_OVERRIDE", "1.1");
+ return device_version;
+}
+
+std::string
+device::device_clc_version() const {
+ static const std::string device_clc_version =
+ debug_get_option("CLOVER_DEVICE_CLC_VERSION_OVERRIDE", "1.1");
+ return device_clc_version;
+}
+
+bool
+device::supports_ir(enum pipe_shader_ir ir) const {
+ return pipe->get_shader_param(pipe, PIPE_SHADER_COMPUTE,
+ PIPE_SHADER_CAP_SUPPORTED_IRS) & (1 << ir);
+}
+
+std::string
+device::supported_extensions() const {
+ return
+ "cl_khr_byte_addressable_store"
+ " cl_khr_global_int32_base_atomics"
+ " cl_khr_global_int32_extended_atomics"
+ " cl_khr_local_int32_base_atomics"
+ " cl_khr_local_int32_extended_atomics"
+ + std::string(has_int64_atomics() ? " cl_khr_int64_base_atomics" : "")
+ + std::string(has_int64_atomics() ? " cl_khr_int64_extended_atomics" : "")
+ + std::string(has_doubles() ? " cl_khr_fp64" : "")
+ + std::string(has_halves() ? " cl_khr_fp16" : "")
+ + std::string(svm_support() ? " cl_arm_shared_virtual_memory" : "");
+}
+
+const void *
+device::get_compiler_options(enum pipe_shader_ir ir) const {
+ return pipe->get_compiler_options(pipe, ir, PIPE_SHADER_COMPUTE);
+}
diff --git a/src/gallium/frontends/clover/core/device.hpp b/src/gallium/frontends/clover/core/device.hpp
new file mode 100644
index 00000000000..597f9489b2c
--- /dev/null
+++ b/src/gallium/frontends/clover/core/device.hpp
@@ -0,0 +1,109 @@
+//
+// Copyright 2012 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#ifndef CLOVER_CORE_DEVICE_HPP
+#define CLOVER_CORE_DEVICE_HPP
+
+#include <set>
+#include <vector>
+
+#include "core/object.hpp"
+#include "core/format.hpp"
+#include "pipe-loader/pipe_loader.h"
+
+namespace clover {
+ class platform;
+ class root_resource;
+ class hard_event;
+
+ class device : public ref_counter, public _cl_device_id {
+ public:
+ device(clover::platform &platform, pipe_loader_device *ldev);
+ ~device();
+
+ device(const device &dev) = delete;
+ device &
+ operator=(const device &dev) = delete;
+
+ bool
+ operator==(const device &dev) const;
+
+ cl_device_type type() const;
+ cl_uint vendor_id() const;
+ size_t max_images_read() const;
+ size_t max_images_write() const;
+ size_t max_image_buffer_size() const;
+ cl_uint max_image_levels_2d() const;
+ cl_uint max_image_levels_3d() const;
+ size_t max_image_array_number() const;
+ cl_uint max_samplers() const;
+ cl_ulong max_mem_global() const;
+ cl_ulong max_mem_local() const;
+ cl_ulong max_mem_input() const;
+ cl_ulong max_const_buffer_size() const;
+ cl_uint max_const_buffers() const;
+ size_t max_threads_per_block() const;
+ cl_ulong max_mem_alloc_size() const;
+ cl_uint max_clock_frequency() const;
+ cl_uint max_compute_units() const;
+ bool image_support() const;
+ bool has_doubles() const;
+ bool has_halves() const;
+ bool has_int64_atomics() const;
+ bool has_unified_memory() const;
+ cl_uint mem_base_addr_align() const;
+ cl_device_svm_capabilities svm_support() const;
+
+ std::vector<size_t> max_block_size() const;
+ cl_uint subgroup_size() const;
+ cl_uint address_bits() const;
+ std::string device_name() const;
+ std::string vendor_name() const;
+ std::string device_version() const;
+ std::string device_clc_version() const;
+ enum pipe_shader_ir ir_format() const;
+ std::string ir_target() const;
+ enum pipe_endian endianness() const;
+ bool supports_ir(enum pipe_shader_ir ir) const;
+ std::string supported_extensions() const;
+
+ friend class command_queue;
+ friend class root_resource;
+ friend class hard_event;
+ friend std::set<cl_image_format>
+ supported_formats(const context &, cl_mem_object_type);
+ const void *get_compiler_options(enum pipe_shader_ir ir) const;
+
+ clover::platform &platform;
+
+ inline bool
+ has_system_svm() const {
+ return svm_support() & CL_DEVICE_SVM_FINE_GRAIN_SYSTEM;
+ }
+
+ private:
+ pipe_screen *pipe;
+ pipe_loader_device *ldev;
+ };
+}
+
+#endif
diff --git a/src/gallium/frontends/clover/core/error.hpp b/src/gallium/frontends/clover/core/error.hpp
new file mode 100644
index 00000000000..0490c19a276
--- /dev/null
+++ b/src/gallium/frontends/clover/core/error.hpp
@@ -0,0 +1,202 @@
+//
+// Copyright 2013 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#ifndef CLOVER_CORE_ERROR_HPP
+#define CLOVER_CORE_ERROR_HPP
+
+#include "CL/cl.h"
+
+#include <stdexcept>
+#include <string>
+
+namespace clover {
+ class command_queue;
+ class context;
+ class device;
+ class event;
+ class hard_event;
+ class soft_event;
+ class kernel;
+ class memory_obj;
+ class buffer;
+ class root_buffer;
+ class sub_buffer;
+ class image;
+ class image2d;
+ class image3d;
+ class platform;
+ class program;
+ class sampler;
+
+ ///
+ /// Class that represents an error that can be converted to an
+ /// OpenCL status code.
+ ///
+ class error : public std::runtime_error {
+ public:
+ error(cl_int code, std::string what = "") :
+ std::runtime_error(what), code(code) {
+ }
+
+ cl_int get() const {
+ return code;
+ }
+
+ protected:
+ cl_int code;
+ };
+
+ class invalid_build_options_error : public error {
+ public:
+ invalid_build_options_error(const std::string &what = "") :
+ error(CL_INVALID_BUILD_OPTIONS, what) {}
+ };
+
+ class build_error : public error {
+ public:
+ build_error(const std::string &what = "") :
+ error(CL_BUILD_PROGRAM_FAILURE, what) {}
+ };
+
+ template<typename O>
+ class invalid_object_error;
+
+ template<>
+ class invalid_object_error<command_queue> : public error {
+ public:
+ invalid_object_error(std::string what = "") :
+ error(CL_INVALID_COMMAND_QUEUE, what) {}
+ };
+
+ template<>
+ class invalid_object_error<context> : public error {
+ public:
+ invalid_object_error(std::string what = "") :
+ error(CL_INVALID_CONTEXT, what) {}
+ };
+
+ template<>
+ class invalid_object_error<device> : public error {
+ public:
+ invalid_object_error(std::string what = "") :
+ error(CL_INVALID_DEVICE, what) {}
+ };
+
+ template<>
+ class invalid_object_error<event> : public error {
+ public:
+ invalid_object_error(std::string what = "") :
+ error(CL_INVALID_EVENT, what) {}
+ };
+
+ template<>
+ class invalid_object_error<soft_event> : public error {
+ public:
+ invalid_object_error(std::string what = "") :
+ error(CL_INVALID_EVENT, what) {}
+ };
+
+ template<>
+ class invalid_object_error<kernel> : public error {
+ public:
+ invalid_object_error(std::string what = "") :
+ error(CL_INVALID_KERNEL, what) {}
+ };
+
+ template<>
+ class invalid_object_error<memory_obj> : public error {
+ public:
+ invalid_object_error(std::string what = "") :
+ error(CL_INVALID_MEM_OBJECT, what) {}
+ };
+
+ template<>
+ class invalid_object_error<buffer> : public error {
+ public:
+ invalid_object_error(std::string what = "") :
+ error(CL_INVALID_MEM_OBJECT, what) {}
+ };
+
+ template<>
+ class invalid_object_error<root_buffer> : public error {
+ public:
+ invalid_object_error(std::string what = "") :
+ error(CL_INVALID_MEM_OBJECT, what) {}
+ };
+
+ template<>
+ class invalid_object_error<sub_buffer> : public error {
+ public:
+ invalid_object_error(std::string what = "") :
+ error(CL_INVALID_MEM_OBJECT, what) {}
+ };
+
+ template<>
+ class invalid_object_error<image> : public error {
+ public:
+ invalid_object_error(std::string what = "") :
+ error(CL_INVALID_MEM_OBJECT, what) {}
+ };
+
+ template<>
+ class invalid_object_error<image2d> : public error {
+ public:
+ invalid_object_error(std::string what = "") :
+ error(CL_INVALID_MEM_OBJECT, what) {}
+ };
+
+ template<>
+ class invalid_object_error<image3d> : public error {
+ public:
+ invalid_object_error(std::string what = "") :
+ error(CL_INVALID_MEM_OBJECT, what) {}
+ };
+
+ template<>
+ class invalid_object_error<platform> : public error {
+ public:
+ invalid_object_error(std::string what = "") :
+ error(CL_INVALID_PLATFORM, what) {}
+ };
+
+ template<>
+ class invalid_object_error<program> : public error {
+ public:
+ invalid_object_error(std::string what = "") :
+ error(CL_INVALID_PROGRAM, what) {}
+ };
+
+ template<>
+ class invalid_object_error<sampler> : public error {
+ public:
+ invalid_object_error(std::string what = "") :
+ error(CL_INVALID_SAMPLER, what) {}
+ };
+
+ class invalid_wait_list_error : public error {
+ public:
+ invalid_wait_list_error(std::string what = "") :
+ error(CL_INVALID_EVENT_WAIT_LIST, what) {}
+ };
+}
+
+#endif
diff --git a/src/gallium/frontends/clover/core/event.cpp b/src/gallium/frontends/clover/core/event.cpp
new file mode 100644
index 00000000000..3d313ce8969
--- /dev/null
+++ b/src/gallium/frontends/clover/core/event.cpp
@@ -0,0 +1,267 @@
+//
+// Copyright 2012 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#include "core/event.hpp"
+#include "pipe/p_screen.h"
+
+using namespace clover;
+
+event::event(clover::context &ctx, const ref_vector<event> &deps,
+ action action_ok, action action_fail) :
+ context(ctx), _wait_count(1), _status(0),
+ action_ok(action_ok), action_fail(action_fail) {
+ for (auto &ev : deps)
+ ev.chain(*this);
+}
+
+event::~event() {
+}
+
+std::vector<intrusive_ref<event>>
+event::trigger_self() {
+ std::lock_guard<std::mutex> lock(mutex);
+ std::vector<intrusive_ref<event>> evs;
+
+ if (_wait_count && !--_wait_count)
+ std::swap(_chain, evs);
+
+ cv.notify_all();
+ return evs;
+}
+
+void
+event::trigger() try {
+ if (wait_count() == 1)
+ action_ok(*this);
+
+ for (event &ev : trigger_self())
+ ev.trigger();
+} catch (error &e) {
+ abort(e.get());
+}
+
+std::vector<intrusive_ref<event>>
+event::abort_self(cl_int status) {
+ std::lock_guard<std::mutex> lock(mutex);
+ std::vector<intrusive_ref<event>> evs;
+
+ _status = status;
+ _wait_count = 0;
+ std::swap(_chain, evs);
+
+ cv.notify_all();
+ return evs;
+}
+
+void
+event::abort(cl_int status) {
+ action_fail(*this);
+
+ for (event &ev : abort_self(status))
+ ev.abort(status);
+}
+
+unsigned
+event::wait_count() const {
+ std::lock_guard<std::mutex> lock(mutex);
+ return _wait_count;
+}
+
+bool
+event::signalled() const {
+ return !wait_count();
+}
+
+cl_int
+event::status() const {
+ std::lock_guard<std::mutex> lock(mutex);
+ return _status;
+}
+
+void
+event::chain(event &ev) {
+ std::unique_lock<std::mutex> lock(mutex, std::defer_lock);
+ std::unique_lock<std::mutex> lock_ev(ev.mutex, std::defer_lock);
+ std::lock(lock, lock_ev);
+
+ if (_wait_count) {
+ ev._wait_count++;
+ _chain.push_back(ev);
+ }
+ ev.deps.push_back(*this);
+}
+
+void
+event::wait_signalled() const {
+ std::unique_lock<std::mutex> lock(mutex);
+ cv.wait(lock, [=]{ return !_wait_count; });
+}
+
+void
+event::wait() const {
+ for (event &ev : deps)
+ ev.wait();
+
+ wait_signalled();
+}
+
+hard_event::hard_event(command_queue &q, cl_command_type command,
+ const ref_vector<event> &deps, action action) :
+ event(q.context(), deps, profile(q, action), [](event &ev){}),
+ _queue(q), _command(command), _fence(NULL) {
+ if (q.profiling_enabled())
+ _time_queued = timestamp::current(q);
+
+ q.sequence(*this);
+ trigger();
+}
+
+hard_event::~hard_event() {
+ pipe_screen *screen = queue()->device().pipe;
+ screen->fence_reference(screen, &_fence, NULL);
+}
+
+cl_int
+hard_event::status() const {
+ pipe_screen *screen = queue()->device().pipe;
+
+ if (event::status() < 0)
+ return event::status();
+
+ else if (!_fence)
+ return CL_QUEUED;
+
+ else if (!screen->fence_finish(screen, NULL, _fence, 0))
+ return CL_SUBMITTED;
+
+ else
+ return CL_COMPLETE;
+}
+
+command_queue *
+hard_event::queue() const {
+ return &_queue();
+}
+
+cl_command_type
+hard_event::command() const {
+ return _command;
+}
+
+void
+hard_event::wait() const {
+ pipe_screen *screen = queue()->device().pipe;
+
+ event::wait();
+
+ if (status() == CL_QUEUED)
+ queue()->flush();
+
+ if (!_fence ||
+ !screen->fence_finish(screen, NULL, _fence, PIPE_TIMEOUT_INFINITE))
+ throw error(CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST);
+}
+
+const lazy<cl_ulong> &
+hard_event::time_queued() const {
+ return _time_queued;
+}
+
+const lazy<cl_ulong> &
+hard_event::time_submit() const {
+ return _time_submit;
+}
+
+const lazy<cl_ulong> &
+hard_event::time_start() const {
+ return _time_start;
+}
+
+const lazy<cl_ulong> &
+hard_event::time_end() const {
+ return _time_end;
+}
+
+void
+hard_event::fence(pipe_fence_handle *fence) {
+ pipe_screen *screen = queue()->device().pipe;
+ screen->fence_reference(screen, &_fence, fence);
+}
+
+event::action
+hard_event::profile(command_queue &q, const action &action) const {
+ if (q.profiling_enabled()) {
+ return [&q, action] (event &ev) {
+ auto &hev = static_cast<hard_event &>(ev);
+
+ hev._time_submit = timestamp::current(q);
+ hev._time_start = timestamp::query(q);
+
+ action(ev);
+
+ hev._time_end = timestamp::query(q);
+ };
+
+ } else {
+ return action;
+ }
+}
+
+soft_event::soft_event(clover::context &ctx, const ref_vector<event> &deps,
+ bool _trigger, action action) :
+ event(ctx, deps, action, action) {
+ if (_trigger)
+ trigger();
+}
+
+cl_int
+soft_event::status() const {
+ if (event::status() < 0)
+ return event::status();
+
+ else if (!signalled() ||
+ any_of([](const event &ev) {
+ return ev.status() != CL_COMPLETE;
+ }, deps))
+ return CL_SUBMITTED;
+
+ else
+ return CL_COMPLETE;
+}
+
+command_queue *
+soft_event::queue() const {
+ return NULL;
+}
+
+cl_command_type
+soft_event::command() const {
+ return CL_COMMAND_USER;
+}
+
+void
+soft_event::wait() const {
+ event::wait();
+
+ if (status() != CL_COMPLETE)
+ throw error(CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST);
+}
diff --git a/src/gallium/frontends/clover/core/event.hpp b/src/gallium/frontends/clover/core/event.hpp
new file mode 100644
index 00000000000..03c97bcf4da
--- /dev/null
+++ b/src/gallium/frontends/clover/core/event.hpp
@@ -0,0 +1,164 @@
+//
+// Copyright 2012 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#ifndef CLOVER_CORE_EVENT_HPP
+#define CLOVER_CORE_EVENT_HPP
+
+#include <condition_variable>
+#include <functional>
+
+#include "core/object.hpp"
+#include "core/queue.hpp"
+#include "core/timestamp.hpp"
+#include "util/lazy.hpp"
+
+namespace clover {
+ ///
+ /// Class that represents a task that might be executed
+ /// asynchronously at some point in the future.
+ ///
+ /// An event consists of a list of dependencies, a boolean
+ /// signalled() flag, and an associated task. An event is
+ /// considered signalled as soon as all its dependencies (if any)
+ /// are signalled as well, and the trigger() method is called; at
+ /// that point the associated task will be started through the
+ /// specified \a action_ok. If the abort() method is called
+ /// instead, the specified \a action_fail is executed and the
+ /// associated task will never be started. Dependent events will
+ /// be aborted recursively.
+ ///
+ /// The execution status of the associated task can be queried
+ /// using the status() method, and it can be waited for completion
+ /// using the wait() method.
+ ///
+ class event : public ref_counter, public _cl_event {
+ public:
+ typedef std::function<void (event &)> action;
+
+ event(clover::context &ctx, const ref_vector<event> &deps,
+ action action_ok, action action_fail);
+ virtual ~event();
+
+ event(const event &ev) = delete;
+ event &
+ operator=(const event &ev) = delete;
+
+ void trigger();
+ void abort(cl_int status);
+ bool signalled() const;
+
+ virtual cl_int status() const;
+ virtual command_queue *queue() const = 0;
+ virtual cl_command_type command() const = 0;
+ void wait_signalled() const;
+ virtual void wait() const;
+
+ virtual struct pipe_fence_handle *fence() const {
+ return NULL;
+ }
+
+ const intrusive_ref<clover::context> context;
+
+ protected:
+ void chain(event &ev);
+
+ std::vector<intrusive_ref<event>> deps;
+
+ private:
+ std::vector<intrusive_ref<event>> trigger_self();
+ std::vector<intrusive_ref<event>> abort_self(cl_int status);
+ unsigned wait_count() const;
+
+ unsigned _wait_count;
+ cl_int _status;
+ action action_ok;
+ action action_fail;
+ std::vector<intrusive_ref<event>> _chain;
+ mutable std::condition_variable cv;
+ mutable std::mutex mutex;
+ };
+
+ ///
+ /// Class that represents a task executed by a command queue.
+ ///
+ /// Similar to a normal clover::event. In addition it's associated
+ /// with a given command queue \a q and a given OpenCL \a command.
+ /// hard_event instances created for the same queue are implicitly
+ /// ordered with respect to each other, and they are implicitly
+ /// triggered on construction.
+ ///
+ /// A hard_event is considered complete when the associated
+ /// hardware task finishes execution.
+ ///
+ class hard_event : public event {
+ public:
+ hard_event(command_queue &q, cl_command_type command,
+ const ref_vector<event> &deps,
+ action action = [](event &){});
+ ~hard_event();
+
+ virtual cl_int status() const;
+ virtual command_queue *queue() const;
+ virtual cl_command_type command() const;
+ virtual void wait() const;
+
+ const lazy<cl_ulong> &time_queued() const;
+ const lazy<cl_ulong> &time_submit() const;
+ const lazy<cl_ulong> &time_start() const;
+ const lazy<cl_ulong> &time_end() const;
+
+ friend class command_queue;
+
+ virtual struct pipe_fence_handle *fence() const {
+ return _fence;
+ }
+
+ private:
+ virtual void fence(pipe_fence_handle *fence);
+ action profile(command_queue &q, const action &action) const;
+
+ const intrusive_ref<command_queue> _queue;
+ cl_command_type _command;
+ pipe_fence_handle *_fence;
+ lazy<cl_ulong> _time_queued, _time_submit, _time_start, _time_end;
+ };
+
+ ///
+ /// Class that represents a software event.
+ ///
+ /// A soft_event is not associated with any specific hardware task
+ /// or command queue. It's considered complete as soon as all its
+ /// dependencies finish execution.
+ ///
+ class soft_event : public event {
+ public:
+ soft_event(clover::context &ctx, const ref_vector<event> &deps,
+ bool trigger, action action = [](event &){});
+
+ virtual cl_int status() const;
+ virtual command_queue *queue() const;
+ virtual cl_command_type command() const;
+ virtual void wait() const;
+ };
+}
+
+#endif
diff --git a/src/gallium/frontends/clover/core/format.cpp b/src/gallium/frontends/clover/core/format.cpp
new file mode 100644
index 00000000000..dee1872c829
--- /dev/null
+++ b/src/gallium/frontends/clover/core/format.cpp
@@ -0,0 +1,162 @@
+//
+// Copyright 2012 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#include "core/format.hpp"
+#include "core/memory.hpp"
+#include "pipe/p_screen.h"
+#include "pipe/p_context.h"
+
+namespace clover {
+ static const std::map<cl_image_format, pipe_format> formats {
+ { { CL_BGRA, CL_UNORM_INT8 }, PIPE_FORMAT_B8G8R8A8_UNORM },
+ { { CL_ARGB, CL_UNORM_INT8 }, PIPE_FORMAT_A8R8G8B8_UNORM },
+ { { CL_RGB, CL_UNORM_SHORT_565 }, PIPE_FORMAT_B5G6R5_UNORM },
+ { { CL_LUMINANCE, CL_UNORM_INT8 }, PIPE_FORMAT_L8_UNORM },
+ { { CL_A, CL_UNORM_INT8 }, PIPE_FORMAT_A8_UNORM },
+ { { CL_INTENSITY, CL_UNORM_INT8 }, PIPE_FORMAT_I8_UNORM },
+ { { CL_LUMINANCE, CL_UNORM_INT16 }, PIPE_FORMAT_L16_UNORM },
+ { { CL_R, CL_FLOAT }, PIPE_FORMAT_R32_FLOAT },
+ { { CL_RG, CL_FLOAT }, PIPE_FORMAT_R32G32_FLOAT },
+ { { CL_RGB, CL_FLOAT }, PIPE_FORMAT_R32G32B32_FLOAT },
+ { { CL_RGBA, CL_FLOAT }, PIPE_FORMAT_R32G32B32A32_FLOAT },
+ { { CL_R, CL_UNORM_INT16 }, PIPE_FORMAT_R16_UNORM },
+ { { CL_RG, CL_UNORM_INT16 }, PIPE_FORMAT_R16G16_UNORM },
+ { { CL_RGB, CL_UNORM_INT16 }, PIPE_FORMAT_R16G16B16_UNORM },
+ { { CL_RGBA, CL_UNORM_INT16 }, PIPE_FORMAT_R16G16B16A16_UNORM },
+ { { CL_R, CL_SNORM_INT16 }, PIPE_FORMAT_R16_SNORM },
+ { { CL_RG, CL_SNORM_INT16 }, PIPE_FORMAT_R16G16_SNORM },
+ { { CL_RGB, CL_SNORM_INT16 }, PIPE_FORMAT_R16G16B16_SNORM },
+ { { CL_RGBA, CL_SNORM_INT16 }, PIPE_FORMAT_R16G16B16A16_SNORM },
+ { { CL_R, CL_UNORM_INT8 }, PIPE_FORMAT_R8_UNORM },
+ { { CL_RG, CL_UNORM_INT8 }, PIPE_FORMAT_R8G8_UNORM },
+ { { CL_RGB, CL_UNORM_INT8 }, PIPE_FORMAT_R8G8B8_UNORM },
+ { { CL_RGBA, CL_UNORM_INT8 }, PIPE_FORMAT_R8G8B8A8_UNORM },
+ { { CL_R, CL_SNORM_INT8 }, PIPE_FORMAT_R8_SNORM },
+ { { CL_RG, CL_SNORM_INT8 }, PIPE_FORMAT_R8G8_SNORM },
+ { { CL_RGB, CL_SNORM_INT8 }, PIPE_FORMAT_R8G8B8_SNORM },
+ { { CL_RGBA, CL_SNORM_INT8 }, PIPE_FORMAT_R8G8B8A8_SNORM },
+ { { CL_R, CL_HALF_FLOAT }, PIPE_FORMAT_R16_FLOAT },
+ { { CL_RG, CL_HALF_FLOAT }, PIPE_FORMAT_R16G16_FLOAT },
+ { { CL_RGB, CL_HALF_FLOAT }, PIPE_FORMAT_R16G16B16_FLOAT },
+ { { CL_RGBA, CL_HALF_FLOAT }, PIPE_FORMAT_R16G16B16A16_FLOAT },
+ { { CL_RGBx, CL_UNORM_SHORT_555 }, PIPE_FORMAT_B5G5R5X1_UNORM },
+ { { CL_RGBx, CL_UNORM_INT8 }, PIPE_FORMAT_R8G8B8X8_UNORM },
+ { { CL_A, CL_UNORM_INT16 }, PIPE_FORMAT_A16_UNORM },
+ { { CL_INTENSITY, CL_UNORM_INT16 }, PIPE_FORMAT_I16_UNORM },
+ { { CL_LUMINANCE, CL_SNORM_INT8 }, PIPE_FORMAT_L8_SNORM },
+ { { CL_INTENSITY, CL_SNORM_INT8 }, PIPE_FORMAT_I8_SNORM },
+ { { CL_A, CL_SNORM_INT16 }, PIPE_FORMAT_A16_SNORM },
+ { { CL_LUMINANCE, CL_SNORM_INT16 }, PIPE_FORMAT_L16_SNORM },
+ { { CL_INTENSITY, CL_SNORM_INT16 }, PIPE_FORMAT_I16_SNORM },
+ { { CL_A, CL_HALF_FLOAT }, PIPE_FORMAT_A16_FLOAT },
+ { { CL_LUMINANCE, CL_HALF_FLOAT }, PIPE_FORMAT_L16_FLOAT },
+ { { CL_INTENSITY, CL_HALF_FLOAT }, PIPE_FORMAT_I16_FLOAT },
+ { { CL_A, CL_FLOAT }, PIPE_FORMAT_A32_FLOAT },
+ { { CL_LUMINANCE, CL_FLOAT }, PIPE_FORMAT_L32_FLOAT },
+ { { CL_INTENSITY, CL_FLOAT }, PIPE_FORMAT_I32_FLOAT },
+ { { CL_RA, CL_UNORM_INT8 }, PIPE_FORMAT_R8A8_UNORM },
+ { { CL_R, CL_UNSIGNED_INT8 }, PIPE_FORMAT_R8_UINT },
+ { { CL_RG, CL_UNSIGNED_INT8 }, PIPE_FORMAT_R8G8_UINT },
+ { { CL_RGB, CL_UNSIGNED_INT8 }, PIPE_FORMAT_R8G8B8_UINT },
+ { { CL_RGBA, CL_UNSIGNED_INT8 }, PIPE_FORMAT_R8G8B8A8_UINT },
+ { { CL_R, CL_SIGNED_INT8 }, PIPE_FORMAT_R8_SINT },
+ { { CL_RG, CL_SIGNED_INT8 }, PIPE_FORMAT_R8G8_SINT },
+ { { CL_RGB, CL_SIGNED_INT8 }, PIPE_FORMAT_R8G8B8_SINT },
+ { { CL_RGBA, CL_SIGNED_INT8 }, PIPE_FORMAT_R8G8B8A8_SINT },
+ { { CL_R, CL_UNSIGNED_INT16 }, PIPE_FORMAT_R16_UINT },
+ { { CL_RG, CL_UNSIGNED_INT16 }, PIPE_FORMAT_R16G16_UINT },
+ { { CL_RGB, CL_UNSIGNED_INT16 }, PIPE_FORMAT_R16G16B16_UINT },
+ { { CL_RGBA, CL_UNSIGNED_INT16 }, PIPE_FORMAT_R16G16B16A16_UINT },
+ { { CL_R, CL_SIGNED_INT16 }, PIPE_FORMAT_R16_SINT },
+ { { CL_RG, CL_SIGNED_INT16 }, PIPE_FORMAT_R16G16_SINT },
+ { { CL_RGB, CL_SIGNED_INT16 }, PIPE_FORMAT_R16G16B16_SINT },
+ { { CL_RGBA, CL_SIGNED_INT16 }, PIPE_FORMAT_R16G16B16A16_SINT },
+ { { CL_R, CL_UNSIGNED_INT32 }, PIPE_FORMAT_R32_UINT },
+ { { CL_RG, CL_UNSIGNED_INT32 }, PIPE_FORMAT_R32G32_UINT },
+ { { CL_RGB, CL_UNSIGNED_INT32 }, PIPE_FORMAT_R32G32B32_UINT },
+ { { CL_RGBA, CL_UNSIGNED_INT32 }, PIPE_FORMAT_R32G32B32A32_UINT },
+ { { CL_R, CL_SIGNED_INT32 }, PIPE_FORMAT_R32_SINT },
+ { { CL_RG, CL_SIGNED_INT32 }, PIPE_FORMAT_R32G32_SINT },
+ { { CL_RGB, CL_SIGNED_INT32 }, PIPE_FORMAT_R32G32B32_SINT },
+ { { CL_RGBA, CL_SIGNED_INT32 }, PIPE_FORMAT_R32G32B32A32_SINT },
+ { { CL_A, CL_UNSIGNED_INT8 }, PIPE_FORMAT_A8_UINT },
+ { { CL_INTENSITY, CL_UNSIGNED_INT8 }, PIPE_FORMAT_I8_UINT },
+ { { CL_LUMINANCE, CL_UNSIGNED_INT8 }, PIPE_FORMAT_L8_UINT },
+ { { CL_A, CL_SIGNED_INT8 }, PIPE_FORMAT_A8_SINT },
+ { { CL_INTENSITY, CL_SIGNED_INT8 }, PIPE_FORMAT_I8_SINT },
+ { { CL_LUMINANCE, CL_SIGNED_INT8 }, PIPE_FORMAT_L8_SINT },
+ { { CL_A, CL_UNSIGNED_INT16 }, PIPE_FORMAT_A16_UINT },
+ { { CL_INTENSITY, CL_UNSIGNED_INT16 }, PIPE_FORMAT_I16_UINT },
+ { { CL_LUMINANCE, CL_UNSIGNED_INT16 }, PIPE_FORMAT_L16_UINT },
+ { { CL_A, CL_SIGNED_INT16 }, PIPE_FORMAT_A16_SINT },
+ { { CL_INTENSITY, CL_SIGNED_INT16 }, PIPE_FORMAT_I16_SINT },
+ { { CL_LUMINANCE, CL_SIGNED_INT16 }, PIPE_FORMAT_L16_SINT },
+ { { CL_A, CL_UNSIGNED_INT32 }, PIPE_FORMAT_A32_UINT },
+ { { CL_INTENSITY, CL_UNSIGNED_INT32 }, PIPE_FORMAT_I32_UINT },
+ { { CL_LUMINANCE, CL_UNSIGNED_INT32 }, PIPE_FORMAT_L32_UINT },
+ { { CL_A, CL_SIGNED_INT32 }, PIPE_FORMAT_A32_SINT },
+ { { CL_INTENSITY, CL_SIGNED_INT32 }, PIPE_FORMAT_I32_SINT },
+ { { CL_LUMINANCE, CL_SIGNED_INT32 }, PIPE_FORMAT_L32_SINT }
+ };
+
+ pipe_texture_target
+ translate_target(cl_mem_object_type type) {
+ switch (type) {
+ case CL_MEM_OBJECT_BUFFER:
+ return PIPE_BUFFER;
+ case CL_MEM_OBJECT_IMAGE2D:
+ return PIPE_TEXTURE_2D;
+ case CL_MEM_OBJECT_IMAGE3D:
+ return PIPE_TEXTURE_3D;
+ default:
+ throw error(CL_INVALID_VALUE);
+ }
+ }
+
+ pipe_format
+ translate_format(const cl_image_format &format) {
+ auto it = formats.find(format);
+
+ if (it == formats.end())
+ throw error(CL_IMAGE_FORMAT_NOT_SUPPORTED);
+
+ return it->second;
+ }
+
+ std::set<cl_image_format>
+ supported_formats(const context &ctx, cl_mem_object_type type) {
+ std::set<cl_image_format> s;
+ pipe_texture_target target = translate_target(type);
+ unsigned bindings = (PIPE_BIND_SAMPLER_VIEW |
+ PIPE_BIND_COMPUTE_RESOURCE);
+
+ for (auto f : formats) {
+ if (all_of([=](const device &dev) {
+ return dev.pipe->is_format_supported(
+ dev.pipe, f.second, target, 1, 1, bindings);
+ }, ctx.devices()))
+ s.insert(f.first);
+ }
+
+ return s;
+ }
+}
diff --git a/src/gallium/frontends/clover/core/format.hpp b/src/gallium/frontends/clover/core/format.hpp
new file mode 100644
index 00000000000..a8b7053c5dc
--- /dev/null
+++ b/src/gallium/frontends/clover/core/format.hpp
@@ -0,0 +1,62 @@
+//
+// Copyright 2012 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#ifndef CLOVER_CORE_FORMAT_HPP
+#define CLOVER_CORE_FORMAT_HPP
+
+#include <set>
+
+#include "core/object.hpp"
+#include "pipe/p_defines.h"
+#include "pipe/p_format.h"
+
+namespace clover {
+ pipe_texture_target translate_target(cl_mem_object_type type);
+ pipe_format translate_format(const cl_image_format &format);
+
+ ///
+ /// Return all the image formats supported by a given context for
+ /// the given memory object type.
+ ///
+ std::set<cl_image_format> supported_formats(const context &ctx,
+ cl_mem_object_type type);
+}
+
+static inline bool
+operator<(const cl_image_format &a, const cl_image_format &b) {
+ return (a.image_channel_order != b.image_channel_order ?
+ a.image_channel_order < b.image_channel_order :
+ a.image_channel_data_type < b.image_channel_data_type);
+}
+
+static inline bool
+operator==(const cl_image_format &a, const cl_image_format &b) {
+ return (a.image_channel_order == b.image_channel_order &&
+ a.image_channel_data_type == b.image_channel_data_type);
+}
+
+static inline bool
+operator!=(const cl_image_format &a, const cl_image_format &b) {
+ return !(a == b);
+}
+
+#endif
diff --git a/src/gallium/frontends/clover/core/kernel.cpp b/src/gallium/frontends/clover/core/kernel.cpp
new file mode 100644
index 00000000000..7d839767aa0
--- /dev/null
+++ b/src/gallium/frontends/clover/core/kernel.cpp
@@ -0,0 +1,610 @@
+//
+// Copyright 2012 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#include "core/kernel.hpp"
+#include "core/resource.hpp"
+#include "util/factor.hpp"
+#include "util/u_math.h"
+#include "pipe/p_context.h"
+
+using namespace clover;
+
+kernel::kernel(clover::program &prog, const std::string &name,
+ const std::vector<module::argument> &margs) :
+ program(prog), _name(name), exec(*this),
+ program_ref(prog._kernel_ref_counter) {
+ for (auto &marg : margs) {
+ if (marg.semantic == module::argument::general)
+ _args.emplace_back(argument::create(marg));
+ }
+}
+
+template<typename V>
+static inline std::vector<uint>
+pad_vector(command_queue &q, const V &v, uint x) {
+ std::vector<uint> w { v.begin(), v.end() };
+ w.resize(q.device().max_block_size().size(), x);
+ return w;
+}
+
+void
+kernel::launch(command_queue &q,
+ const std::vector<size_t> &grid_offset,
+ const std::vector<size_t> &grid_size,
+ const std::vector<size_t> &block_size) {
+ const auto m = program().build(q.device()).binary;
+ const auto reduced_grid_size =
+ map(divides(), grid_size, block_size);
+ void *st = exec.bind(&q, grid_offset);
+ struct pipe_grid_info info = {};
+
+ // The handles are created during exec_context::bind(), so we need make
+ // sure to call exec_context::bind() before retrieving them.
+ std::vector<uint32_t *> g_handles = map([&](size_t h) {
+ return (uint32_t *)&exec.input[h];
+ }, exec.g_handles);
+
+ q.pipe->bind_compute_state(q.pipe, st);
+ q.pipe->bind_sampler_states(q.pipe, PIPE_SHADER_COMPUTE,
+ 0, exec.samplers.size(),
+ exec.samplers.data());
+
+ q.pipe->set_sampler_views(q.pipe, PIPE_SHADER_COMPUTE, 0,
+ exec.sviews.size(), exec.sviews.data());
+ q.pipe->set_compute_resources(q.pipe, 0, exec.resources.size(),
+ exec.resources.data());
+ q.pipe->set_global_binding(q.pipe, 0, exec.g_buffers.size(),
+ exec.g_buffers.data(), g_handles.data());
+
+ // Fill information for the launch_grid() call.
+ info.work_dim = grid_size.size();
+ copy(pad_vector(q, block_size, 1), info.block);
+ copy(pad_vector(q, reduced_grid_size, 1), info.grid);
+ info.pc = find(name_equals(_name), m.syms).offset;
+ info.input = exec.input.data();
+
+ q.pipe->launch_grid(q.pipe, &info);
+
+ q.pipe->set_global_binding(q.pipe, 0, exec.g_buffers.size(), NULL, NULL);
+ q.pipe->set_compute_resources(q.pipe, 0, exec.resources.size(), NULL);
+ q.pipe->set_sampler_views(q.pipe, PIPE_SHADER_COMPUTE, 0,
+ exec.sviews.size(), NULL);
+ q.pipe->bind_sampler_states(q.pipe, PIPE_SHADER_COMPUTE, 0,
+ exec.samplers.size(), NULL);
+
+ q.pipe->memory_barrier(q.pipe, PIPE_BARRIER_GLOBAL_BUFFER);
+ exec.unbind();
+}
+
+size_t
+kernel::mem_local() const {
+ size_t sz = 0;
+
+ for (auto &arg : args()) {
+ if (dynamic_cast<local_argument *>(&arg))
+ sz += arg.storage();
+ }
+
+ return sz;
+}
+
+size_t
+kernel::mem_private() const {
+ return 0;
+}
+
+const std::string &
+kernel::name() const {
+ return _name;
+}
+
+std::vector<size_t>
+kernel::optimal_block_size(const command_queue &q,
+ const std::vector<size_t> &grid_size) const {
+ return factor::find_grid_optimal_factor<size_t>(
+ q.device().max_threads_per_block(), q.device().max_block_size(),
+ grid_size);
+}
+
+std::vector<size_t>
+kernel::required_block_size() const {
+ return { 0, 0, 0 };
+}
+
+kernel::argument_range
+kernel::args() {
+ return map(derefs(), _args);
+}
+
+kernel::const_argument_range
+kernel::args() const {
+ return map(derefs(), _args);
+}
+
+const module &
+kernel::module(const command_queue &q) const {
+ return program().build(q.device()).binary;
+}
+
+kernel::exec_context::exec_context(kernel &kern) :
+ kern(kern), q(NULL), mem_local(0), st(NULL), cs() {
+}
+
+kernel::exec_context::~exec_context() {
+ if (st)
+ q->pipe->delete_compute_state(q->pipe, st);
+}
+
+void *
+kernel::exec_context::bind(intrusive_ptr<command_queue> _q,
+ const std::vector<size_t> &grid_offset) {
+ std::swap(q, _q);
+
+ // Bind kernel arguments.
+ auto &m = kern.program().build(q->device()).binary;
+ auto msym = find(name_equals(kern.name()), m.syms);
+ auto margs = msym.args;
+ auto msec = find(id_equals(msym.section), m.secs);
+ auto explicit_arg = kern._args.begin();
+
+ for (auto &marg : margs) {
+ switch (marg.semantic) {
+ case module::argument::general:
+ (*(explicit_arg++))->bind(*this, marg);
+ break;
+
+ case module::argument::grid_dimension: {
+ const cl_uint dimension = grid_offset.size();
+ auto arg = argument::create(marg);
+
+ arg->set(sizeof(dimension), &dimension);
+ arg->bind(*this, marg);
+ break;
+ }
+ case module::argument::grid_offset: {
+ for (cl_uint x : pad_vector(*q, grid_offset, 0)) {
+ auto arg = argument::create(marg);
+
+ arg->set(sizeof(x), &x);
+ arg->bind(*this, marg);
+ }
+ break;
+ }
+ case module::argument::image_size: {
+ auto img = dynamic_cast<image_argument &>(**(explicit_arg - 1)).get();
+ std::vector<cl_uint> image_size{
+ static_cast<cl_uint>(img->width()),
+ static_cast<cl_uint>(img->height()),
+ static_cast<cl_uint>(img->depth())};
+ for (auto x : image_size) {
+ auto arg = argument::create(marg);
+
+ arg->set(sizeof(x), &x);
+ arg->bind(*this, marg);
+ }
+ break;
+ }
+ case module::argument::image_format: {
+ auto img = dynamic_cast<image_argument &>(**(explicit_arg - 1)).get();
+ cl_image_format fmt = img->format();
+ std::vector<cl_uint> image_format{
+ static_cast<cl_uint>(fmt.image_channel_data_type),
+ static_cast<cl_uint>(fmt.image_channel_order)};
+ for (auto x : image_format) {
+ auto arg = argument::create(marg);
+
+ arg->set(sizeof(x), &x);
+ arg->bind(*this, marg);
+ }
+ break;
+ }
+ }
+ }
+
+ // Create a new compute state if anything changed.
+ if (!st || q != _q ||
+ cs.req_local_mem != mem_local ||
+ cs.req_input_mem != input.size()) {
+ if (st)
+ _q->pipe->delete_compute_state(_q->pipe, st);
+
+ cs.ir_type = q->device().ir_format();
+ cs.prog = &(msec.data[0]);
+ cs.req_local_mem = mem_local;
+ cs.req_input_mem = input.size();
+ st = q->pipe->create_compute_state(q->pipe, &cs);
+ if (!st) {
+ unbind(); // Cleanup
+ throw error(CL_OUT_OF_RESOURCES);
+ }
+ }
+
+ return st;
+}
+
+void
+kernel::exec_context::unbind() {
+ for (auto &arg : kern.args())
+ arg.unbind(*this);
+
+ input.clear();
+ samplers.clear();
+ sviews.clear();
+ resources.clear();
+ g_buffers.clear();
+ g_handles.clear();
+ mem_local = 0;
+}
+
+namespace {
+ template<typename T>
+ std::vector<uint8_t>
+ bytes(const T& x) {
+ return { (uint8_t *)&x, (uint8_t *)&x + sizeof(x) };
+ }
+
+ ///
+ /// Transform buffer \a v from the native byte order into the byte
+ /// order specified by \a e.
+ ///
+ template<typename T>
+ void
+ byteswap(T &v, pipe_endian e) {
+ if (PIPE_ENDIAN_NATIVE != e)
+ std::reverse(v.begin(), v.end());
+ }
+
+ ///
+ /// Pad buffer \a v to the next multiple of \a n.
+ ///
+ template<typename T>
+ void
+ align(T &v, size_t n) {
+ v.resize(util_align_npot(v.size(), n));
+ }
+
+ bool
+ msb(const std::vector<uint8_t> &s) {
+ if (PIPE_ENDIAN_NATIVE == PIPE_ENDIAN_LITTLE)
+ return s.back() & 0x80;
+ else
+ return s.front() & 0x80;
+ }
+
+ ///
+ /// Resize buffer \a v to size \a n using sign or zero extension
+ /// according to \a ext.
+ ///
+ template<typename T>
+ void
+ extend(T &v, enum module::argument::ext_type ext, size_t n) {
+ const size_t m = std::min(v.size(), n);
+ const bool sign_ext = (ext == module::argument::sign_ext);
+ const uint8_t fill = (sign_ext && msb(v) ? ~0 : 0);
+ T w(n, fill);
+
+ if (PIPE_ENDIAN_NATIVE == PIPE_ENDIAN_LITTLE)
+ std::copy_n(v.begin(), m, w.begin());
+ else
+ std::copy_n(v.end() - m, m, w.end() - m);
+
+ std::swap(v, w);
+ }
+
+ ///
+ /// Append buffer \a w to \a v.
+ ///
+ template<typename T>
+ void
+ insert(T &v, const T &w) {
+ v.insert(v.end(), w.begin(), w.end());
+ }
+
+ ///
+ /// Append \a n elements to the end of buffer \a v.
+ ///
+ template<typename T>
+ size_t
+ allocate(T &v, size_t n) {
+ size_t pos = v.size();
+ v.resize(pos + n);
+ return pos;
+ }
+}
+
+std::unique_ptr<kernel::argument>
+kernel::argument::create(const module::argument &marg) {
+ switch (marg.type) {
+ case module::argument::scalar:
+ return std::unique_ptr<kernel::argument>(new scalar_argument(marg.size));
+
+ case module::argument::global:
+ return std::unique_ptr<kernel::argument>(new global_argument);
+
+ case module::argument::local:
+ return std::unique_ptr<kernel::argument>(new local_argument);
+
+ case module::argument::constant:
+ return std::unique_ptr<kernel::argument>(new constant_argument);
+
+ case module::argument::image2d_rd:
+ case module::argument::image3d_rd:
+ return std::unique_ptr<kernel::argument>(new image_rd_argument);
+
+ case module::argument::image2d_wr:
+ case module::argument::image3d_wr:
+ return std::unique_ptr<kernel::argument>(new image_wr_argument);
+
+ case module::argument::sampler:
+ return std::unique_ptr<kernel::argument>(new sampler_argument);
+
+ }
+ throw error(CL_INVALID_KERNEL_DEFINITION);
+}
+
+kernel::argument::argument() : _set(false) {
+}
+
+bool
+kernel::argument::set() const {
+ return _set;
+}
+
+size_t
+kernel::argument::storage() const {
+ return 0;
+}
+
+kernel::scalar_argument::scalar_argument(size_t size) : size(size) {
+}
+
+void
+kernel::scalar_argument::set(size_t size, const void *value) {
+ if (!value)
+ throw error(CL_INVALID_ARG_VALUE);
+
+ if (size != this->size)
+ throw error(CL_INVALID_ARG_SIZE);
+
+ v = { (uint8_t *)value, (uint8_t *)value + size };
+ _set = true;
+}
+
+void
+kernel::scalar_argument::bind(exec_context &ctx,
+ const module::argument &marg) {
+ auto w = v;
+
+ extend(w, marg.ext_type, marg.target_size);
+ byteswap(w, ctx.q->device().endianness());
+ align(ctx.input, marg.target_align);
+ insert(ctx.input, w);
+}
+
+void
+kernel::scalar_argument::unbind(exec_context &ctx) {
+}
+
+void
+kernel::global_argument::set(size_t size, const void *value) {
+ if (size != sizeof(cl_mem))
+ throw error(CL_INVALID_ARG_SIZE);
+
+ buf = pobj<buffer>(value ? *(cl_mem *)value : NULL);
+ svm = nullptr;
+ _set = true;
+}
+
+void
+kernel::global_argument::set_svm(const void *value) {
+ svm = value;
+ buf = nullptr;
+ _set = true;
+}
+
+void
+kernel::global_argument::bind(exec_context &ctx,
+ const module::argument &marg) {
+ align(ctx.input, marg.target_align);
+
+ if (buf) {
+ const resource &r = buf->resource(*ctx.q);
+ ctx.g_handles.push_back(ctx.input.size());
+ ctx.g_buffers.push_back(r.pipe);
+
+ // How to handle multi-demensional offsets?
+ // We don't need to. Buffer offsets are always
+ // one-dimensional.
+ auto v = bytes(r.offset[0]);
+ extend(v, marg.ext_type, marg.target_size);
+ byteswap(v, ctx.q->device().endianness());
+ insert(ctx.input, v);
+ } else if (svm) {
+ auto v = bytes(svm);
+ extend(v, marg.ext_type, marg.target_size);
+ byteswap(v, ctx.q->device().endianness());
+ insert(ctx.input, v);
+ } else {
+ // Null pointer.
+ allocate(ctx.input, marg.target_size);
+ }
+}
+
+void
+kernel::global_argument::unbind(exec_context &ctx) {
+}
+
+size_t
+kernel::local_argument::storage() const {
+ return _storage;
+}
+
+void
+kernel::local_argument::set(size_t size, const void *value) {
+ if (value)
+ throw error(CL_INVALID_ARG_VALUE);
+
+ if (!size)
+ throw error(CL_INVALID_ARG_SIZE);
+
+ _storage = size;
+ _set = true;
+}
+
+void
+kernel::local_argument::bind(exec_context &ctx,
+ const module::argument &marg) {
+ auto v = bytes(ctx.mem_local);
+
+ extend(v, module::argument::zero_ext, marg.target_size);
+ byteswap(v, ctx.q->device().endianness());
+ align(ctx.input, marg.target_align);
+ insert(ctx.input, v);
+
+ ctx.mem_local += _storage;
+}
+
+void
+kernel::local_argument::unbind(exec_context &ctx) {
+}
+
+void
+kernel::constant_argument::set(size_t size, const void *value) {
+ if (size != sizeof(cl_mem))
+ throw error(CL_INVALID_ARG_SIZE);
+
+ buf = pobj<buffer>(value ? *(cl_mem *)value : NULL);
+ _set = true;
+}
+
+void
+kernel::constant_argument::bind(exec_context &ctx,
+ const module::argument &marg) {
+ align(ctx.input, marg.target_align);
+
+ if (buf) {
+ resource &r = buf->resource(*ctx.q);
+ auto v = bytes(ctx.resources.size() << 24 | r.offset[0]);
+
+ extend(v, module::argument::zero_ext, marg.target_size);
+ byteswap(v, ctx.q->device().endianness());
+ insert(ctx.input, v);
+
+ st = r.bind_surface(*ctx.q, false);
+ ctx.resources.push_back(st);
+ } else {
+ // Null pointer.
+ allocate(ctx.input, marg.target_size);
+ }
+}
+
+void
+kernel::constant_argument::unbind(exec_context &ctx) {
+ if (buf)
+ buf->resource(*ctx.q).unbind_surface(*ctx.q, st);
+}
+
+void
+kernel::image_rd_argument::set(size_t size, const void *value) {
+ if (!value)
+ throw error(CL_INVALID_ARG_VALUE);
+
+ if (size != sizeof(cl_mem))
+ throw error(CL_INVALID_ARG_SIZE);
+
+ img = &obj<image>(*(cl_mem *)value);
+ _set = true;
+}
+
+void
+kernel::image_rd_argument::bind(exec_context &ctx,
+ const module::argument &marg) {
+ auto v = bytes(ctx.sviews.size());
+
+ extend(v, module::argument::zero_ext, marg.target_size);
+ byteswap(v, ctx.q->device().endianness());
+ align(ctx.input, marg.target_align);
+ insert(ctx.input, v);
+
+ st = img->resource(*ctx.q).bind_sampler_view(*ctx.q);
+ ctx.sviews.push_back(st);
+}
+
+void
+kernel::image_rd_argument::unbind(exec_context &ctx) {
+ img->resource(*ctx.q).unbind_sampler_view(*ctx.q, st);
+}
+
+void
+kernel::image_wr_argument::set(size_t size, const void *value) {
+ if (!value)
+ throw error(CL_INVALID_ARG_VALUE);
+
+ if (size != sizeof(cl_mem))
+ throw error(CL_INVALID_ARG_SIZE);
+
+ img = &obj<image>(*(cl_mem *)value);
+ _set = true;
+}
+
+void
+kernel::image_wr_argument::bind(exec_context &ctx,
+ const module::argument &marg) {
+ auto v = bytes(ctx.resources.size());
+
+ extend(v, module::argument::zero_ext, marg.target_size);
+ byteswap(v, ctx.q->device().endianness());
+ align(ctx.input, marg.target_align);
+ insert(ctx.input, v);
+
+ st = img->resource(*ctx.q).bind_surface(*ctx.q, true);
+ ctx.resources.push_back(st);
+}
+
+void
+kernel::image_wr_argument::unbind(exec_context &ctx) {
+ img->resource(*ctx.q).unbind_surface(*ctx.q, st);
+}
+
+void
+kernel::sampler_argument::set(size_t size, const void *value) {
+ if (!value)
+ throw error(CL_INVALID_SAMPLER);
+
+ if (size != sizeof(cl_sampler))
+ throw error(CL_INVALID_ARG_SIZE);
+
+ s = &obj(*(cl_sampler *)value);
+ _set = true;
+}
+
+void
+kernel::sampler_argument::bind(exec_context &ctx,
+ const module::argument &marg) {
+ st = s->bind(*ctx.q);
+ ctx.samplers.push_back(st);
+}
+
+void
+kernel::sampler_argument::unbind(exec_context &ctx) {
+ s->unbind(*ctx.q, st);
+}
diff --git a/src/gallium/frontends/clover/core/kernel.hpp b/src/gallium/frontends/clover/core/kernel.hpp
new file mode 100644
index 00000000000..4441091f300
--- /dev/null
+++ b/src/gallium/frontends/clover/core/kernel.hpp
@@ -0,0 +1,251 @@
+//
+// Copyright 2012 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#ifndef CLOVER_CORE_KERNEL_HPP
+#define CLOVER_CORE_KERNEL_HPP
+
+#include <memory>
+
+#include "core/object.hpp"
+#include "core/program.hpp"
+#include "core/memory.hpp"
+#include "core/sampler.hpp"
+#include "pipe/p_state.h"
+
+namespace clover {
+ class kernel : public ref_counter, public _cl_kernel {
+ private:
+ ///
+ /// Class containing all the state required to execute a compute
+ /// kernel.
+ ///
+ struct exec_context {
+ exec_context(kernel &kern);
+ ~exec_context();
+
+ exec_context(const exec_context &) = delete;
+ exec_context &
+ operator=(const exec_context &) = delete;
+
+ void *bind(intrusive_ptr<command_queue> _q,
+ const std::vector<size_t> &grid_offset);
+ void unbind();
+
+ kernel &kern;
+ intrusive_ptr<command_queue> q;
+
+ std::vector<uint8_t> input;
+ std::vector<void *> samplers;
+ std::vector<pipe_sampler_view *> sviews;
+ std::vector<pipe_surface *> resources;
+ std::vector<pipe_resource *> g_buffers;
+ std::vector<size_t> g_handles;
+ size_t mem_local;
+
+ private:
+ void *st;
+ pipe_compute_state cs;
+ };
+
+ public:
+ class argument {
+ public:
+ static std::unique_ptr<argument>
+ create(const module::argument &marg);
+
+ argument(const argument &arg) = delete;
+ argument &
+ operator=(const argument &arg) = delete;
+
+ /// \a true if the argument has been set.
+ bool set() const;
+
+ /// Storage space required for the referenced object.
+ virtual size_t storage() const;
+
+ /// Set this argument to some object.
+ virtual void set(size_t size, const void *value) = 0;
+
+ /// Set this argument to an SVM pointer.
+ virtual void set_svm(const void *value) {
+ throw error(CL_INVALID_ARG_INDEX);
+ };
+
+ /// Allocate the necessary resources to bind the specified
+ /// object to this argument, and update \a ctx accordingly.
+ virtual void bind(exec_context &ctx,
+ const module::argument &marg) = 0;
+
+ /// Free any resources that were allocated in bind().
+ virtual void unbind(exec_context &ctx) = 0;
+
+ virtual ~argument() {};
+ protected:
+ argument();
+
+ bool _set;
+ };
+
+ private:
+ typedef adaptor_range<
+ derefs, std::vector<std::unique_ptr<argument>> &
+ > argument_range;
+
+ typedef adaptor_range<
+ derefs, const std::vector<std::unique_ptr<argument>> &
+ > const_argument_range;
+
+ public:
+ kernel(clover::program &prog, const std::string &name,
+ const std::vector<clover::module::argument> &margs);
+
+ kernel(const kernel &kern) = delete;
+ kernel &
+ operator=(const kernel &kern) = delete;
+
+ void launch(command_queue &q,
+ const std::vector<size_t> &grid_offset,
+ const std::vector<size_t> &grid_size,
+ const std::vector<size_t> &block_size);
+
+ size_t mem_local() const;
+ size_t mem_private() const;
+
+ const std::string &name() const;
+
+ std::vector<size_t>
+ optimal_block_size(const command_queue &q,
+ const std::vector<size_t> &grid_size) const;
+ std::vector<size_t>
+ required_block_size() const;
+
+ argument_range args();
+ const_argument_range args() const;
+
+ const intrusive_ref<clover::program> program;
+
+ private:
+ const clover::module &module(const command_queue &q) const;
+
+ class scalar_argument : public argument {
+ public:
+ scalar_argument(size_t size);
+
+ virtual void set(size_t size, const void *value);
+ virtual void bind(exec_context &ctx,
+ const module::argument &marg);
+ virtual void unbind(exec_context &ctx);
+
+ private:
+ size_t size;
+ std::vector<uint8_t> v;
+ };
+
+ class global_argument : public argument {
+ public:
+ virtual void set(size_t size, const void *value);
+ virtual void set_svm(const void *value);
+ virtual void bind(exec_context &ctx,
+ const module::argument &marg);
+ virtual void unbind(exec_context &ctx);
+
+ private:
+ buffer *buf;
+ const void *svm;
+ };
+
+ class local_argument : public argument {
+ public:
+ virtual size_t storage() const;
+
+ virtual void set(size_t size, const void *value);
+ virtual void bind(exec_context &ctx,
+ const module::argument &marg);
+ virtual void unbind(exec_context &ctx);
+
+ private:
+ size_t _storage = 0;
+ };
+
+ class constant_argument : public argument {
+ public:
+ virtual void set(size_t size, const void *value);
+ virtual void bind(exec_context &ctx,
+ const module::argument &marg);
+ virtual void unbind(exec_context &ctx);
+
+ private:
+ buffer *buf;
+ pipe_surface *st;
+ };
+
+ class image_argument : public argument {
+ public:
+ const image *get() const {
+ return img;
+ }
+ protected:
+ image *img;
+ };
+
+ class image_rd_argument : public image_argument {
+ public:
+ virtual void set(size_t size, const void *value);
+ virtual void bind(exec_context &ctx,
+ const module::argument &marg);
+ virtual void unbind(exec_context &ctx);
+
+ private:
+ pipe_sampler_view *st;
+ };
+
+ class image_wr_argument : public image_argument {
+ public:
+ virtual void set(size_t size, const void *value);
+ virtual void bind(exec_context &ctx,
+ const module::argument &marg);
+ virtual void unbind(exec_context &ctx);
+
+ private:
+ pipe_surface *st;
+ };
+
+ class sampler_argument : public argument {
+ public:
+ virtual void set(size_t size, const void *value);
+ virtual void bind(exec_context &ctx,
+ const module::argument &marg);
+ virtual void unbind(exec_context &ctx);
+
+ private:
+ sampler *s;
+ void *st;
+ };
+
+ std::vector<std::unique_ptr<argument>> _args;
+ std::string _name;
+ exec_context exec;
+ const ref_holder program_ref;
+ };
+}
+
+#endif
diff --git a/src/gallium/frontends/clover/core/memory.cpp b/src/gallium/frontends/clover/core/memory.cpp
new file mode 100644
index 00000000000..ed13d92c281
--- /dev/null
+++ b/src/gallium/frontends/clover/core/memory.cpp
@@ -0,0 +1,214 @@
+//
+// Copyright 2012 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#include "core/memory.hpp"
+#include "core/resource.hpp"
+#include "util/format/u_format.h"
+
+using namespace clover;
+
+memory_obj::memory_obj(clover::context &ctx, cl_mem_flags flags,
+ size_t size, void *host_ptr) :
+ context(ctx), _flags(flags),
+ _size(size), _host_ptr(host_ptr) {
+ if (flags & CL_MEM_COPY_HOST_PTR)
+ data.append((char *)host_ptr, size);
+}
+
+memory_obj::~memory_obj() {
+ while (_destroy_notify.size()) {
+ _destroy_notify.top()();
+ _destroy_notify.pop();
+ }
+}
+
+bool
+memory_obj::operator==(const memory_obj &obj) const {
+ return this == &obj;
+}
+
+void
+memory_obj::destroy_notify(std::function<void ()> f) {
+ _destroy_notify.push(f);
+}
+
+cl_mem_flags
+memory_obj::flags() const {
+ return _flags;
+}
+
+size_t
+memory_obj::size() const {
+ return _size;
+}
+
+void *
+memory_obj::host_ptr() const {
+ return _host_ptr;
+}
+
+buffer::buffer(clover::context &ctx, cl_mem_flags flags,
+ size_t size, void *host_ptr) :
+ memory_obj(ctx, flags, size, host_ptr) {
+}
+
+cl_mem_object_type
+buffer::type() const {
+ return CL_MEM_OBJECT_BUFFER;
+}
+
+root_buffer::root_buffer(clover::context &ctx, cl_mem_flags flags,
+ size_t size, void *host_ptr) :
+ buffer(ctx, flags, size, host_ptr) {
+}
+
+resource &
+root_buffer::resource(command_queue &q) {
+ // Create a new resource if there's none for this device yet.
+ if (!resources.count(&q.device())) {
+ auto r = (!resources.empty() ?
+ new root_resource(q.device(), *this,
+ *resources.begin()->second) :
+ new root_resource(q.device(), *this, q, data));
+
+ resources.insert(std::make_pair(&q.device(),
+ std::unique_ptr<root_resource>(r)));
+ data.clear();
+ }
+
+ return *resources.find(&q.device())->second;
+}
+
+sub_buffer::sub_buffer(root_buffer &parent, cl_mem_flags flags,
+ size_t offset, size_t size) :
+ buffer(parent.context(), flags, size,
+ (char *)parent.host_ptr() + offset),
+ parent(parent), _offset(offset) {
+}
+
+resource &
+sub_buffer::resource(command_queue &q) {
+ // Create a new resource if there's none for this device yet.
+ if (!resources.count(&q.device())) {
+ auto r = new sub_resource(parent().resource(q), {{ offset() }});
+
+ resources.insert(std::make_pair(&q.device(),
+ std::unique_ptr<sub_resource>(r)));
+ }
+
+ return *resources.find(&q.device())->second;
+}
+
+size_t
+sub_buffer::offset() const {
+ return _offset;
+}
+
+image::image(clover::context &ctx, cl_mem_flags flags,
+ const cl_image_format *format,
+ size_t width, size_t height, size_t depth,
+ size_t row_pitch, size_t slice_pitch, size_t size,
+ void *host_ptr) :
+ memory_obj(ctx, flags, size, host_ptr),
+ _format(*format), _width(width), _height(height), _depth(depth),
+ _row_pitch(row_pitch), _slice_pitch(slice_pitch) {
+}
+
+resource &
+image::resource(command_queue &q) {
+ // Create a new resource if there's none for this device yet.
+ if (!resources.count(&q.device())) {
+ auto r = (!resources.empty() ?
+ new root_resource(q.device(), *this,
+ *resources.begin()->second) :
+ new root_resource(q.device(), *this, q, data));
+
+ resources.insert(std::make_pair(&q.device(),
+ std::unique_ptr<root_resource>(r)));
+ data.clear();
+ }
+
+ return *resources.find(&q.device())->second;
+}
+
+cl_image_format
+image::format() const {
+ return _format;
+}
+
+size_t
+image::width() const {
+ return _width;
+}
+
+size_t
+image::height() const {
+ return _height;
+}
+
+size_t
+image::depth() const {
+ return _depth;
+}
+
+size_t
+image::pixel_size() const {
+ return util_format_get_blocksize(translate_format(_format));
+}
+
+size_t
+image::row_pitch() const {
+ return _row_pitch;
+}
+
+size_t
+image::slice_pitch() const {
+ return _slice_pitch;
+}
+
+image2d::image2d(clover::context &ctx, cl_mem_flags flags,
+ const cl_image_format *format, size_t width,
+ size_t height, size_t row_pitch,
+ void *host_ptr) :
+ image(ctx, flags, format, width, height, 1,
+ row_pitch, 0, height * row_pitch, host_ptr) {
+}
+
+cl_mem_object_type
+image2d::type() const {
+ return CL_MEM_OBJECT_IMAGE2D;
+}
+
+image3d::image3d(clover::context &ctx, cl_mem_flags flags,
+ const cl_image_format *format,
+ size_t width, size_t height, size_t depth,
+ size_t row_pitch, size_t slice_pitch,
+ void *host_ptr) :
+ image(ctx, flags, format, width, height, depth,
+ row_pitch, slice_pitch, depth * slice_pitch,
+ host_ptr) {
+}
+
+cl_mem_object_type
+image3d::type() const {
+ return CL_MEM_OBJECT_IMAGE3D;
+}
diff --git a/src/gallium/frontends/clover/core/memory.hpp b/src/gallium/frontends/clover/core/memory.hpp
new file mode 100644
index 00000000000..bd6da6be4d1
--- /dev/null
+++ b/src/gallium/frontends/clover/core/memory.hpp
@@ -0,0 +1,159 @@
+//
+// Copyright 2012 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#ifndef CLOVER_CORE_MEMORY_HPP
+#define CLOVER_CORE_MEMORY_HPP
+
+#include <functional>
+#include <map>
+#include <memory>
+#include <stack>
+
+#include "core/object.hpp"
+#include "core/queue.hpp"
+#include "core/resource.hpp"
+
+namespace clover {
+ class memory_obj : public ref_counter, public _cl_mem {
+ protected:
+ memory_obj(clover::context &ctx, cl_mem_flags flags,
+ size_t size, void *host_ptr);
+
+ memory_obj(const memory_obj &obj) = delete;
+ memory_obj &
+ operator=(const memory_obj &obj) = delete;
+
+ public:
+ virtual ~memory_obj();
+
+ bool
+ operator==(const memory_obj &obj) const;
+
+ virtual cl_mem_object_type type() const = 0;
+ virtual clover::resource &resource(command_queue &q) = 0;
+
+ void destroy_notify(std::function<void ()> f);
+ cl_mem_flags flags() const;
+ size_t size() const;
+ void *host_ptr() const;
+
+ const intrusive_ref<clover::context> context;
+
+ private:
+ cl_mem_flags _flags;
+ size_t _size;
+ void *_host_ptr;
+ std::stack<std::function<void ()>> _destroy_notify;
+
+ protected:
+ std::string data;
+ };
+
+ class buffer : public memory_obj {
+ protected:
+ buffer(clover::context &ctx, cl_mem_flags flags,
+ size_t size, void *host_ptr);
+
+ public:
+ virtual cl_mem_object_type type() const;
+ };
+
+ class root_buffer : public buffer {
+ public:
+ root_buffer(clover::context &ctx, cl_mem_flags flags,
+ size_t size, void *host_ptr);
+
+ virtual clover::resource &resource(command_queue &q);
+
+ private:
+ std::map<device *,
+ std::unique_ptr<root_resource>> resources;
+ };
+
+ class sub_buffer : public buffer {
+ public:
+ sub_buffer(root_buffer &parent, cl_mem_flags flags,
+ size_t offset, size_t size);
+
+ virtual clover::resource &resource(command_queue &q);
+ size_t offset() const;
+
+ const intrusive_ref<root_buffer> parent;
+
+ private:
+ size_t _offset;
+ std::map<device *,
+ std::unique_ptr<sub_resource>> resources;
+ };
+
+ class image : public memory_obj {
+ protected:
+ image(clover::context &ctx, cl_mem_flags flags,
+ const cl_image_format *format,
+ size_t width, size_t height, size_t depth,
+ size_t row_pitch, size_t slice_pitch, size_t size,
+ void *host_ptr);
+
+ public:
+ virtual clover::resource &resource(command_queue &q);
+ cl_image_format format() const;
+ size_t width() const;
+ size_t height() const;
+ size_t depth() const;
+ size_t pixel_size() const;
+ size_t row_pitch() const;
+ size_t slice_pitch() const;
+
+ private:
+ cl_image_format _format;
+ size_t _width;
+ size_t _height;
+ size_t _depth;
+ size_t _row_pitch;
+ size_t _slice_pitch;
+ std::map<device *,
+ std::unique_ptr<root_resource>> resources;
+ };
+
+ class image2d : public image {
+ public:
+ image2d(clover::context &ctx, cl_mem_flags flags,
+ const cl_image_format *format, size_t width,
+ size_t height, size_t row_pitch,
+ void *host_ptr);
+
+ virtual cl_mem_object_type type() const;
+ };
+
+ class image3d : public image {
+ public:
+ image3d(clover::context &ctx, cl_mem_flags flags,
+ const cl_image_format *format,
+ size_t width, size_t height, size_t depth,
+ size_t row_pitch, size_t slice_pitch,
+ void *host_ptr);
+
+ virtual cl_mem_object_type type() const;
+ };
+}
+
+#endif
diff --git a/src/gallium/frontends/clover/core/module.cpp b/src/gallium/frontends/clover/core/module.cpp
new file mode 100644
index 00000000000..a6c5b98d8e0
--- /dev/null
+++ b/src/gallium/frontends/clover/core/module.cpp
@@ -0,0 +1,228 @@
+//
+// Copyright 2012 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#include <type_traits>
+#include <iostream>
+
+#include "core/module.hpp"
+
+using namespace clover;
+
+namespace {
+ template<typename T, typename = void>
+ struct _serializer;
+
+ /// Serialize the specified object.
+ template<typename T>
+ void
+ _proc(std::ostream &os, const T &x) {
+ _serializer<T>::proc(os, x);
+ }
+
+ /// Deserialize the specified object.
+ template<typename T>
+ void
+ _proc(std::istream &is, T &x) {
+ _serializer<T>::proc(is, x);
+ }
+
+ template<typename T>
+ T
+ _proc(std::istream &is) {
+ T x;
+ _serializer<T>::proc(is, x);
+ return x;
+ }
+
+ /// Calculate the size of the specified object.
+ template<typename T>
+ void
+ _proc(module::size_t &sz, const T &x) {
+ _serializer<T>::proc(sz, x);
+ }
+
+ /// (De)serialize a scalar value.
+ template<typename T>
+ struct _serializer<T, typename std::enable_if<
+ std::is_scalar<T>::value>::type> {
+ static void
+ proc(std::ostream &os, const T &x) {
+ os.write(reinterpret_cast<const char *>(&x), sizeof(x));
+ }
+
+ static void
+ proc(std::istream &is, T &x) {
+ is.read(reinterpret_cast<char *>(&x), sizeof(x));
+ }
+
+ static void
+ proc(module::size_t &sz, const T &x) {
+ sz += sizeof(x);
+ }
+ };
+
+ /// (De)serialize a vector.
+ template<typename T>
+ struct _serializer<std::vector<T>,
+ typename std::enable_if<
+ !std::is_scalar<T>::value>::type> {
+ static void
+ proc(std::ostream &os, const std::vector<T> &v) {
+ _proc<uint32_t>(os, v.size());
+
+ for (size_t i = 0; i < v.size(); i++)
+ _proc<T>(os, v[i]);
+ }
+
+ static void
+ proc(std::istream &is, std::vector<T> &v) {
+ v.resize(_proc<uint32_t>(is));
+
+ for (size_t i = 0; i < v.size(); i++)
+ new(&v[i]) T(_proc<T>(is));
+ }
+
+ static void
+ proc(module::size_t &sz, const std::vector<T> &v) {
+ sz += sizeof(uint32_t);
+
+ for (size_t i = 0; i < v.size(); i++)
+ _proc<T>(sz, v[i]);
+ }
+ };
+
+ template<typename T>
+ struct _serializer<std::vector<T>,
+ typename std::enable_if<
+ std::is_scalar<T>::value>::type> {
+ static void
+ proc(std::ostream &os, const std::vector<T> &v) {
+ _proc<uint32_t>(os, v.size());
+ os.write(reinterpret_cast<const char *>(&v[0]),
+ v.size() * sizeof(T));
+ }
+
+ static void
+ proc(std::istream &is, std::vector<T> &v) {
+ v.resize(_proc<uint32_t>(is));
+ is.read(reinterpret_cast<char *>(&v[0]),
+ v.size() * sizeof(T));
+ }
+
+ static void
+ proc(module::size_t &sz, const std::vector<T> &v) {
+ sz += sizeof(uint32_t) + sizeof(T) * v.size();
+ }
+ };
+
+ /// (De)serialize a string.
+ template<>
+ struct _serializer<std::string> {
+ static void
+ proc(std::ostream &os, const std::string &s) {
+ _proc<uint32_t>(os, s.size());
+ os.write(&s[0], s.size() * sizeof(std::string::value_type));
+ }
+
+ static void
+ proc(std::istream &is, std::string &s) {
+ s.resize(_proc<uint32_t>(is));
+ is.read(&s[0], s.size() * sizeof(std::string::value_type));
+ }
+
+ static void
+ proc(module::size_t &sz, const std::string &s) {
+ sz += sizeof(uint32_t) + sizeof(std::string::value_type) * s.size();
+ }
+ };
+
+ /// (De)serialize a module::section.
+ template<>
+ struct _serializer<module::section> {
+ template<typename S, typename QT>
+ static void
+ proc(S &s, QT &x) {
+ _proc(s, x.id);
+ _proc(s, x.type);
+ _proc(s, x.size);
+ _proc(s, x.data);
+ }
+ };
+
+ /// (De)serialize a module::argument.
+ template<>
+ struct _serializer<module::argument> {
+ template<typename S, typename QT>
+ static void
+ proc(S &s, QT &x) {
+ _proc(s, x.type);
+ _proc(s, x.size);
+ _proc(s, x.target_size);
+ _proc(s, x.target_align);
+ _proc(s, x.ext_type);
+ _proc(s, x.semantic);
+ }
+ };
+
+ /// (De)serialize a module::symbol.
+ template<>
+ struct _serializer<module::symbol> {
+ template<typename S, typename QT>
+ static void
+ proc(S &s, QT &x) {
+ _proc(s, x.name);
+ _proc(s, x.section);
+ _proc(s, x.offset);
+ _proc(s, x.args);
+ }
+ };
+
+ /// (De)serialize a module.
+ template<>
+ struct _serializer<module> {
+ template<typename S, typename QT>
+ static void
+ proc(S &s, QT &x) {
+ _proc(s, x.syms);
+ _proc(s, x.secs);
+ }
+ };
+};
+
+namespace clover {
+ void
+ module::serialize(std::ostream &os) const {
+ _proc(os, *this);
+ }
+
+ module
+ module::deserialize(std::istream &is) {
+ return _proc<module>(is);
+ }
+
+ module::size_t
+ module::size() const {
+ size_t sz = 0;
+ _proc(sz, *this);
+ return sz;
+ }
+}
diff --git a/src/gallium/frontends/clover/core/module.hpp b/src/gallium/frontends/clover/core/module.hpp
new file mode 100644
index 00000000000..2ddd26426fb
--- /dev/null
+++ b/src/gallium/frontends/clover/core/module.hpp
@@ -0,0 +1,128 @@
+//
+// Copyright 2012 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#ifndef CLOVER_CORE_MODULE_HPP
+#define CLOVER_CORE_MODULE_HPP
+
+#include <vector>
+#include <string>
+
+namespace clover {
+ struct module {
+ typedef uint32_t resource_id;
+ typedef uint32_t size_t;
+
+ struct section {
+ enum type {
+ text_intermediate,
+ text_library,
+ text_executable,
+ data_constant,
+ data_global,
+ data_local,
+ data_private
+ };
+
+ section(resource_id id, enum type type, size_t size,
+ const std::vector<char> &data) :
+ id(id), type(type), size(size), data(data) { }
+ section() : id(0), type(text_intermediate), size(0), data() { }
+
+ resource_id id;
+ type type;
+ size_t size;
+ std::vector<char> data;
+ };
+
+ struct argument {
+ enum type {
+ scalar,
+ constant,
+ global,
+ local,
+ image2d_rd,
+ image2d_wr,
+ image3d_rd,
+ image3d_wr,
+ sampler
+ };
+
+ enum ext_type {
+ zero_ext,
+ sign_ext
+ };
+
+ enum semantic {
+ general,
+ grid_dimension,
+ grid_offset,
+ image_size,
+ image_format
+ };
+
+ argument(enum type type, size_t size,
+ size_t target_size, size_t target_align,
+ enum ext_type ext_type,
+ enum semantic semantic = general) :
+ type(type), size(size),
+ target_size(target_size), target_align(target_align),
+ ext_type(ext_type), semantic(semantic) { }
+
+ argument(enum type type, size_t size) :
+ type(type), size(size),
+ target_size(size), target_align(1),
+ ext_type(zero_ext), semantic(general) { }
+
+ argument() : type(scalar), size(0),
+ target_size(0), target_align(1),
+ ext_type(zero_ext), semantic(general) { }
+
+ type type;
+ size_t size;
+ size_t target_size;
+ size_t target_align;
+ ext_type ext_type;
+ semantic semantic;
+ };
+
+ struct symbol {
+ symbol(const std::string &name, resource_id section,
+ size_t offset, const std::vector<argument> &args) :
+ name(name), section(section), offset(offset), args(args) { }
+ symbol() : name(), section(0), offset(0), args() { }
+
+ std::string name;
+ resource_id section;
+ size_t offset;
+ std::vector<argument> args;
+ };
+
+ void serialize(std::ostream &os) const;
+ static module deserialize(std::istream &is);
+ size_t size() const;
+
+ std::vector<symbol> syms;
+ std::vector<section> secs;
+ };
+}
+
+#endif
diff --git a/src/gallium/frontends/clover/core/object.hpp b/src/gallium/frontends/clover/core/object.hpp
new file mode 100644
index 00000000000..8fc2175d236
--- /dev/null
+++ b/src/gallium/frontends/clover/core/object.hpp
@@ -0,0 +1,239 @@
+//
+// Copyright 2013 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#ifndef CLOVER_CORE_OBJECT_HPP
+#define CLOVER_CORE_OBJECT_HPP
+
+#include <cassert>
+#include <functional>
+#include <vector>
+
+#include "CL/cl.h"
+
+#include "core/error.hpp"
+#include "core/property.hpp"
+#include "api/dispatch.hpp"
+#include "util/macros.h"
+
+///
+/// Main namespace of the CL state tracker.
+///
+namespace clover {
+ ///
+ /// Class that represents a CL API object.
+ ///
+ template<typename T, typename S>
+ struct descriptor {
+ typedef T object_type;
+ typedef S descriptor_type;
+
+ descriptor() : dispatch(&_dispatch) {
+ static_assert(std::is_standard_layout<descriptor_type>::value,
+ "ICD requires CL API objects to be standard layout.");
+ }
+
+ const cl_icd_dispatch *dispatch;
+ };
+
+ struct default_tag;
+ struct allow_empty_tag;
+ struct wait_list_tag;
+ struct property_list_tag;
+
+ namespace detail {
+ template<typename T, typename D>
+ struct descriptor_traits {
+ typedef T object_type;
+
+ static void
+ validate(D *d) {
+ auto o = static_cast<typename D::object_type *>(d);
+ if (!o || o->dispatch != &_dispatch ||
+ !dynamic_cast<object_type *>(o))
+ throw invalid_object_error<T>();
+ }
+
+ static void
+ validate_list(D * const *ds, size_t n) {
+ if (!ds || !n)
+ throw error(CL_INVALID_VALUE);
+ }
+ };
+
+ template<typename D>
+ struct descriptor_traits<default_tag, D> {
+ typedef typename D::object_type object_type;
+
+ static void
+ validate(D *d) {
+ if (!d || d->dispatch != &_dispatch)
+ throw invalid_object_error<object_type>();
+ }
+
+ static void
+ validate_list(D *const *ds, size_t n) {
+ if (!ds || !n)
+ throw error(CL_INVALID_VALUE);
+ }
+ };
+
+ template<typename D>
+ struct descriptor_traits<allow_empty_tag, D> {
+ typedef typename D::object_type object_type;
+
+ static void
+ validate(D *d) {
+ if (!d || d->dispatch != &_dispatch)
+ throw invalid_object_error<object_type>();
+ }
+
+ static void
+ validate_list(D *const *ds, size_t n) {
+ if (bool(ds) != bool(n))
+ throw error(CL_INVALID_VALUE);
+ }
+ };
+
+ template<typename D>
+ struct descriptor_traits<wait_list_tag, D> {
+ typedef typename D::object_type object_type;
+
+ static void
+ validate(D *d) {
+ if (!d || d->dispatch != &_dispatch)
+ throw invalid_wait_list_error();
+ }
+
+ static void
+ validate_list(D *const *ds, size_t n) {
+ if (bool(ds) != bool(n))
+ throw invalid_wait_list_error();
+ }
+ };
+ }
+
+ ///
+ /// Get a Clover object from an API object performing object
+ /// validation.
+ ///
+ /// \a T can either be the Clover object type to return or a \a tag
+ /// object to select some special validation behavior by means of a
+ /// specialization of the detail::descriptor_traits template. The
+ /// default behavior is to infer the most general Clover object
+ /// type for the given API object.
+ ///
+ template<typename T = default_tag, typename D>
+ typename detail::descriptor_traits<T, D>::object_type &
+ obj(D *d) {
+ detail::descriptor_traits<T, D>::validate(d);
+
+ return static_cast<
+ typename detail::descriptor_traits<T, D>::object_type &>(*d);
+ }
+
+ ///
+ /// Get a pointer to a Clover object from an API object performing
+ /// object validation. Returns \c NULL if its argument is \c NULL.
+ ///
+ /// \sa obj
+ ///
+ template<typename T = default_tag, typename D>
+ typename detail::descriptor_traits<T, D>::object_type *
+ pobj(D *d) {
+ if (d)
+ detail::descriptor_traits<T, D>::validate(d);
+
+ return static_cast<
+ typename detail::descriptor_traits<T, D>::object_type *>(d);
+ }
+
+ ///
+ /// Get an API object from a Clover object.
+ ///
+ template<typename O>
+ typename O::descriptor_type *
+ desc(O &o) {
+ return static_cast<typename O::descriptor_type *>(&o);
+ }
+
+ ///
+ /// Get an API object from a pointer to a Clover object.
+ ///
+ template<typename O>
+ typename O::descriptor_type *
+ desc(O *o) {
+ return static_cast<typename O::descriptor_type *>(o);
+ }
+
+ ///
+ /// Get a range of Clover objects from a range of API objects
+ /// performing object validation.
+ ///
+ /// \sa obj
+ ///
+ template<typename T = default_tag, typename D>
+ ref_vector<typename detail::descriptor_traits<T, D>::object_type>
+ objs(D *const *ds, size_t n) {
+ detail::descriptor_traits<T, D>::validate_list(ds, n);
+ return map(obj<T, D>, range(ds, n));
+ }
+
+ ///
+ /// Get a range of API objects from a range of Clover objects.
+ ///
+ template<typename Os>
+ std::vector<typename Os::value_type::descriptor_type *>
+ descs(const Os &os) {
+ return map([](typename Os::value_type &o) {
+ return desc(o);
+ }, os);
+ }
+}
+
+struct _cl_context :
+ public clover::descriptor<clover::context, _cl_context> {};
+
+struct _cl_device_id :
+ public clover::descriptor<clover::device, _cl_device_id> {};
+
+struct _cl_event :
+ public clover::descriptor<clover::event, _cl_event> {};
+
+struct _cl_kernel :
+ public clover::descriptor<clover::kernel, _cl_kernel> {};
+
+struct _cl_mem :
+ public clover::descriptor<clover::memory_obj, _cl_mem> {};
+
+struct _cl_platform_id :
+ public clover::descriptor<clover::platform, _cl_platform_id> {};
+
+struct _cl_program :
+ public clover::descriptor<clover::program, _cl_program> {};
+
+struct _cl_command_queue :
+ public clover::descriptor<clover::command_queue, _cl_command_queue> {};
+
+struct _cl_sampler :
+ public clover::descriptor<clover::sampler, _cl_sampler> {};
+
+#endif
diff --git a/src/gallium/frontends/clover/core/platform.cpp b/src/gallium/frontends/clover/core/platform.cpp
new file mode 100644
index 00000000000..ddd63fc5a0d
--- /dev/null
+++ b/src/gallium/frontends/clover/core/platform.cpp
@@ -0,0 +1,46 @@
+//
+// Copyright 2012 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#include "core/platform.hpp"
+
+using namespace clover;
+
+platform::platform() : adaptor_range(evals(), devs) {
+ int n = pipe_loader_probe(NULL, 0);
+ std::vector<pipe_loader_device *> ldevs(n);
+
+ pipe_loader_probe(&ldevs.front(), n);
+
+ for (pipe_loader_device *ldev : ldevs) {
+ try {
+ if (ldev)
+ devs.push_back(create<device>(*this, ldev));
+ } catch (error &) {
+ pipe_loader_release(&ldev, 1);
+ }
+ }
+}
+
+std::string
+platform::supported_extensions() const {
+ return "cl_khr_icd";
+}
diff --git a/src/gallium/frontends/clover/core/platform.hpp b/src/gallium/frontends/clover/core/platform.hpp
new file mode 100644
index 00000000000..b94434c983c
--- /dev/null
+++ b/src/gallium/frontends/clover/core/platform.hpp
@@ -0,0 +1,50 @@
+//
+// Copyright 2013 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#ifndef CLOVER_CORE_PLATFORM_HPP
+#define CLOVER_CORE_PLATFORM_HPP
+
+#include <vector>
+
+#include "core/object.hpp"
+#include "core/device.hpp"
+#include "util/range.hpp"
+
+namespace clover {
+ class platform : public _cl_platform_id,
+ public adaptor_range<
+ evals, std::vector<intrusive_ref<device>> &> {
+ public:
+ platform();
+
+ platform(const platform &platform) = delete;
+ platform &
+ operator=(const platform &platform) = delete;
+
+ std::string supported_extensions() const;
+
+ protected:
+ std::vector<intrusive_ref<device>> devs;
+ };
+}
+
+#endif
diff --git a/src/gallium/frontends/clover/core/program.cpp b/src/gallium/frontends/clover/core/program.cpp
new file mode 100644
index 00000000000..526e06a26c3
--- /dev/null
+++ b/src/gallium/frontends/clover/core/program.cpp
@@ -0,0 +1,135 @@
+//
+// Copyright 2012 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#include "core/compiler.hpp"
+#include "core/program.hpp"
+
+using namespace clover;
+
+program::program(clover::context &ctx, const std::string &source) :
+ has_source(true), context(ctx), _devices(ctx.devices()), _source(source),
+ _kernel_ref_counter(0) {
+}
+
+program::program(clover::context &ctx,
+ const ref_vector<device> &devs,
+ const std::vector<module> &binaries) :
+ has_source(false), context(ctx),
+ _devices(devs), _kernel_ref_counter(0) {
+ for_each([&](device &dev, const module &bin) {
+ _builds[&dev] = { bin };
+ },
+ devs, binaries);
+}
+
+void
+program::compile(const ref_vector<device> &devs, const std::string &opts,
+ const header_map &headers) {
+ if (has_source) {
+ _devices = devs;
+
+ for (auto &dev : devs) {
+ std::string log;
+
+ try {
+ const module m =
+ compiler::compile_program(_source, headers, dev, opts, log);
+ _builds[&dev] = { m, opts, log };
+ } catch (...) {
+ _builds[&dev] = { module(), opts, log };
+ throw;
+ }
+ }
+ }
+}
+
+void
+program::link(const ref_vector<device> &devs, const std::string &opts,
+ const ref_vector<program> &progs) {
+ _devices = devs;
+
+ for (auto &dev : devs) {
+ const std::vector<module> ms = map([&](const program &prog) {
+ return prog.build(dev).binary;
+ }, progs);
+ std::string log = _builds[&dev].log;
+
+ try {
+ const module m = compiler::link_program(ms, dev, opts, log);
+ _builds[&dev] = { m, opts, log };
+ } catch (...) {
+ _builds[&dev] = { module(), opts, log };
+ throw;
+ }
+ }
+}
+
+const std::string &
+program::source() const {
+ return _source;
+}
+
+program::device_range
+program::devices() const {
+ return map(evals(), _devices);
+}
+
+cl_build_status
+program::build::status() const {
+ if (!binary.secs.empty())
+ return CL_BUILD_SUCCESS;
+ else if (log.size())
+ return CL_BUILD_ERROR;
+ else
+ return CL_BUILD_NONE;
+}
+
+cl_program_binary_type
+program::build::binary_type() const {
+ if (any_of(type_equals(module::section::text_intermediate), binary.secs))
+ return CL_PROGRAM_BINARY_TYPE_COMPILED_OBJECT;
+ else if (any_of(type_equals(module::section::text_library), binary.secs))
+ return CL_PROGRAM_BINARY_TYPE_LIBRARY;
+ else if (any_of(type_equals(module::section::text_executable), binary.secs))
+ return CL_PROGRAM_BINARY_TYPE_EXECUTABLE;
+ else
+ return CL_PROGRAM_BINARY_TYPE_NONE;
+}
+
+const struct program::build &
+program::build(const device &dev) const {
+ static const struct build null;
+ return _builds.count(&dev) ? _builds.find(&dev)->second : null;
+}
+
+const std::vector<module::symbol> &
+program::symbols() const {
+ if (_builds.empty())
+ throw error(CL_INVALID_PROGRAM_EXECUTABLE);
+
+ return _builds.begin()->second.binary.syms;
+}
+
+unsigned
+program::kernel_ref_count() const {
+ return _kernel_ref_counter.ref_count();
+}
diff --git a/src/gallium/frontends/clover/core/program.hpp b/src/gallium/frontends/clover/core/program.hpp
new file mode 100644
index 00000000000..05964e78a79
--- /dev/null
+++ b/src/gallium/frontends/clover/core/program.hpp
@@ -0,0 +1,91 @@
+//
+// Copyright 2012 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#ifndef CLOVER_CORE_PROGRAM_HPP
+#define CLOVER_CORE_PROGRAM_HPP
+
+#include <map>
+
+#include "core/object.hpp"
+#include "core/context.hpp"
+#include "core/module.hpp"
+
+namespace clover {
+ typedef std::vector<std::pair<std::string, std::string>> header_map;
+
+ class program : public ref_counter, public _cl_program {
+ private:
+ typedef adaptor_range<
+ evals, const std::vector<intrusive_ref<device>> &> device_range;
+
+ public:
+ program(clover::context &ctx,
+ const std::string &source);
+ program(clover::context &ctx,
+ const ref_vector<device> &devs = {},
+ const std::vector<module> &binaries = {});
+
+ program(const program &prog) = delete;
+ program &
+ operator=(const program &prog) = delete;
+
+ void compile(const ref_vector<device> &devs, const std::string &opts,
+ const header_map &headers = {});
+ void link(const ref_vector<device> &devs, const std::string &opts,
+ const ref_vector<program> &progs);
+
+ const bool has_source;
+ const std::string &source() const;
+
+ device_range devices() const;
+
+ struct build {
+ build(const module &m = {}, const std::string &opts = {},
+ const std::string &log = {}) : binary(m), opts(opts), log(log) {}
+
+ cl_build_status status() const;
+ cl_program_binary_type binary_type() const;
+
+ module binary;
+ std::string opts;
+ std::string log;
+ };
+
+ const build &build(const device &dev) const;
+
+ const std::vector<module::symbol> &symbols() const;
+
+ unsigned kernel_ref_count() const;
+
+ const intrusive_ref<clover::context> context;
+
+ friend class kernel;
+
+ private:
+ std::vector<intrusive_ref<device>> _devices;
+ std::map<const device *, struct build> _builds;
+ std::string _source;
+ ref_counter _kernel_ref_counter;
+ };
+}
+
+#endif
diff --git a/src/gallium/frontends/clover/core/property.hpp b/src/gallium/frontends/clover/core/property.hpp
new file mode 100644
index 00000000000..7f8e17684d9
--- /dev/null
+++ b/src/gallium/frontends/clover/core/property.hpp
@@ -0,0 +1,261 @@
+//
+// Copyright 2013 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#ifndef CLOVER_CORE_PROPERTY_HPP
+#define CLOVER_CORE_PROPERTY_HPP
+
+#include <map>
+
+#include "util/range.hpp"
+#include "util/algorithm.hpp"
+
+namespace clover {
+ class property_buffer;
+
+ namespace detail {
+ template<typename T>
+ class property_scalar {
+ public:
+ property_scalar(property_buffer &buf) : buf(buf) {
+ }
+
+ inline property_scalar &
+ operator=(const T &x);
+
+ private:
+ property_buffer &buf;
+ };
+
+ template<typename T>
+ class property_vector {
+ public:
+ property_vector(property_buffer &buf) : buf(buf) {
+ }
+
+ template<typename S>
+ inline property_vector &
+ operator=(const S &v);
+
+ private:
+ property_buffer &buf;
+ };
+
+ template<typename T>
+ class property_matrix {
+ public:
+ property_matrix(property_buffer &buf) : buf(buf) {
+ }
+
+ template<typename S>
+ inline property_matrix &
+ operator=(const S &v);
+
+ private:
+ property_buffer &buf;
+ };
+
+ class property_string {
+ public:
+ property_string(property_buffer &buf) : buf(buf) {
+ }
+
+ inline property_string &
+ operator=(const std::string &v);
+
+ private:
+ property_buffer &buf;
+ };
+ };
+
+ ///
+ /// Return value buffer used by the CL property query functions.
+ ///
+ class property_buffer {
+ public:
+ property_buffer(void *r_buf, size_t size, size_t *r_size) :
+ r_buf(r_buf), size(size), r_size(r_size) {
+ }
+
+ template<typename T>
+ detail::property_scalar<T>
+ as_scalar() {
+ return { *this };
+ }
+
+ template<typename T>
+ detail::property_vector<T>
+ as_vector() {
+ return { *this };
+ }
+
+ template<typename T>
+ detail::property_matrix<T>
+ as_matrix() {
+ return { *this };
+ }
+
+ detail::property_string
+ as_string() {
+ return { *this };
+ }
+
+ template<typename T>
+ iterator_range<T *>
+ allocate(size_t n) {
+ if (r_buf && size < n * sizeof(T))
+ throw error(CL_INVALID_VALUE);
+
+ if (r_size)
+ *r_size = n * sizeof(T);
+
+ if (r_buf)
+ return range((T *)r_buf, n);
+ else
+ return { };
+ }
+
+ private:
+ void *const r_buf;
+ const size_t size;
+ size_t *const r_size;
+ };
+
+ namespace detail {
+ template<typename T>
+ inline property_scalar<T> &
+ property_scalar<T>::operator=(const T &x) {
+ auto r = buf.allocate<T>(1);
+
+ if (!r.empty())
+ r.front() = x;
+
+ return *this;
+ }
+
+ template<typename T>
+ template<typename S>
+ inline property_vector<T> &
+ property_vector<T>::operator=(const S &v) {
+ auto r = buf.allocate<T>(v.size());
+
+ if (!r.empty())
+ copy(v, r.begin());
+
+ return *this;
+ }
+
+ template<typename T>
+ template<typename S>
+ inline property_matrix<T> &
+ property_matrix<T>::operator=(const S &v) {
+ auto r = buf.allocate<T *>(v.size());
+
+ if (!r.empty())
+ for_each([](typename S::value_type src, T *dst) {
+ if (dst)
+ copy(src, dst);
+ }, v, r);
+
+ return *this;
+ }
+
+ inline property_string &
+ property_string::operator=(const std::string &v) {
+ auto r = buf.allocate<char>(v.size() + 1);
+
+ if (!r.empty())
+ copy(range(v.begin(), r.size()), r.begin());
+
+ return *this;
+ }
+ };
+
+ template<typename T>
+ class property_element {
+ public:
+ property_element() : x() {
+ }
+
+ property_element(T x) : x(x) {
+ }
+
+ template<typename S>
+ S
+ as() const {
+ assert(sizeof(S) <= sizeof(T));
+ return reinterpret_cast<S>(x);
+ }
+
+ private:
+ T x;
+ };
+
+ template<typename D>
+ using property_list = std::map<D, property_element<D>>;
+
+ struct property_list_tag;
+
+ ///
+ /// Create a clover::property_list object from a zero-terminated
+ /// CL property list.
+ ///
+ template<typename T, typename D,
+ typename = typename std::enable_if<
+ std::is_same<T, property_list_tag>::value>::type>
+ property_list<D>
+ obj(const D *d_props) {
+ property_list<D> props;
+
+ while (d_props && *d_props) {
+ auto key = *d_props++;
+ auto value = *d_props++;
+
+ if (props.count(key))
+ throw error(CL_INVALID_PROPERTY);
+
+ props.insert({ key, value });
+ }
+
+ return props;
+ }
+
+ ///
+ /// Create a zero-terminated CL property list from a
+ /// clover::property_list object.
+ ///
+ template<typename D>
+ std::vector<D>
+ desc(const property_list<D> &props) {
+ std::vector<D> d_props;
+
+ for (auto &prop : props) {
+ d_props.push_back(prop.first);
+ d_props.push_back(prop.second.template as<D>());
+ }
+
+ d_props.push_back(0);
+
+ return d_props;
+ }
+}
+
+#endif
diff --git a/src/gallium/frontends/clover/core/queue.cpp b/src/gallium/frontends/clover/core/queue.cpp
new file mode 100644
index 00000000000..c91b97ad15e
--- /dev/null
+++ b/src/gallium/frontends/clover/core/queue.cpp
@@ -0,0 +1,102 @@
+//
+// Copyright 2012 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#include "core/queue.hpp"
+#include "core/event.hpp"
+#include "pipe/p_screen.h"
+#include "pipe/p_context.h"
+#include "pipe/p_state.h"
+
+using namespace clover;
+
+namespace {
+ void
+ debug_notify_callback(void *data,
+ unsigned *id,
+ enum pipe_debug_type type,
+ const char *fmt,
+ va_list args) {
+ const command_queue *queue = (const command_queue *)data;
+ char buffer[1024];
+ vsnprintf(buffer, sizeof(buffer), fmt, args);
+ queue->context().notify(buffer);
+ }
+}
+
+command_queue::command_queue(clover::context &ctx, clover::device &dev,
+ cl_command_queue_properties props) :
+ context(ctx), device(dev), props(props) {
+ pipe = dev.pipe->context_create(dev.pipe, NULL, PIPE_CONTEXT_COMPUTE_ONLY);
+ if (!pipe)
+ throw error(CL_INVALID_DEVICE);
+
+ if (ctx.notify) {
+ struct pipe_debug_callback cb;
+ memset(&cb, 0, sizeof(cb));
+ cb.debug_message = &debug_notify_callback;
+ cb.data = this;
+ if (pipe->set_debug_callback)
+ pipe->set_debug_callback(pipe, &cb);
+ }
+}
+
+command_queue::~command_queue() {
+ pipe->destroy(pipe);
+}
+
+void
+command_queue::flush() {
+ pipe_screen *screen = device().pipe;
+ pipe_fence_handle *fence = NULL;
+
+ std::lock_guard<std::mutex> lock(queued_events_mutex);
+ if (!queued_events.empty()) {
+ pipe->flush(pipe, &fence, 0);
+
+ while (!queued_events.empty() &&
+ queued_events.front()().signalled()) {
+ queued_events.front()().fence(fence);
+ queued_events.pop_front();
+ }
+
+ screen->fence_reference(screen, &fence, NULL);
+ }
+}
+
+cl_command_queue_properties
+command_queue::properties() const {
+ return props;
+}
+
+bool
+command_queue::profiling_enabled() const {
+ return props & CL_QUEUE_PROFILING_ENABLE;
+}
+
+void
+command_queue::sequence(hard_event &ev) {
+ std::lock_guard<std::mutex> lock(queued_events_mutex);
+ if (!queued_events.empty())
+ queued_events.back()().chain(ev);
+
+ queued_events.push_back(ev);
+}
diff --git a/src/gallium/frontends/clover/core/queue.hpp b/src/gallium/frontends/clover/core/queue.hpp
new file mode 100644
index 00000000000..bddb86c0e4c
--- /dev/null
+++ b/src/gallium/frontends/clover/core/queue.hpp
@@ -0,0 +1,78 @@
+//
+// Copyright 2012 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#ifndef CLOVER_CORE_QUEUE_HPP
+#define CLOVER_CORE_QUEUE_HPP
+
+#include <deque>
+#include <mutex>
+
+#include "core/object.hpp"
+#include "core/context.hpp"
+#include "core/timestamp.hpp"
+#include "pipe/p_context.h"
+
+namespace clover {
+ class resource;
+ class mapping;
+ class hard_event;
+
+ class command_queue : public ref_counter, public _cl_command_queue {
+ public:
+ command_queue(clover::context &ctx, clover::device &dev,
+ cl_command_queue_properties props);
+ ~command_queue();
+
+ command_queue(const command_queue &q) = delete;
+ command_queue &
+ operator=(const command_queue &q) = delete;
+
+ void flush();
+
+ cl_command_queue_properties properties() const;
+ bool profiling_enabled() const;
+
+ const intrusive_ref<clover::context> context;
+ const intrusive_ref<clover::device> device;
+
+ friend class resource;
+ friend class root_resource;
+ friend class mapping;
+ friend class hard_event;
+ friend class sampler;
+ friend class kernel;
+ friend class clover::timestamp::query;
+ friend class clover::timestamp::current;
+
+ private:
+ /// Serialize a hardware event with respect to the previous ones,
+ /// and push it to the pending list.
+ void sequence(hard_event &ev);
+
+ cl_command_queue_properties props;
+ pipe_context *pipe;
+ std::mutex queued_events_mutex;
+ std::deque<intrusive_ref<hard_event>> queued_events;
+ };
+}
+
+#endif
diff --git a/src/gallium/frontends/clover/core/resource.cpp b/src/gallium/frontends/clover/core/resource.cpp
new file mode 100644
index 00000000000..dd207982588
--- /dev/null
+++ b/src/gallium/frontends/clover/core/resource.cpp
@@ -0,0 +1,231 @@
+//
+// Copyright 2012 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#include "core/resource.hpp"
+#include "core/memory.hpp"
+#include "pipe/p_screen.h"
+#include "util/u_sampler.h"
+#include "util/format/u_format.h"
+#include "util/u_inlines.h"
+
+using namespace clover;
+
+namespace {
+ class box {
+ public:
+ box(const resource::vector &origin, const resource::vector &size) :
+ pipe({ (int)origin[0], (int16_t)origin[1],
+ (int16_t)origin[2], (int)size[0],
+ (int16_t)size[1], (int16_t)size[2] }) {
+ }
+
+ operator const pipe_box *() {
+ return &pipe;
+ }
+
+ protected:
+ pipe_box pipe;
+ };
+}
+
+resource::resource(clover::device &dev, memory_obj &obj) :
+ device(dev), obj(obj), pipe(NULL), offset() {
+}
+
+resource::~resource() {
+}
+
+void
+resource::copy(command_queue &q, const vector &origin, const vector &region,
+ resource &src_res, const vector &src_origin) {
+ auto p = offset + origin;
+
+ q.pipe->resource_copy_region(q.pipe, pipe, 0, p[0], p[1], p[2],
+ src_res.pipe, 0,
+ box(src_res.offset + src_origin, region));
+}
+
+void *
+resource::add_map(command_queue &q, cl_map_flags flags, bool blocking,
+ const vector &origin, const vector &region) {
+ maps.emplace_back(q, *this, flags, blocking, origin, region);
+ return maps.back();
+}
+
+void
+resource::del_map(void *p) {
+ erase_if([&](const mapping &m) {
+ return static_cast<void *>(m) == p;
+ }, maps);
+}
+
+unsigned
+resource::map_count() const {
+ return maps.size();
+}
+
+pipe_sampler_view *
+resource::bind_sampler_view(command_queue &q) {
+ pipe_sampler_view info;
+
+ u_sampler_view_default_template(&info, pipe, pipe->format);
+ return q.pipe->create_sampler_view(q.pipe, pipe, &info);
+}
+
+void
+resource::unbind_sampler_view(command_queue &q,
+ pipe_sampler_view *st) {
+ q.pipe->sampler_view_destroy(q.pipe, st);
+}
+
+pipe_surface *
+resource::bind_surface(command_queue &q, bool rw) {
+ pipe_surface info {};
+
+ info.format = pipe->format;
+ info.writable = rw;
+
+ if (pipe->target == PIPE_BUFFER)
+ info.u.buf.last_element = pipe->width0 - 1;
+
+ return q.pipe->create_surface(q.pipe, pipe, &info);
+}
+
+void
+resource::unbind_surface(command_queue &q, pipe_surface *st) {
+ q.pipe->surface_destroy(q.pipe, st);
+}
+
+root_resource::root_resource(clover::device &dev, memory_obj &obj,
+ command_queue &q, const std::string &data) :
+ resource(dev, obj) {
+ pipe_resource info {};
+ const bool user_ptr_support = dev.pipe->get_param(dev.pipe,
+ PIPE_CAP_RESOURCE_FROM_USER_MEMORY);
+
+ if (image *img = dynamic_cast<image *>(&obj)) {
+ info.format = translate_format(img->format());
+ info.width0 = img->width();
+ info.height0 = img->height();
+ info.depth0 = img->depth();
+ } else {
+ info.width0 = obj.size();
+ info.height0 = 1;
+ info.depth0 = 1;
+ }
+
+ info.array_size = 1;
+ info.target = translate_target(obj.type());
+ info.bind = (PIPE_BIND_SAMPLER_VIEW |
+ PIPE_BIND_COMPUTE_RESOURCE |
+ PIPE_BIND_GLOBAL);
+
+ if (obj.flags() & CL_MEM_USE_HOST_PTR && user_ptr_support) {
+ // Page alignment is normally required for this, just try, hope for the
+ // best and fall back if it fails.
+ pipe = dev.pipe->resource_from_user_memory(dev.pipe, &info, obj.host_ptr());
+ if (pipe)
+ return;
+ }
+
+ if (obj.flags() & (CL_MEM_ALLOC_HOST_PTR | CL_MEM_USE_HOST_PTR)) {
+ info.usage = PIPE_USAGE_STAGING;
+ }
+
+ pipe = dev.pipe->resource_create(dev.pipe, &info);
+ if (!pipe)
+ throw error(CL_OUT_OF_RESOURCES);
+
+ if (obj.flags() & (CL_MEM_USE_HOST_PTR | CL_MEM_COPY_HOST_PTR)) {
+ const void *data_ptr = !data.empty() ? data.data() : obj.host_ptr();
+ box rect { {{ 0, 0, 0 }}, {{ info.width0, info.height0, info.depth0 }} };
+ unsigned cpp = util_format_get_blocksize(info.format);
+
+ if (pipe->target == PIPE_BUFFER)
+ q.pipe->buffer_subdata(q.pipe, pipe, PIPE_TRANSFER_WRITE,
+ 0, info.width0, data_ptr);
+ else
+ q.pipe->texture_subdata(q.pipe, pipe, 0, PIPE_TRANSFER_WRITE,
+ rect, data_ptr, cpp * info.width0,
+ cpp * info.width0 * info.height0);
+ }
+}
+
+root_resource::root_resource(clover::device &dev, memory_obj &obj,
+ root_resource &r) :
+ resource(dev, obj) {
+ assert(0); // XXX -- resource shared among dev and r.dev
+}
+
+root_resource::~root_resource() {
+ pipe_resource_reference(&this->pipe, NULL);
+}
+
+sub_resource::sub_resource(resource &r, const vector &offset) :
+ resource(r.device(), r.obj) {
+ this->pipe = r.pipe;
+ this->offset = r.offset + offset;
+}
+
+mapping::mapping(command_queue &q, resource &r,
+ cl_map_flags flags, bool blocking,
+ const resource::vector &origin,
+ const resource::vector &region) :
+ pctx(q.pipe), pres(NULL) {
+ unsigned usage = ((flags & CL_MAP_WRITE ? PIPE_TRANSFER_WRITE : 0 ) |
+ (flags & CL_MAP_READ ? PIPE_TRANSFER_READ : 0 ) |
+ (flags & CL_MAP_WRITE_INVALIDATE_REGION ?
+ PIPE_TRANSFER_DISCARD_RANGE : 0) |
+ (!blocking ? PIPE_TRANSFER_UNSYNCHRONIZED : 0));
+
+ p = pctx->transfer_map(pctx, r.pipe, 0, usage,
+ box(origin + r.offset, region), &pxfer);
+ if (!p) {
+ pxfer = NULL;
+ throw error(CL_OUT_OF_RESOURCES);
+ }
+ pipe_resource_reference(&pres, r.pipe);
+}
+
+mapping::mapping(mapping &&m) :
+ pctx(m.pctx), pxfer(m.pxfer), pres(m.pres), p(m.p) {
+ m.pctx = NULL;
+ m.pxfer = NULL;
+ m.pres = NULL;
+ m.p = NULL;
+}
+
+mapping::~mapping() {
+ if (pxfer) {
+ pctx->transfer_unmap(pctx, pxfer);
+ }
+ pipe_resource_reference(&pres, NULL);
+}
+
+mapping &
+mapping::operator=(mapping m) {
+ std::swap(pctx, m.pctx);
+ std::swap(pxfer, m.pxfer);
+ std::swap(pres, m.pres);
+ std::swap(p, m.p);
+ return *this;
+}
diff --git a/src/gallium/frontends/clover/core/resource.hpp b/src/gallium/frontends/clover/core/resource.hpp
new file mode 100644
index 00000000000..3b994b4008b
--- /dev/null
+++ b/src/gallium/frontends/clover/core/resource.hpp
@@ -0,0 +1,133 @@
+//
+// Copyright 2012 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#ifndef CLOVER_CORE_RESOURCE_HPP
+#define CLOVER_CORE_RESOURCE_HPP
+
+#include <list>
+
+#include "core/queue.hpp"
+#include "util/algebra.hpp"
+#include "pipe/p_state.h"
+
+namespace clover {
+ class memory_obj;
+ class mapping;
+
+ ///
+ /// Class that represents a device-specific instance of some memory
+ /// object.
+ ///
+ class resource {
+ public:
+ typedef std::array<size_t, 3> vector;
+
+ virtual ~resource();
+
+ resource(const resource &r) = delete;
+ resource &
+ operator=(const resource &r) = delete;
+
+ void copy(command_queue &q, const vector &origin, const vector &region,
+ resource &src_resource, const vector &src_origin);
+
+ void *add_map(command_queue &q, cl_map_flags flags, bool blocking,
+ const vector &origin, const vector &region);
+ void del_map(void *p);
+ unsigned map_count() const;
+
+ const intrusive_ref<clover::device> device;
+ memory_obj &obj;
+
+ friend class sub_resource;
+ friend class mapping;
+ friend class kernel;
+
+ protected:
+ resource(clover::device &dev, memory_obj &obj);
+
+ pipe_sampler_view *bind_sampler_view(command_queue &q);
+ void unbind_sampler_view(command_queue &q,
+ pipe_sampler_view *st);
+
+ pipe_surface *bind_surface(command_queue &q, bool rw);
+ void unbind_surface(command_queue &q, pipe_surface *st);
+
+ pipe_resource *pipe;
+ vector offset;
+
+ private:
+ std::list<mapping> maps;
+ };
+
+ ///
+ /// Resource associated with its own top-level data storage
+ /// allocated in some device.
+ ///
+ class root_resource : public resource {
+ public:
+ root_resource(clover::device &dev, memory_obj &obj,
+ command_queue &q, const std::string &data);
+ root_resource(clover::device &dev, memory_obj &obj, root_resource &r);
+ virtual ~root_resource();
+ };
+
+ ///
+ /// Resource that reuses a portion of some other resource as data
+ /// storage.
+ ///
+ class sub_resource : public resource {
+ public:
+ sub_resource(resource &r, const vector &offset);
+ };
+
+ ///
+ /// Class that represents a mapping of some resource into the CPU
+ /// memory space.
+ ///
+ class mapping {
+ public:
+ mapping(command_queue &q, resource &r, cl_map_flags flags,
+ bool blocking, const resource::vector &origin,
+ const resource::vector &region);
+ mapping(mapping &&m);
+ ~mapping();
+
+ mapping &
+ operator=(mapping m);
+
+ mapping(const mapping &m) = delete;
+
+ template<typename T>
+ operator T *() const {
+ return (T *)p;
+ }
+
+ private:
+ pipe_context *pctx;
+ pipe_transfer *pxfer;
+ pipe_resource *pres;
+ void *p;
+ };
+}
+
+#endif
diff --git a/src/gallium/frontends/clover/core/sampler.cpp b/src/gallium/frontends/clover/core/sampler.cpp
new file mode 100644
index 00000000000..6f2784b538e
--- /dev/null
+++ b/src/gallium/frontends/clover/core/sampler.cpp
@@ -0,0 +1,73 @@
+//
+// Copyright 2012 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#include "core/sampler.hpp"
+#include "pipe/p_state.h"
+
+using namespace clover;
+
+sampler::sampler(clover::context &ctx, bool norm_mode,
+ cl_addressing_mode addr_mode,
+ cl_filter_mode filter_mode) :
+ context(ctx), _norm_mode(norm_mode),
+ _addr_mode(addr_mode), _filter_mode(filter_mode) {
+}
+
+bool
+sampler::norm_mode() {
+ return _norm_mode;
+}
+
+cl_addressing_mode
+sampler::addr_mode() {
+ return _addr_mode;
+}
+
+cl_filter_mode
+sampler::filter_mode() {
+ return _filter_mode;
+}
+
+void *
+sampler::bind(command_queue &q) {
+ struct pipe_sampler_state info {};
+
+ info.normalized_coords = norm_mode();
+
+ info.wrap_s = info.wrap_t = info.wrap_r =
+ (addr_mode() == CL_ADDRESS_CLAMP_TO_EDGE ? PIPE_TEX_WRAP_CLAMP_TO_EDGE :
+ addr_mode() == CL_ADDRESS_CLAMP ? PIPE_TEX_WRAP_CLAMP_TO_BORDER :
+ addr_mode() == CL_ADDRESS_REPEAT ? PIPE_TEX_WRAP_REPEAT :
+ addr_mode() == CL_ADDRESS_MIRRORED_REPEAT ? PIPE_TEX_WRAP_MIRROR_REPEAT :
+ PIPE_TEX_WRAP_CLAMP_TO_EDGE);
+
+ info.min_img_filter = info.mag_img_filter =
+ (filter_mode() == CL_FILTER_LINEAR ? PIPE_TEX_FILTER_LINEAR :
+ PIPE_TEX_FILTER_NEAREST);
+
+ return q.pipe->create_sampler_state(q.pipe, &info);
+}
+
+void
+sampler::unbind(command_queue &q, void *st) {
+ q.pipe->delete_sampler_state(q.pipe, st);
+}
diff --git a/src/gallium/frontends/clover/core/sampler.hpp b/src/gallium/frontends/clover/core/sampler.hpp
new file mode 100644
index 00000000000..2632c3067fa
--- /dev/null
+++ b/src/gallium/frontends/clover/core/sampler.hpp
@@ -0,0 +1,58 @@
+//
+// Copyright 2012 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#ifndef CLOVER_CORE_SAMPLER_HPP
+#define CLOVER_CORE_SAMPLER_HPP
+
+#include "core/object.hpp"
+#include "core/queue.hpp"
+
+namespace clover {
+ class sampler : public ref_counter, public _cl_sampler {
+ public:
+ sampler(clover::context &ctx, bool norm_mode,
+ cl_addressing_mode addr_mode,
+ cl_filter_mode filter_mode);
+
+ sampler(const sampler &s) = delete;
+ sampler &
+ operator=(const sampler &s) = delete;
+
+ bool norm_mode();
+ cl_addressing_mode addr_mode();
+ cl_filter_mode filter_mode();
+
+ const intrusive_ref<clover::context> context;
+
+ friend class kernel;
+
+ private:
+ void *bind(command_queue &q);
+ void unbind(command_queue &q, void *st);
+
+ bool _norm_mode;
+ cl_addressing_mode _addr_mode;
+ cl_filter_mode _filter_mode;
+ };
+}
+
+#endif
diff --git a/src/gallium/frontends/clover/core/timestamp.cpp b/src/gallium/frontends/clover/core/timestamp.cpp
new file mode 100644
index 00000000000..3fd341f30da
--- /dev/null
+++ b/src/gallium/frontends/clover/core/timestamp.cpp
@@ -0,0 +1,64 @@
+//
+// Copyright 2013 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#include "core/timestamp.hpp"
+#include "core/queue.hpp"
+#include "pipe/p_screen.h"
+#include "pipe/p_context.h"
+
+using namespace clover;
+
+timestamp::query::query(command_queue &q) :
+ q(q),
+ _query(q.pipe->create_query(q.pipe, PIPE_QUERY_TIMESTAMP, 0)) {
+ q.pipe->end_query(q.pipe, _query);
+}
+
+timestamp::query::query(query &&other) :
+ q(other.q),
+ _query(other._query) {
+ other._query = NULL;
+}
+
+timestamp::query::~query() {
+ if (_query)
+ q().pipe->destroy_query(q().pipe, _query);
+}
+
+cl_ulong
+timestamp::query::operator()() const {
+ pipe_query_result result;
+
+ if (!q().pipe->get_query_result(q().pipe, _query, false, &result))
+ throw error(CL_PROFILING_INFO_NOT_AVAILABLE);
+
+ return result.u64;
+}
+
+timestamp::current::current(command_queue &q) :
+ result(q.pipe->screen->get_timestamp(q.pipe->screen)) {
+}
+
+cl_ulong
+timestamp::current::operator()() const {
+ return result;
+}
diff --git a/src/gallium/frontends/clover/core/timestamp.hpp b/src/gallium/frontends/clover/core/timestamp.hpp
new file mode 100644
index 00000000000..b4b2c83eb92
--- /dev/null
+++ b/src/gallium/frontends/clover/core/timestamp.hpp
@@ -0,0 +1,74 @@
+//
+// Copyright 2013 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#ifndef CLOVER_CORE_TIMESTAMP_HPP
+#define CLOVER_CORE_TIMESTAMP_HPP
+
+#include "core/object.hpp"
+
+struct pipe_query;
+
+namespace clover {
+ class command_queue;
+
+ namespace timestamp {
+ ///
+ /// Emit a timestamp query that is executed asynchronously by
+ /// the command queue \a q.
+ ///
+ class query {
+ public:
+ query(command_queue &q);
+ query(query &&other);
+ ~query();
+
+ query &operator=(const query &) = delete;
+
+ ///
+ /// Retrieve the query results.
+ ///
+ cl_ulong operator()() const;
+
+ private:
+ const intrusive_ref<command_queue> q;
+ pipe_query *_query;
+ };
+
+ ///
+ /// Get the current timestamp value.
+ ///
+ class current {
+ public:
+ current(command_queue &q);
+
+ ///
+ /// Retrieve the query results.
+ ///
+ cl_ulong operator()() const;
+
+ private:
+ cl_ulong result;
+ };
+ }
+}
+
+#endif
diff --git a/src/gallium/frontends/clover/llvm/codegen.hpp b/src/gallium/frontends/clover/llvm/codegen.hpp
new file mode 100644
index 00000000000..e35627c4729
--- /dev/null
+++ b/src/gallium/frontends/clover/llvm/codegen.hpp
@@ -0,0 +1,68 @@
+//
+// Copyright 2016 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+///
+/// \file
+/// Tools to generate various forms of binary code from existing LLVM IR in
+/// the given llvm::Module object and output the result as a clover::module.
+///
+
+#ifndef CLOVER_LLVM_CODEGEN_HPP
+#define CLOVER_LLVM_CODEGEN_HPP
+
+#include "llvm/util.hpp"
+#include "core/module.hpp"
+
+#include <llvm/IR/Module.h>
+
+#include <clang/Frontend/CompilerInstance.h>
+
+namespace clover {
+ namespace llvm {
+ std::string
+ print_module_bitcode(const ::llvm::Module &mod);
+
+ module
+ build_module_library(const ::llvm::Module &mod,
+ enum module::section::type section_type);
+
+ std::unique_ptr< ::llvm::Module>
+ parse_module_library(const module &m, ::llvm::LLVMContext &ctx,
+ std::string &r_log);
+
+ module
+ build_module_native(::llvm::Module &mod, const target &target,
+ const clang::CompilerInstance &c,
+ std::string &r_log);
+
+ std::string
+ print_module_native(const ::llvm::Module &mod, const target &target);
+
+ module
+ build_module_common(const ::llvm::Module &mod,
+ const std::vector<char> &code,
+ const std::map<std::string, unsigned> &offsets,
+ const clang::CompilerInstance &c);
+ }
+}
+
+#endif
diff --git a/src/gallium/frontends/clover/llvm/codegen/bitcode.cpp b/src/gallium/frontends/clover/llvm/codegen/bitcode.cpp
new file mode 100644
index 00000000000..7434e8cf6c9
--- /dev/null
+++ b/src/gallium/frontends/clover/llvm/codegen/bitcode.cpp
@@ -0,0 +1,91 @@
+//
+// Copyright 2012-2016 Francisco Jerez
+// Copyright 2012-2016 Advanced Micro Devices, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+///
+/// \file
+/// Trivial codegen back-end that simply passes through the existing LLVM IR
+/// and either formats it so it can be consumed by pipe drivers (if
+/// build_module_bitcode() is used) or serializes so it can be deserialized at
+/// a later point and passed to the actual codegen back-end (if
+/// build_module_library() / parse_module_library() is used), potentially
+/// after linking against other bitcode object files.
+///
+
+#include "llvm/codegen.hpp"
+#include "llvm/compat.hpp"
+#include "llvm/metadata.hpp"
+#include "core/error.hpp"
+#include "util/algorithm.hpp"
+
+#include <map>
+#include <llvm/Config/llvm-config.h>
+#if LLVM_VERSION_MAJOR < 4
+#include <llvm/Bitcode/ReaderWriter.h>
+#else
+#include <llvm/Bitcode/BitcodeReader.h>
+#include <llvm/Bitcode/BitcodeWriter.h>
+#endif
+#include <llvm/Support/raw_ostream.h>
+
+using namespace clover;
+using namespace clover::llvm;
+
+namespace {
+ std::vector<char>
+ emit_code(const ::llvm::Module &mod) {
+ ::llvm::SmallVector<char, 1024> data;
+ ::llvm::raw_svector_ostream os { data };
+ compat::write_bitcode_to_file(mod, os);
+ return { os.str().begin(), os.str().end() };
+ }
+}
+
+std::string
+clover::llvm::print_module_bitcode(const ::llvm::Module &mod) {
+ std::string s;
+ ::llvm::raw_string_ostream os { s };
+ mod.print(os, NULL);
+ return os.str();
+}
+
+module
+clover::llvm::build_module_library(const ::llvm::Module &mod,
+ enum module::section::type section_type) {
+ module m;
+ const auto code = emit_code(mod);
+ m.secs.emplace_back(0, section_type, code.size(), code);
+ return m;
+}
+
+std::unique_ptr< ::llvm::Module>
+clover::llvm::parse_module_library(const module &m, ::llvm::LLVMContext &ctx,
+ std::string &r_log) {
+ auto mod = ::llvm::parseBitcodeFile(::llvm::MemoryBufferRef(
+ as_string(m.secs[0].data), " "), ctx);
+
+ compat::handle_module_error(mod, [&](const std::string &s) {
+ fail(r_log, error(CL_INVALID_PROGRAM), s);
+ });
+
+ return std::unique_ptr< ::llvm::Module>(std::move(*mod));
+}
diff --git a/src/gallium/frontends/clover/llvm/codegen/common.cpp b/src/gallium/frontends/clover/llvm/codegen/common.cpp
new file mode 100644
index 00000000000..730ba5a2ff7
--- /dev/null
+++ b/src/gallium/frontends/clover/llvm/codegen/common.cpp
@@ -0,0 +1,209 @@
+//
+// Copyright 2012-2016 Francisco Jerez
+// Copyright 2012-2016 Advanced Micro Devices, Inc.
+// Copyright 2015 Zoltan Gilian
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+///
+/// \file
+/// Codegen back-end-independent part of the construction of an executable
+/// clover::module, including kernel argument metadata extraction and
+/// formatting of the pre-generated binary code in a form that can be
+/// understood by pipe drivers.
+///
+
+#include "llvm/codegen.hpp"
+#include "llvm/metadata.hpp"
+
+#include "CL/cl.h"
+
+#include "pipe/p_state.h"
+#include "util/u_math.h"
+
+#include <clang/Basic/TargetInfo.h>
+
+using namespace clover;
+using namespace clover::llvm;
+
+using ::llvm::Module;
+using ::llvm::Function;
+using ::llvm::Type;
+using ::llvm::isa;
+using ::llvm::cast;
+using ::llvm::dyn_cast;
+
+namespace {
+ enum module::argument::type
+ get_image_type(const std::string &type,
+ const std::string &qual) {
+ if (type == "image2d_t" && qual == "read_only")
+ return module::argument::image2d_rd;
+ else if (type == "image2d_t" && qual == "write_only")
+ return module::argument::image2d_wr;
+ else if (type == "image3d_t" && qual == "read_only")
+ return module::argument::image3d_rd;
+ else if (type == "image3d_t" && qual == "write_only")
+ return module::argument::image3d_wr;
+ else
+ unreachable("Unknown image type");
+ }
+
+ std::vector<module::argument>
+ make_kernel_args(const Module &mod, const std::string &kernel_name,
+ const clang::CompilerInstance &c) {
+ std::vector<module::argument> args;
+ const Function &f = *mod.getFunction(kernel_name);
+ ::llvm::DataLayout dl(&mod);
+ const auto size_type =
+ dl.getSmallestLegalIntType(mod.getContext(), sizeof(cl_uint) * 8);
+
+ for (const auto &arg : f.args()) {
+ const auto arg_type = arg.getType();
+
+ // OpenCL 1.2 specification, Ch. 6.1.5: "A built-in data
+ // type that is not a power of two bytes in size must be
+ // aligned to the next larger power of two.
+ // This rule applies to built-in types only, not structs or unions."
+ const unsigned arg_store_size = dl.getTypeStoreSize(arg_type);
+ const unsigned arg_api_size = dl.getTypeAllocSize(arg_type);
+
+ const auto target_type = compat::get_abi_type(arg_type, mod);
+ const unsigned target_size = dl.getTypeStoreSize(target_type);
+ const unsigned target_align = dl.getABITypeAlignment(target_type);
+
+ const auto type_name = get_argument_metadata(f, arg,
+ "kernel_arg_type");
+
+ if (type_name == "image2d_t" || type_name == "image3d_t") {
+ // Image.
+ const auto access_qual = get_argument_metadata(
+ f, arg, "kernel_arg_access_qual");
+ args.emplace_back(get_image_type(type_name, access_qual),
+ arg_store_size, target_size,
+ target_align, module::argument::zero_ext);
+
+ } else if (type_name == "__llvm_image_size") {
+ // Image size implicit argument.
+ args.emplace_back(module::argument::scalar, sizeof(cl_uint),
+ dl.getTypeStoreSize(size_type),
+ dl.getABITypeAlignment(size_type),
+ module::argument::zero_ext,
+ module::argument::image_size);
+
+ } else if (type_name == "__llvm_image_format") {
+ // Image format implicit argument.
+ args.emplace_back(module::argument::scalar, sizeof(cl_uint),
+ dl.getTypeStoreSize(size_type),
+ dl.getABITypeAlignment(size_type),
+ module::argument::zero_ext,
+ module::argument::image_format);
+
+ } else {
+ // Other types.
+ const auto actual_type =
+ isa< ::llvm::PointerType>(arg_type) && arg.hasByValAttr() ?
+ cast< ::llvm::PointerType>(arg_type)->getElementType() : arg_type;
+
+ if (actual_type->isPointerTy()) {
+ const unsigned address_space =
+ cast< ::llvm::PointerType>(actual_type)->getAddressSpace();
+
+ if (address_space == compat::target_address_space(
+ c.getTarget(), clang::LangAS::opencl_local)) {
+ args.emplace_back(module::argument::local, arg_api_size,
+ target_size, target_align,
+ module::argument::zero_ext);
+ } else {
+ // XXX: Correctly handle constant address space. There is no
+ // way for r600g to pass a handle for constant buffers back
+ // to clover like it can for global buffers, so
+ // creating constant arguments will break r600g. For now,
+ // continue treating constant buffers as global buffers
+ // until we can come up with a way to create handles for
+ // constant buffers.
+ args.emplace_back(module::argument::global, arg_api_size,
+ target_size, target_align,
+ module::argument::zero_ext);
+ }
+
+ } else {
+ const bool needs_sign_ext = f.getAttributes().hasAttribute(
+ arg.getArgNo() + 1, ::llvm::Attribute::SExt);
+
+ args.emplace_back(module::argument::scalar, arg_api_size,
+ target_size, target_align,
+ (needs_sign_ext ? module::argument::sign_ext :
+ module::argument::zero_ext));
+ }
+ }
+ }
+
+ // Append implicit arguments. XXX - The types, ordering and
+ // vector size of the implicit arguments should depend on the
+ // target according to the selected calling convention.
+ args.emplace_back(module::argument::scalar, sizeof(cl_uint),
+ dl.getTypeStoreSize(size_type),
+ dl.getABITypeAlignment(size_type),
+ module::argument::zero_ext,
+ module::argument::grid_dimension);
+
+ args.emplace_back(module::argument::scalar, sizeof(cl_uint),
+ dl.getTypeStoreSize(size_type),
+ dl.getABITypeAlignment(size_type),
+ module::argument::zero_ext,
+ module::argument::grid_offset);
+
+ return args;
+ }
+
+ module::section
+ make_text_section(const std::vector<char> &code) {
+ const pipe_binary_program_header header { uint32_t(code.size()) };
+ module::section text { 0, module::section::text_executable,
+ header.num_bytes, {} };
+
+ text.data.insert(text.data.end(), reinterpret_cast<const char *>(&header),
+ reinterpret_cast<const char *>(&header) + sizeof(header));
+ text.data.insert(text.data.end(), code.begin(), code.end());
+
+ return text;
+ }
+}
+
+module
+clover::llvm::build_module_common(const Module &mod,
+ const std::vector<char> &code,
+ const std::map<std::string,
+ unsigned> &offsets,
+ const clang::CompilerInstance &c) {
+ module m;
+
+ for (const auto &llvm_name : map(std::mem_fn(&Function::getName),
+ get_kernels(mod))) {
+ const ::std::string name(llvm_name);
+ if (offsets.count(name))
+ m.syms.emplace_back(name, 0, offsets.at(name),
+ make_kernel_args(mod, name, c));
+ }
+
+ m.secs.push_back(make_text_section(code));
+ return m;
+}
diff --git a/src/gallium/frontends/clover/llvm/codegen/native.cpp b/src/gallium/frontends/clover/llvm/codegen/native.cpp
new file mode 100644
index 00000000000..52346131200
--- /dev/null
+++ b/src/gallium/frontends/clover/llvm/codegen/native.cpp
@@ -0,0 +1,163 @@
+//
+// Copyright 2012-2016 Francisco Jerez
+// Copyright 2012-2016 Advanced Micro Devices, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+///
+/// \file
+/// Generate code using an arbitrary LLVM back-end capable of emitting
+/// executable code as an ELF object file.
+///
+
+#include "llvm/codegen.hpp"
+#include "llvm/compat.hpp"
+#include "llvm/util.hpp"
+#include "core/error.hpp"
+
+#include <llvm/Target/TargetMachine.h>
+#include <llvm/Support/TargetRegistry.h>
+#include <llvm/Transforms/Utils/Cloning.h>
+
+#include <libelf.h>
+#include <gelf.h>
+
+using namespace clover;
+using namespace clover::llvm;
+using ::llvm::TargetMachine;
+
+namespace {
+ namespace elf {
+ std::unique_ptr<Elf, int (*)(Elf *)>
+ get(const std::vector<char> &code) {
+ // One of the libelf implementations
+ // (http://www.mr511.de/software/english.htm) requires calling
+ // elf_version() before elf_memory().
+ elf_version(EV_CURRENT);
+ return { elf_memory(const_cast<char *>(code.data()), code.size()),
+ elf_end };
+ }
+
+ Elf_Scn *
+ get_symbol_table(Elf *elf) {
+ size_t section_str_index;
+ elf_getshdrstrndx(elf, &section_str_index);
+
+ for (Elf_Scn *s = elf_nextscn(elf, NULL); s; s = elf_nextscn(elf, s)) {
+ GElf_Shdr header;
+ if (gelf_getshdr(s, &header) != &header)
+ return nullptr;
+
+ if (!std::strcmp(elf_strptr(elf, section_str_index, header.sh_name),
+ ".symtab"))
+ return s;
+ }
+
+ return nullptr;
+ }
+
+ std::map<std::string, unsigned>
+ get_symbol_offsets(Elf *elf, Elf_Scn *symtab) {
+ Elf_Data *const symtab_data = elf_getdata(symtab, NULL);
+ GElf_Shdr header;
+ if (gelf_getshdr(symtab, &header) != &header)
+ return {};
+
+ std::map<std::string, unsigned> symbol_offsets;
+ GElf_Sym symbol;
+ unsigned i = 0;
+
+ while (GElf_Sym *s = gelf_getsym(symtab_data, i++, &symbol)) {
+ const char *name = elf_strptr(elf, header.sh_link, s->st_name);
+ symbol_offsets[name] = s->st_value;
+ }
+
+ return symbol_offsets;
+ }
+ }
+
+ std::map<std::string, unsigned>
+ get_symbol_offsets(const std::vector<char> &code, std::string &r_log) {
+ const auto elf = elf::get(code);
+ const auto symtab = elf::get_symbol_table(elf.get());
+ if (!symtab)
+ fail(r_log, build_error(), "Unable to find symbol table.");
+
+ return elf::get_symbol_offsets(elf.get(), symtab);
+ }
+
+ std::vector<char>
+ emit_code(::llvm::Module &mod, const target &target,
+ compat::CodeGenFileType ft,
+ std::string &r_log) {
+ std::string err;
+ auto t = ::llvm::TargetRegistry::lookupTarget(target.triple, err);
+ if (!t)
+ fail(r_log, build_error(), err);
+
+ std::unique_ptr<TargetMachine> tm {
+ t->createTargetMachine(target.triple, target.cpu, "", {},
+ ::llvm::None, compat::default_code_model,
+ ::llvm::CodeGenOpt::Default) };
+ if (!tm)
+ fail(r_log, build_error(),
+ "Could not create TargetMachine: " + target.triple);
+
+ ::llvm::SmallVector<char, 1024> data;
+
+ {
+ ::llvm::legacy::PassManager pm;
+ ::llvm::raw_svector_ostream os { data };
+
+ mod.setDataLayout(tm->createDataLayout());
+ tm->Options.MCOptions.AsmVerbose =
+ (ft == compat::CGFT_AssemblyFile);
+
+ if (compat::add_passes_to_emit_file(*tm, pm, os, ft))
+ fail(r_log, build_error(), "TargetMachine can't emit this file");
+
+ pm.run(mod);
+ }
+
+ return { data.begin(), data.end() };
+ }
+}
+
+module
+clover::llvm::build_module_native(::llvm::Module &mod, const target &target,
+ const clang::CompilerInstance &c,
+ std::string &r_log) {
+ const auto code = emit_code(mod, target,
+ compat::CGFT_ObjectFile, r_log);
+ return build_module_common(mod, code, get_symbol_offsets(code, r_log), c);
+}
+
+std::string
+clover::llvm::print_module_native(const ::llvm::Module &mod,
+ const target &target) {
+ std::string log;
+ try {
+ std::unique_ptr< ::llvm::Module> cmod { compat::clone_module(mod) };
+ return as_string(emit_code(*cmod, target,
+ compat::CGFT_AssemblyFile, log));
+ } catch (...) {
+ return "Couldn't output native disassembly: " + log;
+ }
+}
diff --git a/src/gallium/frontends/clover/llvm/compat.hpp b/src/gallium/frontends/clover/llvm/compat.hpp
new file mode 100644
index 00000000000..51902739acc
--- /dev/null
+++ b/src/gallium/frontends/clover/llvm/compat.hpp
@@ -0,0 +1,216 @@
+//
+// Copyright 2016 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+///
+/// \file
+/// Some thin wrappers around the Clang/LLVM API used to preserve
+/// compatibility with older API versions while keeping the ifdef clutter low
+/// in the rest of the clover::llvm subtree. In case of an API break please
+/// consider whether it's possible to preserve backwards compatibility by
+/// introducing a new one-liner inline function or typedef here under the
+/// compat namespace in order to keep the running code free from preprocessor
+/// conditionals.
+///
+
+#ifndef CLOVER_LLVM_COMPAT_HPP
+#define CLOVER_LLVM_COMPAT_HPP
+
+#include "util/algorithm.hpp"
+
+#include <llvm/Config/llvm-config.h>
+#if LLVM_VERSION_MAJOR < 4
+#include <llvm/Bitcode/ReaderWriter.h>
+#else
+#include <llvm/Bitcode/BitcodeReader.h>
+#include <llvm/Bitcode/BitcodeWriter.h>
+#endif
+
+#include <llvm/IR/LLVMContext.h>
+#include <llvm/Linker/Linker.h>
+#include <llvm/Transforms/IPO.h>
+#include <llvm/Transforms/Utils/Cloning.h>
+#include <llvm/Target/TargetMachine.h>
+#if LLVM_VERSION_MAJOR >= 4
+#include <llvm/Support/Error.h>
+#else
+#include <llvm/Support/ErrorOr.h>
+#endif
+
+#include <llvm/IR/LegacyPassManager.h>
+#include <llvm/Analysis/TargetLibraryInfo.h>
+
+#include <clang/Basic/TargetInfo.h>
+#include <clang/Frontend/CompilerInstance.h>
+
+#if LLVM_VERSION_MAJOR >= 8
+#include <clang/Basic/CodeGenOptions.h>
+#else
+#include <clang/Frontend/CodeGenOptions.h>
+#endif
+
+#if LLVM_VERSION_MAJOR >= 10
+#include <llvm/Support/CodeGen.h>
+#endif
+
+namespace clover {
+ namespace llvm {
+ namespace compat {
+
+#if LLVM_VERSION_MAJOR >= 10
+ const auto CGFT_ObjectFile = ::llvm::CGFT_ObjectFile;
+ const auto CGFT_AssemblyFile = ::llvm::CGFT_AssemblyFile;
+ typedef ::llvm::CodeGenFileType CodeGenFileType;
+#else
+ const auto CGFT_ObjectFile = ::llvm::TargetMachine::CGFT_ObjectFile;
+ const auto CGFT_AssemblyFile =
+ ::llvm::TargetMachine::CGFT_AssemblyFile;
+ typedef ::llvm::TargetMachine::CodeGenFileType CodeGenFileType;
+#endif
+
+ template<typename T, typename AS>
+ unsigned target_address_space(const T &target, const AS lang_as) {
+ const auto &map = target.getAddressSpaceMap();
+#if LLVM_VERSION_MAJOR >= 5
+ return map[static_cast<unsigned>(lang_as)];
+#else
+ return map[lang_as - clang::LangAS::Offset];
+#endif
+ }
+
+#if LLVM_VERSION_MAJOR >= 10
+ const clang::InputKind ik_opencl = clang::Language::OpenCL;
+#elif LLVM_VERSION_MAJOR >= 5
+ const clang::InputKind ik_opencl = clang::InputKind::OpenCL;
+#else
+ const clang::InputKind ik_opencl = clang::IK_OpenCL;
+#endif
+
+#if LLVM_VERSION_MAJOR >= 5
+ const clang::LangStandard::Kind lang_opencl10 = clang::LangStandard::lang_opencl10;
+#else
+ const clang::LangStandard::Kind lang_opencl10 = clang::LangStandard::lang_opencl;
+#endif
+
+ inline void
+ add_link_bitcode_file(clang::CodeGenOptions &opts,
+ const std::string &path) {
+#if LLVM_VERSION_MAJOR >= 5
+ clang::CodeGenOptions::BitcodeFileToLink F;
+
+ F.Filename = path;
+ F.PropagateAttrs = true;
+ F.LinkFlags = ::llvm::Linker::Flags::None;
+ opts.LinkBitcodeFiles.emplace_back(F);
+#else
+ opts.LinkBitcodeFiles.emplace_back(::llvm::Linker::Flags::None, path);
+#endif
+ }
+
+#if LLVM_VERSION_MAJOR >= 6
+ const auto default_code_model = ::llvm::None;
+#else
+ const auto default_code_model = ::llvm::CodeModel::Default;
+#endif
+
+ template<typename M, typename F> void
+ handle_module_error(M &mod, const F &f) {
+#if LLVM_VERSION_MAJOR >= 4
+ if (::llvm::Error err = mod.takeError())
+ ::llvm::handleAllErrors(std::move(err), [&](::llvm::ErrorInfoBase &eib) {
+ f(eib.message());
+ });
+#else
+ if (!mod)
+ f(mod.getError().message());
+#endif
+ }
+
+ template<typename T> void
+ set_diagnostic_handler(::llvm::LLVMContext &ctx,
+ T *diagnostic_handler, void *data) {
+#if LLVM_VERSION_MAJOR >= 6
+ ctx.setDiagnosticHandlerCallBack(diagnostic_handler, data);
+#else
+ ctx.setDiagnosticHandler(diagnostic_handler, data);
+#endif
+ }
+
+ inline std::unique_ptr< ::llvm::Module>
+ clone_module(const ::llvm::Module &mod)
+ {
+#if LLVM_VERSION_MAJOR >= 7
+ return ::llvm::CloneModule(mod);
+#else
+ return ::llvm::CloneModule(&mod);
+#endif
+ }
+
+ template<typename T> void
+ write_bitcode_to_file(const ::llvm::Module &mod, T &os)
+ {
+#if LLVM_VERSION_MAJOR >= 7
+ ::llvm::WriteBitcodeToFile(mod, os);
+#else
+ ::llvm::WriteBitcodeToFile(&mod, os);
+#endif
+ }
+
+ template<typename TM, typename PM, typename OS, typename FT>
+ bool add_passes_to_emit_file(TM &tm, PM &pm, OS &os, FT &ft)
+ {
+#if LLVM_VERSION_MAJOR >= 7
+ return tm.addPassesToEmitFile(pm, os, nullptr, ft);
+#else
+ return tm.addPassesToEmitFile(pm, os, ft);
+#endif
+ }
+
+ template<typename T> inline bool
+ create_compiler_invocation_from_args(clang::CompilerInvocation &cinv,
+ T copts,
+ clang::DiagnosticsEngine &diag)
+ {
+#if LLVM_VERSION_MAJOR >= 10
+ return clang::CompilerInvocation::CreateFromArgs(
+ cinv, copts, diag);
+#else
+ return clang::CompilerInvocation::CreateFromArgs(
+ cinv, copts.data(), copts.data() + copts.size(), diag);
+#endif
+ }
+
+ template<typename T, typename M>
+ T get_abi_type(const T &arg_type, const M &mod) {
+#if LLVM_VERSION_MAJOR >= 7
+ return arg_type;
+#else
+ ::llvm::DataLayout dl(&mod);
+ const unsigned arg_store_size = dl.getTypeStoreSize(arg_type);
+ return !arg_type->isIntegerTy() ? arg_type :
+ dl.getSmallestLegalIntType(mod.getContext(), arg_store_size * 8);
+#endif
+ }
+ }
+ }
+}
+
+#endif
diff --git a/src/gallium/frontends/clover/llvm/invocation.cpp b/src/gallium/frontends/clover/llvm/invocation.cpp
new file mode 100644
index 00000000000..95a9d036622
--- /dev/null
+++ b/src/gallium/frontends/clover/llvm/invocation.cpp
@@ -0,0 +1,465 @@
+//
+// Copyright 2012-2016 Francisco Jerez
+// Copyright 2012-2016 Advanced Micro Devices, Inc.
+// Copyright 2014-2016 Jan Vesely
+// Copyright 2014-2015 Serge Martin
+// Copyright 2015 Zoltan Gilian
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#include <llvm/IR/DiagnosticPrinter.h>
+#include <llvm/IR/DiagnosticInfo.h>
+#include <llvm/IR/LLVMContext.h>
+#include <llvm/Support/raw_ostream.h>
+#include <llvm/Transforms/IPO/PassManagerBuilder.h>
+#include <llvm-c/Target.h>
+#ifdef HAVE_CLOVER_SPIRV
+#include <LLVMSPIRVLib/LLVMSPIRVLib.h>
+#endif
+
+#include <clang/CodeGen/CodeGenAction.h>
+#include <clang/Lex/PreprocessorOptions.h>
+#include <clang/Frontend/TextDiagnosticBuffer.h>
+#include <clang/Frontend/TextDiagnosticPrinter.h>
+#include <clang/Basic/TargetInfo.h>
+
+// We need to include internal headers last, because the internal headers
+// include CL headers which have #define's like:
+//
+//#define cl_khr_gl_sharing 1
+//#define cl_khr_icd 1
+//
+// Which will break the compilation of clang/Basic/OpenCLOptions.h
+
+#include "core/error.hpp"
+#include "llvm/codegen.hpp"
+#include "llvm/compat.hpp"
+#include "llvm/invocation.hpp"
+#include "llvm/metadata.hpp"
+#include "llvm/util.hpp"
+#ifdef HAVE_CLOVER_SPIRV
+#include "spirv/invocation.hpp"
+#endif
+#include "util/algorithm.hpp"
+
+
+using namespace clover;
+using namespace clover::llvm;
+
+using ::llvm::Function;
+using ::llvm::LLVMContext;
+using ::llvm::Module;
+using ::llvm::raw_string_ostream;
+
+namespace {
+
+ struct cl_version {
+ std::string version_str; // CL Version
+ unsigned version_number; // Numeric CL Version
+ };
+
+ static const unsigned ANY_VERSION = 999;
+ const cl_version cl_versions[] = {
+ { "1.0", 100},
+ { "1.1", 110},
+ { "1.2", 120},
+ { "2.0", 200},
+ { "2.1", 210},
+ { "2.2", 220},
+ };
+
+ struct clc_version_lang_std {
+ unsigned version_number; // CLC Version
+ clang::LangStandard::Kind clc_lang_standard;
+ };
+
+ const clc_version_lang_std cl_version_lang_stds[] = {
+ { 100, compat::lang_opencl10},
+ { 110, clang::LangStandard::lang_opencl11},
+ { 120, clang::LangStandard::lang_opencl12},
+ { 200, clang::LangStandard::lang_opencl20},
+ };
+
+ void
+ init_targets() {
+ static bool targets_initialized = false;
+ if (!targets_initialized) {
+ LLVMInitializeAllTargets();
+ LLVMInitializeAllTargetInfos();
+ LLVMInitializeAllTargetMCs();
+ LLVMInitializeAllAsmParsers();
+ LLVMInitializeAllAsmPrinters();
+ targets_initialized = true;
+ }
+ }
+
+ void
+ diagnostic_handler(const ::llvm::DiagnosticInfo &di, void *data) {
+ if (di.getSeverity() == ::llvm::DS_Error) {
+ raw_string_ostream os { *reinterpret_cast<std::string *>(data) };
+ ::llvm::DiagnosticPrinterRawOStream printer { os };
+ di.print(printer);
+ throw build_error();
+ }
+ }
+
+ std::unique_ptr<LLVMContext>
+ create_context(std::string &r_log) {
+ init_targets();
+ std::unique_ptr<LLVMContext> ctx { new LLVMContext };
+ compat::set_diagnostic_handler(*ctx, diagnostic_handler, &r_log);
+ return ctx;
+ }
+
+ const struct clc_version_lang_std&
+ get_cl_lang_standard(unsigned requested, unsigned max = ANY_VERSION) {
+ for (const struct clc_version_lang_std &version : cl_version_lang_stds) {
+ if (version.version_number == max ||
+ version.version_number == requested) {
+ return version;
+ }
+ }
+ throw build_error("Unknown/Unsupported language version");
+ }
+
+ const struct cl_version&
+ get_cl_version(const std::string &version_str,
+ unsigned max = ANY_VERSION) {
+ for (const struct cl_version &version : cl_versions) {
+ if (version.version_number == max || version.version_str == version_str) {
+ return version;
+ }
+ }
+ throw build_error("Unknown/Unsupported language version");
+ }
+
+ clang::LangStandard::Kind
+ get_lang_standard_from_version_str(const std::string &version_str,
+ bool is_build_opt = false) {
+
+ //Per CL 2.0 spec, section 5.8.4.5:
+ // If it's an option, use the value directly.
+ // If it's a device version, clamp to max 1.x version, a.k.a. 1.2
+ const cl_version version =
+ get_cl_version(version_str, is_build_opt ? ANY_VERSION : 120);
+
+ const struct clc_version_lang_std standard =
+ get_cl_lang_standard(version.version_number);
+
+ return standard.clc_lang_standard;
+ }
+
+ clang::LangStandard::Kind
+ get_language_version(const std::vector<std::string> &opts,
+ const std::string &device_version) {
+
+ const std::string search = "-cl-std=CL";
+
+ for (auto &opt: opts) {
+ auto pos = opt.find(search);
+ if (pos == 0){
+ const auto ver = opt.substr(pos + search.size());
+ const auto device_ver = get_cl_version(device_version);
+ const auto requested = get_cl_version(ver);
+ if (requested.version_number > device_ver.version_number) {
+ throw build_error();
+ }
+ return get_lang_standard_from_version_str(ver, true);
+ }
+ }
+
+ return get_lang_standard_from_version_str(device_version);
+ }
+
+ std::unique_ptr<clang::CompilerInstance>
+ create_compiler_instance(const device &dev, const std::string& ir_target,
+ const std::vector<std::string> &opts,
+ std::string &r_log) {
+ std::unique_ptr<clang::CompilerInstance> c { new clang::CompilerInstance };
+ clang::TextDiagnosticBuffer *diag_buffer = new clang::TextDiagnosticBuffer;
+ clang::DiagnosticsEngine diag { new clang::DiagnosticIDs,
+ new clang::DiagnosticOptions, diag_buffer };
+
+ // Parse the compiler options. A file name should be present at the end
+ // and must have the .cl extension in order for the CompilerInvocation
+ // class to recognize it as an OpenCL source file.
+ const std::vector<const char *> copts =
+ map(std::mem_fn(&std::string::c_str), opts);
+
+ const target &target = ir_target;
+ const std::string &device_clc_version = dev.device_clc_version();
+
+ if (!compat::create_compiler_invocation_from_args(
+ c->getInvocation(), copts, diag))
+ throw invalid_build_options_error();
+
+ diag_buffer->FlushDiagnostics(diag);
+ if (diag.hasErrorOccurred())
+ throw invalid_build_options_error();
+
+ c->getTargetOpts().CPU = target.cpu;
+ c->getTargetOpts().Triple = target.triple;
+ c->getLangOpts().NoBuiltin = true;
+
+ // This is a workaround for a Clang bug which causes the number
+ // of warnings and errors to be printed to stderr.
+ // http://www.llvm.org/bugs/show_bug.cgi?id=19735
+ c->getDiagnosticOpts().ShowCarets = false;
+
+ c->getInvocation().setLangDefaults(c->getLangOpts(),
+ compat::ik_opencl, ::llvm::Triple(target.triple),
+ c->getPreprocessorOpts(),
+ get_language_version(opts, device_clc_version));
+
+ c->createDiagnostics(new clang::TextDiagnosticPrinter(
+ *new raw_string_ostream(r_log),
+ &c->getDiagnosticOpts(), true));
+
+ c->setTarget(clang::TargetInfo::CreateTargetInfo(
+ c->getDiagnostics(), c->getInvocation().TargetOpts));
+
+ return c;
+ }
+
+ std::unique_ptr<Module>
+ compile(LLVMContext &ctx, clang::CompilerInstance &c,
+ const std::string &name, const std::string &source,
+ const header_map &headers, const device &dev,
+ const std::string &opts, bool use_libclc, std::string &r_log) {
+ c.getFrontendOpts().ProgramAction = clang::frontend::EmitLLVMOnly;
+ c.getHeaderSearchOpts().UseBuiltinIncludes = true;
+ c.getHeaderSearchOpts().UseStandardSystemIncludes = true;
+ c.getHeaderSearchOpts().ResourceDir = CLANG_RESOURCE_DIR;
+
+ if (use_libclc) {
+ // Add libclc generic search path
+ c.getHeaderSearchOpts().AddPath(LIBCLC_INCLUDEDIR,
+ clang::frontend::Angled,
+ false, false);
+
+ // Add libclc include
+ c.getPreprocessorOpts().Includes.push_back("clc/clc.h");
+ } else {
+ // Add opencl-c generic search path
+ c.getHeaderSearchOpts().AddPath(CLANG_RESOURCE_DIR,
+ clang::frontend::Angled,
+ false, false);
+
+ // Add opencl include
+ c.getPreprocessorOpts().Includes.push_back("opencl-c.h");
+ }
+
+ // Add definition for the OpenCL version
+ c.getPreprocessorOpts().addMacroDef("__OPENCL_VERSION__=" +
+ std::to_string(get_cl_version(
+ dev.device_version()).version_number));
+
+ // clc.h requires that this macro be defined:
+ c.getPreprocessorOpts().addMacroDef("cl_clang_storage_class_specifiers");
+ c.getPreprocessorOpts().addRemappedFile(
+ name, ::llvm::MemoryBuffer::getMemBuffer(source).release());
+
+ if (headers.size()) {
+ const std::string tmp_header_path = "/tmp/clover/";
+
+ c.getHeaderSearchOpts().AddPath(tmp_header_path,
+ clang::frontend::Angled,
+ false, false);
+
+ for (const auto &header : headers)
+ c.getPreprocessorOpts().addRemappedFile(
+ tmp_header_path + header.first,
+ ::llvm::MemoryBuffer::getMemBuffer(header.second).release());
+ }
+
+ // Tell clang to link this file before performing any
+ // optimizations. This is required so that we can replace calls
+ // to the OpenCL C barrier() builtin with calls to target
+ // intrinsics that have the noduplicate attribute. This
+ // attribute will prevent Clang from creating illegal uses of
+ // barrier() (e.g. Moving barrier() inside a conditional that is
+ // no executed by all threads) during its optimizaton passes.
+ if (use_libclc)
+ compat::add_link_bitcode_file(c.getCodeGenOpts(),
+ LIBCLC_LIBEXECDIR + dev.ir_target() + ".bc");
+
+ // Compile the code
+ clang::EmitLLVMOnlyAction act(&ctx);
+ if (!c.ExecuteAction(act))
+ throw build_error();
+
+ return act.takeModule();
+ }
+}
+
+module
+clover::llvm::compile_program(const std::string &source,
+ const header_map &headers,
+ const device &dev,
+ const std::string &opts,
+ std::string &r_log) {
+ if (has_flag(debug::clc))
+ debug::log(".cl", "// Options: " + opts + '\n' + source);
+
+ auto ctx = create_context(r_log);
+ auto c = create_compiler_instance(dev, dev.ir_target(),
+ tokenize(opts + " input.cl"), r_log);
+ auto mod = compile(*ctx, *c, "input.cl", source, headers, dev, opts, true,
+ r_log);
+
+ if (has_flag(debug::llvm))
+ debug::log(".ll", print_module_bitcode(*mod));
+
+ return build_module_library(*mod, module::section::text_intermediate);
+}
+
+namespace {
+ void
+ optimize(Module &mod, unsigned optimization_level,
+ bool internalize_symbols) {
+ ::llvm::legacy::PassManager pm;
+
+ // By default, the function internalizer pass will look for a function
+ // called "main" and then mark all other functions as internal. Marking
+ // functions as internal enables the optimizer to perform optimizations
+ // like function inlining and global dead-code elimination.
+ //
+ // When there is no "main" function in a module, the internalize pass will
+ // treat the module like a library, and it won't internalize any functions.
+ // Since there is no "main" function in our kernels, we need to tell
+ // the internalizer pass that this module is not a library by passing a
+ // list of kernel functions to the internalizer. The internalizer will
+ // treat the functions in the list as "main" functions and internalize
+ // all of the other functions.
+ if (internalize_symbols) {
+ std::vector<std::string> names =
+ map(std::mem_fn(&Function::getName), get_kernels(mod));
+ pm.add(::llvm::createInternalizePass(
+ [=](const ::llvm::GlobalValue &gv) {
+ return std::find(names.begin(), names.end(),
+ gv.getName()) != names.end();
+ }));
+ }
+
+ ::llvm::PassManagerBuilder pmb;
+ pmb.OptLevel = optimization_level;
+ pmb.LibraryInfo = new ::llvm::TargetLibraryInfoImpl(
+ ::llvm::Triple(mod.getTargetTriple()));
+ pmb.populateModulePassManager(pm);
+ pm.run(mod);
+ }
+
+ std::unique_ptr<Module>
+ link(LLVMContext &ctx, const clang::CompilerInstance &c,
+ const std::vector<module> &modules, std::string &r_log) {
+ std::unique_ptr<Module> mod { new Module("link", ctx) };
+ std::unique_ptr< ::llvm::Linker> linker { new ::llvm::Linker(*mod) };
+
+ for (auto &m : modules) {
+ if (linker->linkInModule(parse_module_library(m, ctx, r_log)))
+ throw build_error();
+ }
+
+ return mod;
+ }
+}
+
+module
+clover::llvm::link_program(const std::vector<module> &modules,
+ const device &dev, const std::string &opts,
+ std::string &r_log) {
+ std::vector<std::string> options = tokenize(opts + " input.cl");
+ const bool create_library = count("-create-library", options);
+ erase_if(equals("-create-library"), options);
+
+ auto ctx = create_context(r_log);
+ auto c = create_compiler_instance(dev, dev.ir_target(), options, r_log);
+ auto mod = link(*ctx, *c, modules, r_log);
+
+ optimize(*mod, c->getCodeGenOpts().OptimizationLevel, !create_library);
+
+ static std::atomic_uint seq(0);
+ const std::string id = "." + mod->getModuleIdentifier() + "-" +
+ std::to_string(seq++);
+
+ if (has_flag(debug::llvm))
+ debug::log(id + ".ll", print_module_bitcode(*mod));
+
+ if (create_library) {
+ return build_module_library(*mod, module::section::text_library);
+
+ } else if (dev.ir_format() == PIPE_SHADER_IR_NATIVE) {
+ if (has_flag(debug::native))
+ debug::log(id + ".asm", print_module_native(*mod, dev.ir_target()));
+
+ return build_module_native(*mod, dev.ir_target(), *c, r_log);
+
+ } else {
+ unreachable("Unsupported IR.");
+ }
+}
+
+#ifdef HAVE_CLOVER_SPIRV
+module
+clover::llvm::compile_to_spirv(const std::string &source,
+ const header_map &headers,
+ const device &dev,
+ const std::string &opts,
+ std::string &r_log) {
+ if (has_flag(debug::clc))
+ debug::log(".cl", "// Options: " + opts + '\n' + source);
+
+ auto ctx = create_context(r_log);
+ const std::string target = dev.address_bits() == 32u ?
+ "-spir-unknown-unknown" :
+ "-spir64-unknown-unknown";
+ auto c = create_compiler_instance(dev, target,
+ tokenize(opts + " input.cl"), r_log);
+ auto mod = compile(*ctx, *c, "input.cl", source, headers, dev, opts, false,
+ r_log);
+
+ if (has_flag(debug::llvm))
+ debug::log(".ll", print_module_bitcode(*mod));
+
+ std::string error_msg;
+ if (!::llvm::regularizeLlvmForSpirv(mod.get(), error_msg)) {
+ r_log += "Failed to regularize LLVM IR for SPIR-V: " + error_msg + ".\n";
+ throw error(CL_INVALID_VALUE);
+ }
+
+ std::ostringstream os;
+ if (!::llvm::writeSpirv(mod.get(), os, error_msg)) {
+ r_log += "Translation from LLVM IR to SPIR-V failed: " + error_msg + ".\n";
+ throw error(CL_INVALID_VALUE);
+ }
+
+ const std::string osContent = os.str();
+ std::vector<char> binary(osContent.begin(), osContent.end());
+ if (binary.empty()) {
+ r_log += "Failed to retrieve SPIR-V binary.\n";
+ throw error(CL_INVALID_VALUE);
+ }
+
+ if (has_flag(debug::spirv))
+ debug::log(".spvasm", spirv::print_module(binary, dev.device_version()));
+
+ return spirv::compile_program(binary, dev, r_log);
+}
+#endif
diff --git a/src/gallium/frontends/clover/llvm/invocation.hpp b/src/gallium/frontends/clover/llvm/invocation.hpp
new file mode 100644
index 00000000000..1f0e9db2cf7
--- /dev/null
+++ b/src/gallium/frontends/clover/llvm/invocation.hpp
@@ -0,0 +1,54 @@
+//
+// Copyright 2016 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#ifndef CLOVER_LLVM_INVOCATION_HPP
+#define CLOVER_LLVM_INVOCATION_HPP
+
+#include "core/error.hpp"
+#include "core/module.hpp"
+#include "core/program.hpp"
+#include "pipe/p_defines.h"
+
+namespace clover {
+ namespace llvm {
+ module compile_program(const std::string &source,
+ const header_map &headers,
+ const device &device,
+ const std::string &opts,
+ std::string &r_log);
+
+ module link_program(const std::vector<module> &modules,
+ const device &device,
+ const std::string &opts,
+ std::string &r_log);
+
+#ifdef HAVE_CLOVER_SPIRV
+ module compile_to_spirv(const std::string &source,
+ const header_map &headers,
+ const device &dev,
+ const std::string &opts,
+ std::string &r_log);
+#endif
+ }
+}
+
+#endif
diff --git a/src/gallium/frontends/clover/llvm/metadata.hpp b/src/gallium/frontends/clover/llvm/metadata.hpp
new file mode 100644
index 00000000000..58042f4b4da
--- /dev/null
+++ b/src/gallium/frontends/clover/llvm/metadata.hpp
@@ -0,0 +1,86 @@
+//
+// Copyright 2016 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+///
+/// \file
+/// Utility functions for LLVM IR metadata introspection.
+///
+
+#ifndef CLOVER_LLVM_METADATA_HPP
+#define CLOVER_LLVM_METADATA_HPP
+
+#include "llvm/compat.hpp"
+#include "util/algorithm.hpp"
+
+#include <vector>
+#include <llvm/Config/llvm-config.h>
+#include <llvm/IR/Module.h>
+#include <llvm/IR/Metadata.h>
+
+namespace clover {
+ namespace llvm {
+ namespace detail {
+ inline bool
+ is_kernel(const ::llvm::Function &f) {
+ return f.getMetadata("kernel_arg_type");
+ }
+
+ inline iterator_range< ::llvm::MDNode::op_iterator>
+ get_kernel_metadata_operands(const ::llvm::Function &f,
+ const std::string &name) {
+ const auto data_node = f.getMetadata(name);
+ return range(data_node->op_begin(), data_node->op_end());
+ }
+ }
+
+ ///
+ /// Extract the string metadata node \p name corresponding to the kernel
+ /// argument given by \p arg.
+ ///
+ inline std::string
+ get_argument_metadata(const ::llvm::Function &f,
+ const ::llvm::Argument &arg,
+ const std::string &name) {
+ return ::llvm::cast< ::llvm::MDString>(
+ detail::get_kernel_metadata_operands(f, name)[arg.getArgNo()])
+ ->getString().str();
+ }
+
+ ///
+ /// Return a vector with all CL kernel functions found in the LLVM
+ /// module \p mod.
+ ///
+ inline std::vector<const ::llvm::Function *>
+ get_kernels(const ::llvm::Module &mod) {
+ std::vector<const ::llvm::Function *> fs;
+
+ for (auto &f : mod.getFunctionList()) {
+ if (detail::is_kernel(f))
+ fs.push_back(&f);
+ }
+
+ return fs;
+ }
+ }
+}
+
+#endif
diff --git a/src/gallium/frontends/clover/llvm/util.hpp b/src/gallium/frontends/clover/llvm/util.hpp
new file mode 100644
index 00000000000..02e73e65071
--- /dev/null
+++ b/src/gallium/frontends/clover/llvm/util.hpp
@@ -0,0 +1,137 @@
+//
+// Copyright 2012-2016 Francisco Jerez
+// Copyright 2012-2016 Advanced Micro Devices, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#ifndef CLOVER_LLVM_UTIL_HPP
+#define CLOVER_LLVM_UTIL_HPP
+
+#include "core/error.hpp"
+#include "util/u_debug.h"
+
+#include <vector>
+#include <fstream>
+#include <iostream>
+#include <sstream>
+
+namespace clover {
+ namespace llvm {
+ template<typename E> void
+ fail(std::string &r_log, E &&e, const std::string &s) {
+ r_log += s;
+ throw e;
+ }
+
+ inline std::vector<std::string>
+ tokenize(const std::string &s) {
+ std::vector<std::string> ss;
+ std::ostringstream oss;
+
+ // OpenCL programs can pass a quoted argument, most frequently the
+ // include path. This is useful so that path containing spaces is
+ // treated as a single argument instead of being split by the spaces.
+ // Additionally, the argument should also be unquoted before being
+ // passed to the compiler. We avoid using std::string::replace here to
+ // remove quotes, as the single and double quote characters can be a
+ // part of the file name.
+ bool escape_next = false;
+ bool in_quote_double = false;
+ bool in_quote_single = false;
+
+ for (auto c : s) {
+ if (escape_next) {
+ oss.put(c);
+ escape_next = false;
+ } else if (c == '\\') {
+ escape_next = true;
+ } else if (c == '"' && !in_quote_single) {
+ in_quote_double = !in_quote_double;
+ } else if (c == '\'' && !in_quote_double) {
+ in_quote_single = !in_quote_single;
+ } else if (c != ' ' || in_quote_single || in_quote_double) {
+ oss.put(c);
+ } else if (oss.tellp() > 0) {
+ ss.emplace_back(oss.str());
+ oss.str("");
+ }
+ }
+
+ if (oss.tellp() > 0)
+ ss.emplace_back(oss.str());
+
+ if (in_quote_double || in_quote_single)
+ throw invalid_build_options_error();
+
+ return ss;
+ }
+
+ inline std::string
+ as_string(const std::vector<char> &v) {
+ return { v.begin(), v.end() };
+ }
+
+ struct target {
+ target(const std::string &s) :
+ cpu(s.begin(), s.begin() + s.find_first_of("-")),
+ triple(s.begin() + s.find_first_of("-") + 1, s.end()) {}
+
+ std::string cpu;
+ std::string triple;
+ };
+
+ namespace debug {
+ enum flag {
+ clc = 1 << 0,
+ llvm = 1 << 1,
+ native = 1 << 2,
+ spirv = 1 << 3,
+ };
+
+ inline bool
+ has_flag(flag f) {
+ static const struct debug_named_value debug_options[] = {
+ { "clc", clc, "Dump the OpenCL C code for all kernels." },
+ { "llvm", llvm, "Dump the generated LLVM IR for all kernels." },
+ { "native", native, "Dump kernel assembly code for targets "
+ "specifying PIPE_SHADER_IR_NATIVE" },
+ { "spirv", spirv, "Dump the generated SPIR-V for all kernels." },
+ DEBUG_NAMED_VALUE_END
+ };
+ static const unsigned flags =
+ debug_get_flags_option("CLOVER_DEBUG", debug_options, 0);
+
+ return flags & f;
+ }
+
+ inline void
+ log(const std::string &suffix, const std::string &s) {
+ const std::string path = debug_get_option("CLOVER_DEBUG_FILE",
+ "stderr");
+ if (path == "stderr")
+ std::cerr << s;
+ else
+ std::ofstream(path + suffix, std::ios::app) << s;
+ }
+ }
+ }
+}
+
+#endif
diff --git a/src/gallium/frontends/clover/meson.build b/src/gallium/frontends/clover/meson.build
new file mode 100644
index 00000000000..7606a6beaf6
--- /dev/null
+++ b/src/gallium/frontends/clover/meson.build
@@ -0,0 +1,164 @@
+# Copyright © 2017-2018 Intel Corporation
+
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+clover_cpp_args = []
+clover_opencl_cpp_args = [
+ '-DCL_TARGET_OPENCL_VERSION=220',
+ '-DCL_USE_DEPRECATED_OPENCL_1_0_APIS',
+ '-DCL_USE_DEPRECATED_OPENCL_1_1_APIS',
+ '-DCL_USE_DEPRECATED_OPENCL_1_2_APIS',
+ '-DCL_USE_DEPRECATED_OPENCL_2_0_APIS',
+ '-DCL_USE_DEPRECATED_OPENCL_2_1_APIS'
+]
+clover_spirv_cpp_args = []
+clover_incs = [inc_include, inc_src, inc_gallium, inc_gallium_aux]
+
+# the CL header files declare attributes on the CL types. Compilers warn if
+# we use them as template arguments. Disable the warning as there isn't
+# anything we can do about it
+if cpp.has_argument('-Wno-ignored-attributes')
+ clover_cpp_args += '-Wno-ignored-attributes'
+endif
+
+if with_opencl_icd
+ clover_cpp_args += '-DHAVE_CLOVER_ICD'
+endif
+
+if with_opencl_spirv
+ clover_spirv_cpp_args += '-DHAVE_CLOVER_SPIRV'
+endif
+
+libclllvm = static_library(
+ 'clllvm',
+ files(
+ 'llvm/codegen/bitcode.cpp',
+ 'llvm/codegen/common.cpp',
+ 'llvm/codegen/native.cpp',
+ 'llvm/codegen.hpp',
+ 'llvm/compat.hpp',
+ 'llvm/invocation.cpp',
+ 'llvm/invocation.hpp',
+ 'llvm/metadata.hpp',
+ 'llvm/util.hpp',
+ ),
+ include_directories : clover_incs,
+ cpp_args : [
+ clover_cpp_args,
+ clover_opencl_cpp_args,
+ clover_spirv_cpp_args,
+ cpp_vis_args,
+ '-DLIBCLC_INCLUDEDIR="@0@/"'.format(dep_clc.get_pkgconfig_variable('includedir')),
+ '-DLIBCLC_LIBEXECDIR="@0@/"'.format(dep_clc.get_pkgconfig_variable('libexecdir')),
+ '-DCLANG_RESOURCE_DIR="@0@"'.format(join_paths(
+ dep_llvm.get_configtool_variable('libdir'), 'clang',
+ dep_llvm.version(), 'include',
+ )),
+ ],
+ dependencies : [dep_llvm, dep_elf, dep_llvmspirvlib],
+ override_options : clover_cpp_std,
+)
+
+libclspirv = static_library(
+ 'clspirv',
+ files('spirv/invocation.cpp', 'spirv/invocation.hpp'),
+ include_directories : clover_incs,
+ cpp_args : [clover_opencl_cpp_args, clover_spirv_cpp_args, cpp_vis_args],
+ dependencies : [dep_spirv_tools],
+ override_options : clover_cpp_std,
+)
+
+libclnir = static_library(
+ 'clnir',
+ files('nir/invocation.cpp', 'nir/invocation.hpp'),
+ include_directories : [clover_incs, inc_mesa],
+ dependencies : idep_nir,
+ cpp_args : [clover_opencl_cpp_args, clover_spirv_cpp_args, cpp_vis_args],
+ override_options : clover_cpp_std,
+)
+
+clover_files = files(
+ 'api/context.cpp',
+ 'api/device.cpp',
+ 'api/dispatch.cpp',
+ 'api/dispatch.hpp',
+ 'api/event.cpp',
+ 'api/interop.cpp',
+ 'api/kernel.cpp',
+ 'api/memory.cpp',
+ 'api/platform.cpp',
+ 'api/program.cpp',
+ 'api/queue.cpp',
+ 'api/sampler.cpp',
+ 'api/transfer.cpp',
+ 'api/util.hpp',
+ 'core/compiler.hpp',
+ 'core/context.cpp',
+ 'core/context.hpp',
+ 'core/device.cpp',
+ 'core/device.hpp',
+ 'core/error.hpp',
+ 'core/event.cpp',
+ 'core/event.hpp',
+ 'core/format.cpp',
+ 'core/format.hpp',
+ 'core/kernel.cpp',
+ 'core/kernel.hpp',
+ 'core/memory.cpp',
+ 'core/memory.hpp',
+ 'core/module.cpp',
+ 'core/module.hpp',
+ 'core/object.hpp',
+ 'core/platform.cpp',
+ 'core/platform.hpp',
+ 'core/program.cpp',
+ 'core/program.hpp',
+ 'core/property.hpp',
+ 'core/queue.cpp',
+ 'core/queue.hpp',
+ 'core/resource.cpp',
+ 'core/resource.hpp',
+ 'core/sampler.cpp',
+ 'core/sampler.hpp',
+ 'core/timestamp.cpp',
+ 'core/timestamp.hpp',
+ 'util/adaptor.hpp',
+ 'util/algebra.hpp',
+ 'util/algorithm.hpp',
+ 'util/factor.hpp',
+ 'util/functional.hpp',
+ 'util/lazy.hpp',
+ 'util/pointer.hpp',
+ 'util/range.hpp',
+ 'util/tuple.hpp',
+)
+
+libclover = static_library(
+ 'clover',
+ [clover_files, sha1_h],
+ include_directories : clover_incs,
+ cpp_args : [
+ clover_opencl_cpp_args,
+ clover_spirv_cpp_args,
+ clover_cpp_args,
+ cpp_vis_args
+ ],
+ link_with : [libclllvm, libclspirv, libclnir],
+ override_options : clover_cpp_std,
+)
diff --git a/src/gallium/frontends/clover/nir/invocation.cpp b/src/gallium/frontends/clover/nir/invocation.cpp
new file mode 100644
index 00000000000..46440d96e09
--- /dev/null
+++ b/src/gallium/frontends/clover/nir/invocation.cpp
@@ -0,0 +1,173 @@
+//
+// Copyright 2019 Karol Herbst
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#include "invocation.hpp"
+
+#include <tuple>
+
+#include "core/device.hpp"
+#include "core/error.hpp"
+#include "pipe/p_state.h"
+#include "util/algorithm.hpp"
+#include "util/functional.hpp"
+
+#include <compiler/glsl_types.h>
+#include <compiler/nir/nir_serialize.h>
+#include <compiler/spirv/nir_spirv.h>
+#include <util/u_math.h>
+
+using namespace clover;
+
+#ifdef HAVE_CLOVER_SPIRV
+
+// Refs and unrefs the glsl_type_singleton.
+static class glsl_type_ref {
+public:
+ glsl_type_ref() {
+ glsl_type_singleton_init_or_ref();
+ }
+
+ ~glsl_type_ref() {
+ glsl_type_singleton_decref();
+ }
+} glsl_type_ref;
+
+static const nir_shader_compiler_options *
+dev_get_nir_compiler_options(const device &dev)
+{
+ const void *co = dev.get_compiler_options(PIPE_SHADER_IR_NIR);
+ return static_cast<const nir_shader_compiler_options*>(co);
+}
+
+module clover::nir::spirv_to_nir(const module &mod, const device &dev,
+ std::string &r_log)
+{
+ struct spirv_to_nir_options spirv_options = {};
+ spirv_options.environment = NIR_SPIRV_OPENCL;
+ spirv_options.caps.address = true;
+ spirv_options.caps.float64 = true;
+ spirv_options.caps.int8 = true;
+ spirv_options.caps.int16 = true;
+ spirv_options.caps.int64 = true;
+ spirv_options.caps.kernel = true;
+ spirv_options.constant_as_global = true;
+
+ module m;
+ // We only insert one section.
+ assert(mod.secs.size() == 1);
+ auto &section = mod.secs[0];
+
+ module::resource_id section_id = 0;
+ for (const auto &sym : mod.syms) {
+ assert(sym.section == 0);
+
+ const auto *binary =
+ reinterpret_cast<const pipe_binary_program_header *>(section.data.data());
+ const uint32_t *data = reinterpret_cast<const uint32_t *>(binary->blob);
+ const size_t num_words = binary->num_bytes / 4;
+ const char *name = sym.name.c_str();
+ auto *compiler_options = dev_get_nir_compiler_options(dev);
+
+ nir_shader *nir = spirv_to_nir(data, num_words, nullptr, 0,
+ MESA_SHADER_KERNEL, name,
+ &spirv_options, compiler_options);
+ if (!nir) {
+ r_log += "Translation from SPIR-V to NIR for kernel \"" + sym.name +
+ "\" failed.\n";
+ throw build_error();
+ }
+
+ nir->info.cs.local_size_variable = true;
+ nir_validate_shader(nir, "clover");
+
+ // Calculate input offsets.
+ unsigned offset = 0;
+ nir_foreach_variable_safe(var, &nir->inputs) {
+ offset = align(offset, glsl_get_cl_alignment(var->type));
+ var->data.driver_location = offset;
+ offset += glsl_get_cl_size(var->type);
+ }
+
+ // Inline all functions first.
+ // according to the comment on nir_inline_functions
+ NIR_PASS_V(nir, nir_lower_variable_initializers, nir_var_function_temp);
+ NIR_PASS_V(nir, nir_lower_returns);
+ NIR_PASS_V(nir, nir_inline_functions);
+ NIR_PASS_V(nir, nir_opt_deref);
+
+ // Pick off the single entrypoint that we want.
+ foreach_list_typed_safe(nir_function, func, node, &nir->functions) {
+ if (!func->is_entrypoint)
+ exec_node_remove(&func->node);
+ }
+ assert(exec_list_length(&nir->functions) == 1);
+
+ nir_validate_shader(nir, "clover after function inlining");
+
+ NIR_PASS_V(nir, nir_lower_variable_initializers,
+ static_cast<nir_variable_mode>(~nir_var_function_temp));
+
+ // copy propagate to prepare for lower_explicit_io
+ NIR_PASS_V(nir, nir_split_var_copies);
+ NIR_PASS_V(nir, nir_opt_copy_prop_vars);
+ NIR_PASS_V(nir, nir_lower_var_copies);
+ NIR_PASS_V(nir, nir_lower_vars_to_ssa);
+ NIR_PASS_V(nir, nir_opt_dce);
+
+ nir_variable_mode modes = (nir_variable_mode)(
+ nir_var_shader_in |
+ nir_var_mem_global |
+ nir_var_mem_shared);
+ nir_address_format format = nir->info.cs.ptr_size == 64 ?
+ nir_address_format_64bit_global : nir_address_format_32bit_global;
+ NIR_PASS_V(nir, nir_lower_explicit_io, modes, format);
+
+ NIR_PASS_V(nir, nir_lower_system_values);
+ if (compiler_options->lower_int64_options)
+ NIR_PASS_V(nir, nir_lower_int64,
+ compiler_options->lower_int64_options);
+
+ NIR_PASS_V(nir, nir_opt_dce);
+
+ struct blob blob;
+ blob_init(&blob);
+ nir_serialize(&blob, nir, false);
+
+ const pipe_binary_program_header header { uint32_t(blob.size) };
+ module::section text { section_id, module::section::text_executable, header.num_bytes, {} };
+ text.data.insert(text.data.end(), reinterpret_cast<const char *>(&header),
+ reinterpret_cast<const char *>(&header) + sizeof(header));
+ text.data.insert(text.data.end(), blob.data, blob.data + blob.size);
+
+ m.syms.emplace_back(sym.name, section_id, 0, sym.args);
+ m.secs.push_back(text);
+ section_id++;
+ }
+ return m;
+}
+#else
+module clover::nir::spirv_to_nir(const module &mod, const device &dev, std::string &r_log)
+{
+ r_log += "SPIR-V support in clover is not enabled.\n";
+ throw error(CL_LINKER_NOT_AVAILABLE);
+}
+#endif
diff --git a/src/gallium/frontends/clover/nir/invocation.hpp b/src/gallium/frontends/clover/nir/invocation.hpp
new file mode 100644
index 00000000000..41407a79765
--- /dev/null
+++ b/src/gallium/frontends/clover/nir/invocation.hpp
@@ -0,0 +1,36 @@
+//
+// Copyright 2019 Karol Herbst
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#ifndef CLOVER_NIR_INVOCATION_HPP
+#define CLOVER_NIR_INVOCATION_HPP
+
+#include "core/module.hpp"
+
+namespace clover {
+ class device;
+ namespace nir {
+ // converts a given spirv module to nir
+ module spirv_to_nir(const module &mod, const device &dev, std::string &r_log);
+ }
+}
+
+#endif
diff --git a/src/gallium/frontends/clover/spirv/invocation.cpp b/src/gallium/frontends/clover/spirv/invocation.cpp
new file mode 100644
index 00000000000..01ced45c13b
--- /dev/null
+++ b/src/gallium/frontends/clover/spirv/invocation.cpp
@@ -0,0 +1,740 @@
+//
+// Copyright 2018 Pierre Moreau
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#include "invocation.hpp"
+
+#include <tuple>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+#ifdef HAVE_CLOVER_SPIRV
+#include <spirv-tools/libspirv.hpp>
+#include <spirv-tools/linker.hpp>
+#endif
+
+#include "core/error.hpp"
+#include "core/platform.hpp"
+#include "invocation.hpp"
+#include "llvm/util.hpp"
+#include "pipe/p_state.h"
+#include "util/algorithm.hpp"
+#include "util/functional.hpp"
+#include "util/u_math.h"
+
+#include "compiler/spirv/spirv.h"
+
+#define SPIRV_HEADER_WORD_SIZE 5
+
+using namespace clover;
+
+#ifdef HAVE_CLOVER_SPIRV
+namespace {
+
+ template<typename T>
+ T get(const char *source, size_t index) {
+ const uint32_t *word_ptr = reinterpret_cast<const uint32_t *>(source);
+ return static_cast<T>(word_ptr[index]);
+ }
+
+ enum module::argument::type
+ convert_storage_class(SpvStorageClass storage_class, std::string &err) {
+ switch (storage_class) {
+ case SpvStorageClassFunction:
+ return module::argument::scalar;
+ case SpvStorageClassUniformConstant:
+ return module::argument::global;
+ case SpvStorageClassWorkgroup:
+ return module::argument::local;
+ case SpvStorageClassCrossWorkgroup:
+ return module::argument::global;
+ default:
+ err += "Invalid storage type " + std::to_string(storage_class) + "\n";
+ throw build_error();
+ }
+ }
+
+ enum module::argument::type
+ convert_image_type(SpvId id, SpvDim dim, SpvAccessQualifier access,
+ std::string &err) {
+ if (dim == SpvDim2D && access == SpvAccessQualifierReadOnly)
+ return module::argument::image2d_rd;
+ else if (dim == SpvDim2D && access == SpvAccessQualifierWriteOnly)
+ return module::argument::image2d_wr;
+ else if (dim == SpvDim3D && access == SpvAccessQualifierReadOnly)
+ return module::argument::image3d_rd;
+ else if (dim == SpvDim3D && access == SpvAccessQualifierWriteOnly)
+ return module::argument::image3d_wr;
+ else {
+ err += "Unknown access qualifier " + std::to_string(access)
+ + " or dimension " + std::to_string(dim) + " for image "
+ + std::to_string(id) + ".\n";
+ throw build_error();
+ }
+ }
+
+ module::section
+ make_text_section(const std::vector<char> &code,
+ enum module::section::type section_type) {
+ const pipe_binary_program_header header { uint32_t(code.size()) };
+ module::section text { 0, section_type, header.num_bytes, {} };
+
+ text.data.insert(text.data.end(), reinterpret_cast<const char *>(&header),
+ reinterpret_cast<const char *>(&header) + sizeof(header));
+ text.data.insert(text.data.end(), code.begin(), code.end());
+
+ return text;
+ }
+
+ module
+ create_module_from_spirv(const std::vector<char> &source,
+ size_t pointer_byte_size,
+ std::string &err) {
+ const size_t length = source.size() / sizeof(uint32_t);
+ size_t i = SPIRV_HEADER_WORD_SIZE; // Skip header
+
+ std::string kernel_name;
+ size_t kernel_nb = 0u;
+ std::vector<module::argument> args;
+
+ module m;
+
+ std::unordered_map<SpvId, std::string> kernels;
+ std::unordered_map<SpvId, module::argument> types;
+ std::unordered_map<SpvId, SpvId> pointer_types;
+ std::unordered_map<SpvId, unsigned int> constants;
+ std::unordered_set<SpvId> packed_structures;
+ std::unordered_map<SpvId, std::vector<SpvFunctionParameterAttribute>>
+ func_param_attr_map;
+
+ while (i < length) {
+ const auto inst = &source[i * sizeof(uint32_t)];
+ const auto desc_word = get<uint32_t>(inst, 0);
+ const auto opcode = static_cast<SpvOp>(desc_word & SpvOpCodeMask);
+ const unsigned int num_operands = desc_word >> SpvWordCountShift;
+
+ switch (opcode) {
+ case SpvOpEntryPoint:
+ if (get<SpvExecutionModel>(inst, 1) == SpvExecutionModelKernel)
+ kernels.emplace(get<SpvId>(inst, 2),
+ source.data() + (i + 3u) * sizeof(uint32_t));
+ break;
+
+ case SpvOpDecorate: {
+ const auto id = get<SpvId>(inst, 1);
+ const auto decoration = get<SpvDecoration>(inst, 2);
+ if (decoration == SpvDecorationCPacked)
+ packed_structures.emplace(id);
+ else if (decoration == SpvDecorationFuncParamAttr) {
+ const auto attribute =
+ get<SpvFunctionParameterAttribute>(inst, 3u);
+ func_param_attr_map[id].push_back(attribute);
+ }
+ break;
+ }
+
+ case SpvOpGroupDecorate: {
+ const auto group_id = get<SpvId>(inst, 1);
+ if (packed_structures.count(group_id)) {
+ for (unsigned int i = 2u; i < num_operands; ++i)
+ packed_structures.emplace(get<SpvId>(inst, i));
+ }
+ const auto func_param_attr_iter =
+ func_param_attr_map.find(group_id);
+ if (func_param_attr_iter != func_param_attr_map.end()) {
+ for (unsigned int i = 2u; i < num_operands; ++i)
+ func_param_attr_map.emplace(get<SpvId>(inst, i),
+ func_param_attr_iter->second);
+ }
+ break;
+ }
+
+ case SpvOpConstant:
+ // We only care about constants that represent the size of arrays.
+ // If they are passed as argument, they will never be more than
+ // 4GB-wide, and even if they did, a clover::module::argument size
+ // is represented by an int.
+ constants[get<SpvId>(inst, 2)] = get<unsigned int>(inst, 3u);
+ break;
+
+ case SpvOpTypeInt: // FALLTHROUGH
+ case SpvOpTypeFloat: {
+ const auto size = get<uint32_t>(inst, 2) / 8u;
+ types[get<SpvId>(inst, 1)] = { module::argument::scalar, size,
+ size, size,
+ module::argument::zero_ext };
+ break;
+ }
+
+ case SpvOpTypeArray: {
+ const auto id = get<SpvId>(inst, 1);
+ const auto type_id = get<SpvId>(inst, 2);
+ const auto types_iter = types.find(type_id);
+ if (types_iter == types.end())
+ break;
+
+ const auto constant_id = get<SpvId>(inst, 3);
+ const auto constants_iter = constants.find(constant_id);
+ if (constants_iter == constants.end()) {
+ err += "Constant " + std::to_string(constant_id) +
+ " is missing\n";
+ throw build_error();
+ }
+ const auto elem_size = types_iter->second.size;
+ const auto elem_nbs = constants_iter->second;
+ const auto size = elem_size * elem_nbs;
+ types[id] = { module::argument::scalar, size, size,
+ types_iter->second.target_align,
+ module::argument::zero_ext };
+ break;
+ }
+
+ case SpvOpTypeStruct: {
+ const auto id = get<SpvId>(inst, 1);
+ const bool is_packed = packed_structures.count(id);
+
+ unsigned struct_size = 0u;
+ unsigned struct_align = 1u;
+ for (unsigned j = 2u; j < num_operands; ++j) {
+ const auto type_id = get<SpvId>(inst, j);
+ const auto types_iter = types.find(type_id);
+
+ // If a type was not found, that means it is not one of the
+ // types allowed as kernel arguments. And since the module has
+ // been validated, this means this type is not used for kernel
+ // arguments, and therefore can be ignored.
+ if (types_iter == types.end())
+ break;
+
+ const auto alignment = is_packed ? 1u
+ : types_iter->second.target_align;
+ const auto padding = (-struct_size) & (alignment - 1u);
+ struct_size += padding + types_iter->second.target_size;
+ struct_align = std::max(struct_align, alignment);
+ }
+ struct_size += (-struct_size) & (struct_align - 1u);
+ types[id] = { module::argument::scalar, struct_size, struct_size,
+ struct_align, module::argument::zero_ext };
+ break;
+ }
+
+ case SpvOpTypeVector: {
+ const auto id = get<SpvId>(inst, 1);
+ const auto type_id = get<SpvId>(inst, 2);
+ const auto types_iter = types.find(type_id);
+
+ // If a type was not found, that means it is not one of the
+ // types allowed as kernel arguments. And since the module has
+ // been validated, this means this type is not used for kernel
+ // arguments, and therefore can be ignored.
+ if (types_iter == types.end())
+ break;
+
+ const auto elem_size = types_iter->second.size;
+ const auto elem_nbs = get<uint32_t>(inst, 3);
+ const auto size = elem_size * elem_nbs;
+ types[id] = { module::argument::scalar, size, size, size,
+ module::argument::zero_ext };
+ break;
+ }
+
+ case SpvOpTypeForwardPointer: // FALLTHROUGH
+ case SpvOpTypePointer: {
+ const auto id = get<SpvId>(inst, 1);
+ const auto storage_class = get<SpvStorageClass>(inst, 2);
+ // Input means this is for a builtin variable, which can not be
+ // passed as an argument to a kernel.
+ if (storage_class == SpvStorageClassInput)
+ break;
+ types[id] = { convert_storage_class(storage_class, err),
+ sizeof(cl_mem),
+ static_cast<module::size_t>(pointer_byte_size),
+ static_cast<module::size_t>(pointer_byte_size),
+ module::argument::zero_ext };
+ if (opcode == SpvOpTypePointer)
+ pointer_types[id] = get<SpvId>(inst, 3);
+ break;
+ }
+
+ case SpvOpTypeSampler:
+ types[get<SpvId>(inst, 1)] = { module::argument::sampler,
+ sizeof(cl_sampler) };
+ break;
+
+ case SpvOpTypeImage: {
+ const auto id = get<SpvId>(inst, 1);
+ const auto dim = get<SpvDim>(inst, 3);
+ const auto access = get<SpvAccessQualifier>(inst, 9);
+ types[id] = { convert_image_type(id, dim, access, err),
+ sizeof(cl_mem), sizeof(cl_mem), sizeof(cl_mem),
+ module::argument::zero_ext };
+ break;
+ }
+
+ case SpvOpTypePipe: // FALLTHROUGH
+ case SpvOpTypeQueue: {
+ err += "TypePipe and TypeQueue are valid SPIR-V 1.0 types, but are "
+ "not available in the currently supported OpenCL C version."
+ "\n";
+ throw build_error();
+ }
+
+ case SpvOpFunction: {
+ const auto kernels_iter = kernels.find(get<SpvId>(inst, 2));
+ if (kernels_iter != kernels.end())
+ kernel_name = kernels_iter->second;
+ break;
+ }
+
+ case SpvOpFunctionParameter: {
+ if (kernel_name.empty())
+ break;
+
+ const auto type_id = get<SpvId>(inst, 1);
+ auto arg = types.find(type_id)->second;
+ const auto &func_param_attr_iter =
+ func_param_attr_map.find(get<SpvId>(inst, 2));
+ if (func_param_attr_iter != func_param_attr_map.end()) {
+ for (auto &i : func_param_attr_iter->second) {
+ switch (i) {
+ case SpvFunctionParameterAttributeSext:
+ arg.ext_type = module::argument::sign_ext;
+ break;
+ case SpvFunctionParameterAttributeZext:
+ arg.ext_type = module::argument::zero_ext;
+ break;
+ case SpvFunctionParameterAttributeByVal: {
+ const SpvId ptr_type_id =
+ pointer_types.find(type_id)->second;
+ arg = types.find(ptr_type_id)->second;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ }
+ args.emplace_back(arg);
+ break;
+ }
+
+ case SpvOpFunctionEnd:
+ if (kernel_name.empty())
+ break;
+ m.syms.emplace_back(kernel_name, 0, kernel_nb, args);
+ ++kernel_nb;
+ kernel_name.clear();
+ args.clear();
+ break;
+
+ default:
+ break;
+ }
+
+ i += num_operands;
+ }
+
+ m.secs.push_back(make_text_section(source,
+ module::section::text_intermediate));
+ return m;
+ }
+
+ bool
+ check_capabilities(const device &dev, const std::vector<char> &source,
+ std::string &r_log) {
+ const size_t length = source.size() / sizeof(uint32_t);
+ size_t i = SPIRV_HEADER_WORD_SIZE; // Skip header
+
+ while (i < length) {
+ const auto desc_word = get<uint32_t>(source.data(), i);
+ const auto opcode = static_cast<SpvOp>(desc_word & SpvOpCodeMask);
+ const unsigned int num_operands = desc_word >> SpvWordCountShift;
+
+ if (opcode != SpvOpCapability)
+ break;
+
+ const auto capability = get<SpvCapability>(source.data(), i + 1u);
+ switch (capability) {
+ // Mandatory capabilities
+ case SpvCapabilityAddresses:
+ case SpvCapabilityFloat16Buffer:
+ case SpvCapabilityGroups:
+ case SpvCapabilityInt64:
+ case SpvCapabilityInt16:
+ case SpvCapabilityInt8:
+ case SpvCapabilityKernel:
+ case SpvCapabilityLinkage:
+ case SpvCapabilityVector16:
+ break;
+ // Optional capabilities
+ case SpvCapabilityImageBasic:
+ case SpvCapabilityLiteralSampler:
+ case SpvCapabilitySampled1D:
+ case SpvCapabilityImage1D:
+ case SpvCapabilitySampledBuffer:
+ case SpvCapabilityImageBuffer:
+ if (!dev.image_support()) {
+ r_log += "Capability 'ImageBasic' is not supported.\n";
+ return false;
+ }
+ break;
+ case SpvCapabilityFloat64:
+ if (!dev.has_doubles()) {
+ r_log += "Capability 'Float64' is not supported.\n";
+ return false;
+ }
+ break;
+ // Enabled through extensions
+ case SpvCapabilityFloat16:
+ if (!dev.has_halves()) {
+ r_log += "Capability 'Float16' is not supported.\n";
+ return false;
+ }
+ break;
+ case SpvCapabilityInt64Atomics:
+ if (!dev.has_int64_atomics()) {
+ r_log += "Capability 'Int64Atomics' is not supported.\n";
+ return false;
+ }
+ break;
+ default:
+ r_log += "Capability '" + std::to_string(capability) +
+ "' is not supported.\n";
+ return false;
+ }
+
+ i += num_operands;
+ }
+
+ return true;
+ }
+
+ bool
+ check_extensions(const device &dev, const std::vector<char> &source,
+ std::string &r_log) {
+ const size_t length = source.size() / sizeof(uint32_t);
+ size_t i = SPIRV_HEADER_WORD_SIZE; // Skip header
+
+ while (i < length) {
+ const auto desc_word = get<uint32_t>(source.data(), i);
+ const auto opcode = static_cast<SpvOp>(desc_word & SpvOpCodeMask);
+ const unsigned int num_operands = desc_word >> SpvWordCountShift;
+
+ if (opcode == SpvOpCapability) {
+ i += num_operands;
+ continue;
+ }
+ if (opcode != SpvOpExtension)
+ break;
+
+ const char *extension = source.data() + (i + 1u) * sizeof(uint32_t);
+ const std::string device_extensions = dev.supported_extensions();
+ const std::string platform_extensions =
+ dev.platform.supported_extensions();
+ if (device_extensions.find(extension) == std::string::npos &&
+ platform_extensions.find(extension) == std::string::npos) {
+ r_log += "Extension '" + std::string(extension) +
+ "' is not supported.\n";
+ return false;
+ }
+
+ i += num_operands;
+ }
+
+ return true;
+ }
+
+ bool
+ check_memory_model(const device &dev, const std::vector<char> &source,
+ std::string &r_log) {
+ const size_t length = source.size() / sizeof(uint32_t);
+ size_t i = SPIRV_HEADER_WORD_SIZE; // Skip header
+
+ while (i < length) {
+ const auto desc_word = get<uint32_t>(source.data(), i);
+ const auto opcode = static_cast<SpvOp>(desc_word & SpvOpCodeMask);
+ const unsigned int num_operands = desc_word >> SpvWordCountShift;
+
+ switch (opcode) {
+ case SpvOpMemoryModel:
+ switch (get<SpvAddressingModel>(source.data(), i + 1u)) {
+ case SpvAddressingModelPhysical32:
+ return dev.address_bits() == 32;
+ case SpvAddressingModelPhysical64:
+ return dev.address_bits() == 64;
+ default:
+ unreachable("Only Physical32 and Physical64 are valid for OpenCL, and the binary was already validated");
+ return false;
+ }
+ break;
+ default:
+ break;
+ }
+
+ i += num_operands;
+ }
+
+ return false;
+ }
+
+ // Copies the input binary and convert it to the endianness of the host CPU.
+ std::vector<char>
+ spirv_to_cpu(const std::vector<char> &binary)
+ {
+ const uint32_t first_word = get<uint32_t>(binary.data(), 0u);
+ if (first_word == SpvMagicNumber)
+ return binary;
+
+ std::vector<char> cpu_endianness_binary(binary.size());
+ for (size_t i = 0; i < (binary.size() / 4u); ++i) {
+ const uint32_t word = get<uint32_t>(binary.data(), i);
+ reinterpret_cast<uint32_t *>(cpu_endianness_binary.data())[i] =
+ util_bswap32(word);
+ }
+
+ return cpu_endianness_binary;
+ }
+
+#ifdef HAVE_CLOVER_SPIRV
+ std::string
+ format_validator_msg(spv_message_level_t level, const char * /* source */,
+ const spv_position_t &position, const char *message) {
+ std::string level_str;
+ switch (level) {
+ case SPV_MSG_FATAL:
+ level_str = "Fatal";
+ break;
+ case SPV_MSG_INTERNAL_ERROR:
+ level_str = "Internal error";
+ break;
+ case SPV_MSG_ERROR:
+ level_str = "Error";
+ break;
+ case SPV_MSG_WARNING:
+ level_str = "Warning";
+ break;
+ case SPV_MSG_INFO:
+ level_str = "Info";
+ break;
+ case SPV_MSG_DEBUG:
+ level_str = "Debug";
+ break;
+ }
+ return "[" + level_str + "] At word No." +
+ std::to_string(position.index) + ": \"" + message + "\"\n";
+ }
+
+ spv_target_env
+ convert_opencl_str_to_target_env(const std::string &opencl_version) {
+ if (opencl_version == "2.2") {
+ return SPV_ENV_OPENCL_2_2;
+ } else if (opencl_version == "2.1") {
+ return SPV_ENV_OPENCL_2_1;
+ } else if (opencl_version == "2.0") {
+ return SPV_ENV_OPENCL_2_0;
+ } else if (opencl_version == "1.2" ||
+ opencl_version == "1.1" ||
+ opencl_version == "1.0") {
+ // SPIR-V is only defined for OpenCL >= 1.2, however some drivers
+ // might use it with OpenCL 1.0 and 1.1.
+ return SPV_ENV_OPENCL_1_2;
+ } else {
+ throw build_error("Invalid OpenCL version");
+ }
+ }
+#endif
+
+}
+
+module
+clover::spirv::compile_program(const std::vector<char> &binary,
+ const device &dev, std::string &r_log) {
+ std::vector<char> source = spirv_to_cpu(binary);
+
+ if (!is_valid_spirv(source, dev.device_version(), r_log))
+ throw build_error();
+
+ if (!check_capabilities(dev, source, r_log))
+ throw build_error();
+ if (!check_extensions(dev, source, r_log))
+ throw build_error();
+ if (!check_memory_model(dev, source, r_log))
+ throw build_error();
+
+ return create_module_from_spirv(source,
+ dev.address_bits() == 32 ? 4u : 8u, r_log);
+}
+
+module
+clover::spirv::link_program(const std::vector<module> &modules,
+ const device &dev, const std::string &opts,
+ std::string &r_log) {
+ std::vector<std::string> options = clover::llvm::tokenize(opts);
+
+ bool create_library = false;
+
+ std::string ignored_options;
+ for (const std::string &option : options) {
+ if (option == "-create-library") {
+ create_library = true;
+ } else {
+ ignored_options += "'" + option + "' ";
+ }
+ }
+ if (!ignored_options.empty()) {
+ r_log += "Ignoring the following link options: " + ignored_options
+ + "\n";
+ }
+
+ spvtools::LinkerOptions linker_options;
+ linker_options.SetCreateLibrary(create_library);
+
+ module m;
+
+ const auto section_type = create_library ? module::section::text_library :
+ module::section::text_executable;
+
+ std::vector<const uint32_t *> sections;
+ sections.reserve(modules.size());
+ std::vector<size_t> lengths;
+ lengths.reserve(modules.size());
+
+ auto const validator_consumer = [&r_log](spv_message_level_t level,
+ const char *source,
+ const spv_position_t &position,
+ const char *message) {
+ r_log += format_validator_msg(level, source, position, message);
+ };
+
+ for (const auto &mod : modules) {
+ const auto &msec = find([](const module::section &sec) {
+ return sec.type == module::section::text_intermediate ||
+ sec.type == module::section::text_library;
+ }, mod.secs);
+
+ const auto c_il = ((struct pipe_binary_program_header*)msec.data.data())->blob;
+ const auto length = msec.size;
+
+ sections.push_back(reinterpret_cast<const uint32_t *>(c_il));
+ lengths.push_back(length / sizeof(uint32_t));
+ }
+
+ std::vector<uint32_t> linked_binary;
+
+ const std::string opencl_version = dev.device_version();
+ const spv_target_env target_env =
+ convert_opencl_str_to_target_env(opencl_version);
+
+ const spvtools::MessageConsumer consumer = validator_consumer;
+ spvtools::Context context(target_env);
+ context.SetMessageConsumer(std::move(consumer));
+
+ if (Link(context, sections.data(), lengths.data(), sections.size(),
+ &linked_binary, linker_options) != SPV_SUCCESS)
+ throw error(CL_LINK_PROGRAM_FAILURE);
+
+ std::vector<char> final_binary{
+ reinterpret_cast<char *>(linked_binary.data()),
+ reinterpret_cast<char *>(linked_binary.data() +
+ linked_binary.size()) };
+ if (!is_valid_spirv(final_binary, opencl_version, r_log))
+ throw error(CL_LINK_PROGRAM_FAILURE);
+
+ for (const auto &mod : modules)
+ m.syms.insert(m.syms.end(), mod.syms.begin(), mod.syms.end());
+
+ m.secs.emplace_back(make_text_section(final_binary, section_type));
+
+ return m;
+}
+
+bool
+clover::spirv::is_valid_spirv(const std::vector<char> &binary,
+ const std::string &opencl_version,
+ std::string &r_log) {
+ auto const validator_consumer =
+ [&r_log](spv_message_level_t level, const char *source,
+ const spv_position_t &position, const char *message) {
+ r_log += format_validator_msg(level, source, position, message);
+ };
+
+ const spv_target_env target_env =
+ convert_opencl_str_to_target_env(opencl_version);
+ spvtools::SpirvTools spvTool(target_env);
+ spvTool.SetMessageConsumer(validator_consumer);
+
+ return spvTool.Validate(reinterpret_cast<const uint32_t *>(binary.data()),
+ binary.size() / 4u);
+}
+
+std::string
+clover::spirv::print_module(const std::vector<char> &binary,
+ const std::string &opencl_version) {
+ const spv_target_env target_env =
+ convert_opencl_str_to_target_env(opencl_version);
+ spvtools::SpirvTools spvTool(target_env);
+ spv_context spvContext = spvContextCreate(target_env);
+ if (!spvContext)
+ return "Failed to create an spv_context for disassembling the module.";
+
+ spv_text disassembly;
+ spvBinaryToText(spvContext,
+ reinterpret_cast<const uint32_t *>(binary.data()),
+ binary.size() / 4u, SPV_BINARY_TO_TEXT_OPTION_NONE,
+ &disassembly, nullptr);
+ spvContextDestroy(spvContext);
+
+ const std::string disassemblyStr = disassembly->str;
+ spvTextDestroy(disassembly);
+
+ return disassemblyStr;
+}
+
+#else
+bool
+clover::spirv::is_valid_spirv(const std::vector<char> &/*binary*/,
+ const std::string &/*opencl_version*/,
+ std::string &/*r_log*/) {
+ return false;
+}
+
+module
+clover::spirv::compile_program(const std::vector<char> &binary,
+ const device &dev, std::string &r_log) {
+ r_log += "SPIR-V support in clover is not enabled.\n";
+ throw build_error();
+}
+
+module
+clover::spirv::link_program(const std::vector<module> &/*modules*/,
+ const device &/*dev*/, const std::string &/*opts*/,
+ std::string &r_log) {
+ r_log += "SPIR-V support in clover is not enabled.\n";
+ throw error(CL_LINKER_NOT_AVAILABLE);
+}
+
+std::string
+clover::spirv::print_module(const std::vector<char> &binary,
+ const std::string &opencl_version) {
+ return std::string();
+}
+#endif
diff --git a/src/gallium/frontends/clover/spirv/invocation.hpp b/src/gallium/frontends/clover/spirv/invocation.hpp
new file mode 100644
index 00000000000..472d8c0de71
--- /dev/null
+++ b/src/gallium/frontends/clover/spirv/invocation.hpp
@@ -0,0 +1,56 @@
+//
+// Copyright 2018 Pierre Moreau
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#ifndef CLOVER_SPIRV_INVOCATION_HPP
+#define CLOVER_SPIRV_INVOCATION_HPP
+
+#include "core/context.hpp"
+#include "core/module.hpp"
+#include "core/program.hpp"
+
+namespace clover {
+ namespace spirv {
+ // Returns whether the given binary is considered valid for the given
+ // OpenCL version.
+ //
+ // It uses SPIRV-Tools validator to do the validation, and potential
+ // warnings and errors are appended to |r_log|.
+ bool is_valid_spirv(const std::vector<char> &binary,
+ const std::string &opencl_version,
+ std::string &r_log);
+
+ // Creates a clover module out of the given SPIR-V binary.
+ module compile_program(const std::vector<char> &binary,
+ const device &dev, std::string &r_log);
+
+ // Combines multiple clover modules into a single one, resolving
+ // link dependencies between them.
+ module link_program(const std::vector<module> &modules, const device &dev,
+ const std::string &opts, std::string &r_log);
+
+ // Returns a textual representation of the given binary.
+ std::string print_module(const std::vector<char> &binary,
+ const std::string &opencl_version);
+ }
+}
+
+#endif
diff --git a/src/gallium/frontends/clover/util/adaptor.hpp b/src/gallium/frontends/clover/util/adaptor.hpp
new file mode 100644
index 00000000000..e9035968573
--- /dev/null
+++ b/src/gallium/frontends/clover/util/adaptor.hpp
@@ -0,0 +1,183 @@
+//
+// Copyright 2013 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#ifndef CLOVER_UTIL_ADAPTOR_HPP
+#define CLOVER_UTIL_ADAPTOR_HPP
+
+#include <iterator>
+
+#include "util/tuple.hpp"
+#include "util/pointer.hpp"
+#include "util/functional.hpp"
+
+namespace clover {
+ namespace detail {
+ ///
+ /// Implementation of the iterator concept that transforms the
+ /// value of the source iterators \a Is on dereference by use of
+ /// a functor \a F.
+ ///
+ /// The exact category of the resulting iterator should be the
+ /// least common denominator of the source iterator categories.
+ ///
+ template<typename F, typename... Is>
+ class iterator_adaptor {
+ public:
+ typedef std::forward_iterator_tag iterator_category;
+ typedef typename std::result_of<
+ F(typename std::iterator_traits<Is>::reference...)
+ >::type reference;
+ typedef typename std::remove_reference<reference>::type value_type;
+ typedef pseudo_ptr<value_type> pointer;
+ typedef std::ptrdiff_t difference_type;
+
+ iterator_adaptor() {
+ }
+
+ iterator_adaptor(F f, std::tuple<Is...> &&its) :
+ f(f), its(std::move(its)) {
+ }
+
+ reference
+ operator*() const {
+ return tuple::apply(f, tuple::map(derefs(), its));
+ }
+
+ iterator_adaptor &
+ operator++() {
+ tuple::map(preincs(), its);
+ return *this;
+ }
+
+ iterator_adaptor
+ operator++(int) {
+ auto jt = *this;
+ ++*this;
+ return jt;
+ }
+
+ bool
+ operator==(const iterator_adaptor &jt) const {
+ return its == jt.its;
+ }
+
+ bool
+ operator!=(const iterator_adaptor &jt) const {
+ return its != jt.its;
+ }
+
+ pointer
+ operator->() const {
+ return { **this };
+ }
+
+ iterator_adaptor &
+ operator--() {
+ tuple::map(predecs(), its);
+ return *this;
+ }
+
+ iterator_adaptor
+ operator--(int) {
+ auto jt = *this;
+ --*this;
+ return jt;
+ }
+
+ iterator_adaptor &
+ operator+=(difference_type n) {
+ tuple::map(advances_by(n), its);
+ return *this;
+ }
+
+ iterator_adaptor &
+ operator-=(difference_type n) {
+ tuple::map(advances_by(-n), its);
+ return *this;
+ }
+
+ iterator_adaptor
+ operator+(difference_type n) const {
+ auto jt = *this;
+ jt += n;
+ return jt;
+ }
+
+ iterator_adaptor
+ operator-(difference_type n) const {
+ auto jt = *this;
+ jt -= n;
+ return jt;
+ }
+
+ difference_type
+ operator-(const iterator_adaptor &jt) const {
+ return std::get<0>(its) - std::get<0>(jt.its);
+ }
+
+ reference
+ operator[](difference_type n) const {
+ return *(*this + n);
+ }
+
+ bool
+ operator<(iterator_adaptor &jt) const {
+ return *this - jt < 0;
+ }
+
+ bool
+ operator>(iterator_adaptor &jt) const {
+ return *this - jt > 0;
+ }
+
+ bool
+ operator>=(iterator_adaptor &jt) const {
+ return !(*this < jt);
+ }
+
+ bool
+ operator<=(iterator_adaptor &jt) const {
+ return !(*this > jt);
+ }
+
+ protected:
+ F f;
+ std::tuple<Is...> its;
+ };
+
+ template<typename F, typename... Is>
+ iterator_adaptor<F, Is...>
+ operator+(typename iterator_adaptor<F, Is...>::difference_type n,
+ const iterator_adaptor<F, Is...> &jt) {
+ return (jt + n);
+ }
+
+ template<typename F, typename... Is>
+ iterator_adaptor<F, Is...>
+ operator-(typename iterator_adaptor<F, Is...>::difference_type n,
+ const iterator_adaptor<F, Is...> &jt) {
+ return (jt - n);
+ }
+ }
+}
+
+#endif
diff --git a/src/gallium/frontends/clover/util/algebra.hpp b/src/gallium/frontends/clover/util/algebra.hpp
new file mode 100644
index 00000000000..43a9d8bbf5f
--- /dev/null
+++ b/src/gallium/frontends/clover/util/algebra.hpp
@@ -0,0 +1,160 @@
+//
+// Copyright 2013 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#ifndef CLOVER_UTIL_ALGEBRA_HPP
+#define CLOVER_UTIL_ALGEBRA_HPP
+
+#include <type_traits>
+
+#include "util/range.hpp"
+#include "util/functional.hpp"
+
+namespace clover {
+ ///
+ /// Class that identifies vectors (in the linear-algebraic sense).
+ ///
+ /// There should be a definition of this class for each type that
+ /// makes sense as vector arithmetic operand.
+ ///
+ template<typename V, typename = void>
+ struct vector_traits;
+
+ ///
+ /// References of vectors are vectors.
+ ///
+ template<typename T>
+ struct vector_traits<T &, typename vector_traits<T>::enable> {
+ typedef void enable;
+ };
+
+ ///
+ /// Constant vectors are vectors.
+ ///
+ template<typename T>
+ struct vector_traits<const T, typename vector_traits<T>::enable> {
+ typedef void enable;
+ };
+
+ ///
+ /// Arrays of arithmetic types are vectors.
+ ///
+ template<typename T, std::size_t N>
+ struct vector_traits<std::array<T, N>,
+ typename std::enable_if<
+ std::is_arithmetic<T>::value>::type> {
+ typedef void enable;
+ };
+
+ namespace detail {
+ template<typename... Ts>
+ struct are_defined {
+ typedef void enable;
+ };
+ }
+
+ ///
+ /// The result of mapping a vector is a vector.
+ ///
+ template<typename F, typename... Vs>
+ struct vector_traits<adaptor_range<F, Vs...>,
+ typename detail::are_defined<
+ typename vector_traits<Vs>::enable...>::enable> {
+ typedef void enable;
+ };
+
+ ///
+ /// Vector sum.
+ ///
+ template<typename U, typename V,
+ typename = typename vector_traits<U>::enable,
+ typename = typename vector_traits<V>::enable>
+ adaptor_range<plus, U, V>
+ operator+(U &&u, V &&v) {
+ return map(plus(), std::forward<U>(u), std::forward<V>(v));
+ }
+
+ ///
+ /// Vector difference.
+ ///
+ template<typename U, typename V,
+ typename = typename vector_traits<U>::enable,
+ typename = typename vector_traits<V>::enable>
+ adaptor_range<minus, U, V>
+ operator-(U &&u, V &&v) {
+ return map(minus(), std::forward<U>(u), std::forward<V>(v));
+ }
+
+ ///
+ /// Scalar multiplication.
+ ///
+ template<typename U, typename T,
+ typename = typename vector_traits<U>::enable>
+ adaptor_range<multiplies_by_t<T>, U>
+ operator*(U &&u, T &&a) {
+ return map(multiplies_by<T>(std::forward<T>(a)), std::forward<U>(u));
+ }
+
+ ///
+ /// Scalar multiplication.
+ ///
+ template<typename U, typename T,
+ typename = typename vector_traits<U>::enable>
+ adaptor_range<multiplies_by_t<T>, U>
+ operator*(T &&a, U &&u) {
+ return map(multiplies_by<T>(std::forward<T>(a)), std::forward<U>(u));
+ }
+
+ ///
+ /// Additive inverse.
+ ///
+ template<typename U,
+ typename = typename vector_traits<U>::enable>
+ adaptor_range<negate, U>
+ operator-(U &&u) {
+ return map(negate(), std::forward<U>(u));
+ }
+
+ namespace detail {
+ template<typename U, typename V>
+ using dot_type = typename std::common_type<
+ typename std::remove_reference<U>::type::value_type,
+ typename std::remove_reference<V>::type::value_type
+ >::type;
+ }
+
+ ///
+ /// Dot product of two vectors.
+ ///
+ /// It can also do matrix multiplication if \a u or \a v is a
+ /// vector of vectors.
+ ///
+ template<typename U, typename V,
+ typename = typename vector_traits<U>::enable,
+ typename = typename vector_traits<V>::enable>
+ detail::dot_type<U, V>
+ dot(U &&u, V &&v) {
+ return fold(plus(), detail::dot_type<U, V>(),
+ map(multiplies(), u, v));
+ }
+}
+
+#endif
diff --git a/src/gallium/frontends/clover/util/algorithm.hpp b/src/gallium/frontends/clover/util/algorithm.hpp
new file mode 100644
index 00000000000..1658458ee18
--- /dev/null
+++ b/src/gallium/frontends/clover/util/algorithm.hpp
@@ -0,0 +1,218 @@
+//
+// Copyright 2013 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#ifndef CLOVER_UTIL_ALGORITHM_HPP
+#define CLOVER_UTIL_ALGORITHM_HPP
+
+#include <algorithm>
+#include <stdexcept>
+
+#include "util/range.hpp"
+#include "util/functional.hpp"
+
+namespace clover {
+ namespace detail {
+ template<typename R>
+ using preferred_reference_type = decltype(*std::declval<R>().begin());
+ }
+
+ ///
+ /// Return the first element in a range.
+ ///
+ template<typename R>
+ detail::preferred_reference_type<R>
+ head(R &&r) {
+ assert(!r.empty());
+ return r.front();
+ }
+
+ ///
+ /// Return all elements in a range but the first.
+ ///
+ template<typename R>
+ slice_range<R>
+ tail(R &&r) {
+ assert(!r.empty());
+ return { std::forward<R>(r), 1, r.size() };
+ }
+
+ ///
+ /// Return the only element in a range.
+ ///
+ template<typename R>
+ detail::preferred_reference_type<R>
+ unique(R &&r) {
+ if (r.size() != 1)
+ throw std::out_of_range("");
+
+ return r.front();
+ }
+
+ ///
+ /// Combine a variable number of ranges element-wise in a single
+ /// range of tuples.
+ ///
+ template<typename... Rs>
+ adaptor_range<zips, Rs...>
+ zip(Rs &&... rs) {
+ return map(zips(), std::forward<Rs>(rs)...);
+ }
+
+ ///
+ /// Evaluate the elements of a range.
+ ///
+ /// Useful because most of the range algorithms evaluate their
+ /// result lazily.
+ ///
+ template<typename R>
+ void
+ eval(R &&r) {
+ for (auto i = r.begin(), e = r.end(); i != e; ++i)
+ *i;
+ }
+
+ ///
+ /// Apply functor \a f element-wise on a variable number of ranges
+ /// \a rs.
+ ///
+ /// The functor \a f should take as many arguments as ranges are
+ /// provided.
+ ///
+ template<typename F, typename... Rs>
+ void
+ for_each(F &&f, Rs &&... rs) {
+ eval(map(std::forward<F>(f), std::forward<Rs>(rs)...));
+ }
+
+ ///
+ /// Copy all elements from range \a r into an output container
+ /// starting from iterator \a i.
+ ///
+ template<typename R, typename I>
+ void
+ copy(R &&r, I i) {
+ for (detail::preferred_reference_type<R> x : r)
+ *(i++) = x;
+ }
+
+ ///
+ /// Reduce the elements of range \a r by applying functor \a f
+ /// element by element.
+ ///
+ /// \a f should take an accumulator value (which is initialized to
+ /// \a a) and an element value as arguments, and return an updated
+ /// accumulator value.
+ ///
+ /// \returns The final value of the accumulator.
+ ///
+ template<typename F, typename A, typename R>
+ A
+ fold(F &&f, A a, R &&r) {
+ for (detail::preferred_reference_type<R> x : r)
+ a = f(a, x);
+
+ return a;
+ }
+
+ ///
+ /// Return how many elements of range \a r are equal to \a x.
+ ///
+ template<typename T, typename R>
+ typename std::remove_reference<R>::type::size_type
+ count(T &&x, R &&r) {
+ typename std::remove_reference<R>::type::size_type n = 0;
+
+ for (detail::preferred_reference_type<R> y : r) {
+ if (x == y)
+ n++;
+ }
+
+ return n;
+ }
+
+ ///
+ /// Return the first element in range \a r for which predicate \a f
+ /// evaluates to true.
+ ///
+ template<typename F, typename R>
+ detail::preferred_reference_type<R>
+ find(F &&f, R &&r) {
+ for (detail::preferred_reference_type<R> x : r) {
+ if (f(x))
+ return x;
+ }
+
+ throw std::out_of_range("");
+ }
+
+ ///
+ /// Return true if the element-wise application of predicate \a f
+ /// on \a rs evaluates to true for all elements.
+ ///
+ template<typename F, typename... Rs>
+ bool
+ all_of(F &&f, Rs &&... rs) {
+ for (auto b : map(f, rs...)) {
+ if (!b)
+ return false;
+ }
+
+ return true;
+ }
+
+ ///
+ /// Return true if the element-wise application of predicate \a f
+ /// on \a rs evaluates to true for any element.
+ ///
+ template<typename F, typename... Rs>
+ bool
+ any_of(F &&f, Rs &&... rs) {
+ for (auto b : map(f, rs...)) {
+ if (b)
+ return true;
+ }
+
+ return false;
+ }
+
+ ///
+ /// Erase elements for which predicate \a f evaluates to true from
+ /// container \a r.
+ ///
+ template<typename F, typename R>
+ void
+ erase_if(F &&f, R &&r) {
+ auto i = r.begin(), e = r.end();
+
+ for (auto j = r.begin(); j != e; ++j) {
+ if (!f(*j)) {
+ if (j != i)
+ *i = std::move(*j);
+ ++i;
+ }
+ }
+
+ r.erase(i, e);
+ }
+}
+
+#endif
diff --git a/src/gallium/frontends/clover/util/factor.hpp b/src/gallium/frontends/clover/util/factor.hpp
new file mode 100644
index 00000000000..76d3bfe343f
--- /dev/null
+++ b/src/gallium/frontends/clover/util/factor.hpp
@@ -0,0 +1,131 @@
+//
+// Copyright 2013 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#ifndef CLOVER_UTIL_FACTOR_HPP
+#define CLOVER_UTIL_FACTOR_HPP
+
+#include "util/range.hpp"
+
+namespace clover {
+ namespace factor {
+ ///
+ /// Calculate all prime integer factors of \p x.
+ ///
+ /// If \p limit is non-zero, terminate early as soon as enough
+ /// factors have been collected to reach the product \p limit.
+ ///
+ template<typename T>
+ std::vector<T>
+ find_integer_prime_factors(T x, T limit = 0)
+ {
+ const T max_d = (limit > 0 && limit < x ? limit : x);
+ const T min_x = x / max_d;
+ std::vector<T> factors;
+
+ for (T d = 2; d <= max_d && x > min_x; d++) {
+ if (x % d == 0) {
+ for (; x % d == 0; x /= d);
+ factors.push_back(d);
+ }
+ }
+
+ return factors;
+ }
+
+ namespace detail {
+ ///
+ /// Walk the power set of prime factors of the n-dimensional
+ /// integer array \p grid subject to the constraints given by
+ /// \p limits.
+ ///
+ template<typename T>
+ std::pair<T, std::vector<T>>
+ next_grid_factor(const std::pair<T, std::vector<T>> &limits,
+ const std::vector<T> &grid,
+ const std::vector<std::vector<T>> &factors,
+ std::pair<T, std::vector<T>> block,
+ unsigned d = 0, unsigned i = 0) {
+ if (d >= factors.size()) {
+ // We're done.
+ return {};
+
+ } else if (i >= factors[d].size()) {
+ // We're done with this grid dimension, try the next.
+ return next_grid_factor(limits, grid, factors,
+ std::move(block), d + 1, 0);
+
+ } else {
+ T f = factors[d][i];
+
+ // Try the next power of this factor.
+ block.first *= f;
+ block.second[d] *= f;
+
+ if (block.first <= limits.first &&
+ block.second[d] <= limits.second[d] &&
+ grid[d] % block.second[d] == 0) {
+ // We've found a valid grid divisor.
+ return block;
+
+ } else {
+ // Overflow, back off to the zeroth power,
+ while (block.second[d] % f == 0) {
+ block.second[d] /= f;
+ block.first /= f;
+ }
+
+ // ...and carry to the next factor.
+ return next_grid_factor(limits, grid, factors,
+ std::move(block), d, i + 1);
+ }
+ }
+ }
+ }
+
+ ///
+ /// Find the divisor of the integer array \p grid that gives the
+ /// highest possible product not greater than \p product_limit
+ /// subject to the constraints given by \p coord_limit.
+ ///
+ template<typename T>
+ std::vector<T>
+ find_grid_optimal_factor(T product_limit,
+ const std::vector<T> &coord_limit,
+ const std::vector<T> &grid) {
+ const std::vector<std::vector<T>> factors =
+ map(find_integer_prime_factors<T>, grid, coord_limit);
+ const auto limits = std::make_pair(product_limit, coord_limit);
+ auto best = std::make_pair(T(1), std::vector<T>(grid.size(), T(1)));
+
+ for (auto block = best;
+ block.first != 0 && best.first != product_limit;
+ block = detail::next_grid_factor(limits, grid, factors, block)) {
+ if (block.first > best.first)
+ best = block;
+ }
+
+ return best.second;
+ }
+ }
+}
+
+#endif
diff --git a/src/gallium/frontends/clover/util/functional.hpp b/src/gallium/frontends/clover/util/functional.hpp
new file mode 100644
index 00000000000..fc281c5c79a
--- /dev/null
+++ b/src/gallium/frontends/clover/util/functional.hpp
@@ -0,0 +1,419 @@
+//
+// Copyright 2013 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#ifndef CLOVER_UTIL_FUNCTIONAL_HPP
+#define CLOVER_UTIL_FUNCTIONAL_HPP
+
+#include <type_traits>
+
+namespace clover {
+ struct identity {
+ template<typename T>
+ typename std::remove_reference<T>::type
+ operator()(T &&x) const {
+ return x;
+ }
+ };
+
+ struct plus {
+ template<typename T, typename S>
+ typename std::common_type<T, S>::type
+ operator()(T x, S y) const {
+ return x + y;
+ }
+ };
+
+ struct minus {
+ template<typename T, typename S>
+ typename std::common_type<T, S>::type
+ operator()(T x, S y) const {
+ return x - y;
+ }
+ };
+
+ struct negate {
+ template<typename T>
+ T
+ operator()(T x) const {
+ return -x;
+ }
+ };
+
+ struct multiplies {
+ template<typename T, typename S>
+ typename std::common_type<T, S>::type
+ operator()(T x, S y) const {
+ return x * y;
+ }
+ };
+
+ struct divides {
+ template<typename T, typename S>
+ typename std::common_type<T, S>::type
+ operator()(T x, S y) const {
+ return x / y;
+ }
+ };
+
+ struct modulus {
+ template<typename T, typename S>
+ typename std::common_type<T, S>::type
+ operator()(T x, S y) const {
+ return x % y;
+ }
+ };
+
+ struct minimum {
+ template<typename T>
+ T
+ operator()(T x) const {
+ return x;
+ }
+
+ template<typename T, typename... Ts>
+ T
+ operator()(T x, Ts... xs) const {
+ T y = minimum()(xs...);
+ return x < y ? x : y;
+ }
+ };
+
+ struct maximum {
+ template<typename T>
+ T
+ operator()(T x) const {
+ return x;
+ }
+
+ template<typename T, typename... Ts>
+ T
+ operator()(T x, Ts... xs) const {
+ T y = maximum()(xs...);
+ return x < y ? y : x;
+ }
+ };
+
+ struct preincs {
+ template<typename T>
+ T &
+ operator()(T &x) const {
+ return ++x;
+ }
+ };
+
+ struct predecs {
+ template<typename T>
+ T &
+ operator()(T &x) const {
+ return --x;
+ }
+ };
+
+ template<typename T>
+ class multiplies_by_t {
+ public:
+ multiplies_by_t(T x) : x(x) {
+ }
+
+ template<typename S>
+ typename std::common_type<T, S>::type
+ operator()(S y) const {
+ return x * y;
+ }
+
+ private:
+ T x;
+ };
+
+ template<typename T>
+ multiplies_by_t<T>
+ multiplies_by(T x) {
+ return { x };
+ }
+
+ template<typename T>
+ class preincs_by_t {
+ public:
+ preincs_by_t(T n) : n(n) {
+ }
+
+ template<typename S>
+ S &
+ operator()(S &x) const {
+ return x += n;
+ }
+
+ private:
+ T n;
+ };
+
+ template<typename T>
+ preincs_by_t<T>
+ preincs_by(T n) {
+ return { n };
+ }
+
+ template<typename T>
+ class predecs_by_t {
+ public:
+ predecs_by_t(T n) : n(n) {
+ }
+
+ template<typename S>
+ S &
+ operator()(S &x) const {
+ return x -= n;
+ }
+
+ private:
+ T n;
+ };
+
+ template<typename T>
+ predecs_by_t<T>
+ predecs_by(T n) {
+ return { n };
+ }
+
+ struct greater {
+ template<typename T, typename S>
+ bool
+ operator()(T x, S y) const {
+ return x > y;
+ }
+ };
+
+ struct evals {
+ template<typename T>
+ auto
+ operator()(T &&x) const -> decltype(x()) {
+ return x();
+ }
+ };
+
+ struct derefs {
+ template<typename T>
+ auto
+ operator()(T &&x) const -> decltype(*x) {
+ return *x;
+ }
+ };
+
+ struct addresses {
+ template<typename T>
+ T *
+ operator()(T &x) const {
+ return &x;
+ }
+
+ template<typename T>
+ T *
+ operator()(std::reference_wrapper<T> x) const {
+ return &x.get();
+ }
+ };
+
+ struct begins {
+ template<typename T>
+ auto
+ operator()(T &x) const -> decltype(x.begin()) {
+ return x.begin();
+ }
+ };
+
+ struct ends {
+ template<typename T>
+ auto
+ operator()(T &x) const -> decltype(x.end()) {
+ return x.end();
+ }
+ };
+
+ struct sizes {
+ template<typename T>
+ auto
+ operator()(T &x) const -> decltype(x.size()) {
+ return x.size();
+ }
+ };
+
+ template<typename T>
+ class advances_by_t {
+ public:
+ advances_by_t(T n) : n(n) {
+ }
+
+ template<typename S>
+ S
+ operator()(S &&it) const {
+ std::advance(it, n);
+ return it;
+ }
+
+ private:
+ T n;
+ };
+
+ template<typename T>
+ advances_by_t<T>
+ advances_by(T n) {
+ return { n };
+ }
+
+ struct zips {
+ template<typename... Ts>
+ std::tuple<Ts...>
+ operator()(Ts &&... xs) const {
+ return std::tuple<Ts...>(std::forward<Ts>(xs)...);
+ }
+ };
+
+ struct is_zero {
+ template<typename T>
+ bool
+ operator()(const T &x) const {
+ return x == 0;
+ }
+ };
+
+ struct keys {
+ template<typename P>
+ auto
+ operator()(P &&p) const -> decltype(std::get<0>(std::forward<P>(p))) {
+ return std::get<0>(std::forward<P>(p));
+ }
+ };
+
+ struct values {
+ template<typename P>
+ auto
+ operator()(P &&p) const -> decltype(std::get<1>(std::forward<P>(p))) {
+ return std::get<1>(std::forward<P>(p));
+ }
+ };
+
+ template<typename T>
+ class equals_t {
+ public:
+ equals_t(T &&x) : x(x) {}
+
+ template<typename S>
+ bool
+ operator()(S &&y) const {
+ return x == y;
+ }
+
+ private:
+ T x;
+ };
+
+ template<typename T>
+ equals_t<T>
+ equals(T &&x) {
+ return { std::forward<T>(x) };
+ }
+
+ class name_equals {
+ public:
+ name_equals(const std::string &name) : name(name) {
+ }
+
+ template<typename T>
+ bool
+ operator()(const T &x) const {
+ return std::string(x.name.begin(), x.name.end()) == name;
+ }
+
+ private:
+ const std::string &name;
+ };
+
+ class id_equals {
+ public:
+ id_equals(const uint32_t id) : id(id) {
+ }
+
+ template<typename T>
+ bool
+ operator()(const T &x) const {
+ return x.id == id;
+ }
+
+ private:
+ const uint32_t id;
+ };
+
+ template<typename T>
+ class key_equals_t {
+ public:
+ key_equals_t(T &&x) : x(x) {
+ }
+
+ template<typename P>
+ bool
+ operator()(const P &p) const {
+ return p.first == x;
+ }
+
+ private:
+ T x;
+ };
+
+ template<typename T>
+ key_equals_t<T>
+ key_equals(T &&x) {
+ return { std::forward<T>(x) };
+ }
+
+ template<typename T>
+ class type_equals_t {
+ public:
+ type_equals_t(T type) : type(type) {
+ }
+
+ template<typename S>
+ bool
+ operator()(const S &x) const {
+ return x.type == type;
+ }
+
+ private:
+ T type;
+ };
+
+ template<typename T>
+ type_equals_t<T>
+ type_equals(T x) {
+ return { x };
+ }
+
+ struct interval_overlaps {
+ template<typename T>
+ bool
+ operator()(T x0, T x1, T y0, T y1) {
+ return ((x0 <= y0 && y0 < x1) ||
+ (y0 <= x0 && x0 < y1));
+ }
+ };
+}
+
+#endif
diff --git a/src/gallium/frontends/clover/util/lazy.hpp b/src/gallium/frontends/clover/util/lazy.hpp
new file mode 100644
index 00000000000..e32a8f8b1b9
--- /dev/null
+++ b/src/gallium/frontends/clover/util/lazy.hpp
@@ -0,0 +1,161 @@
+//
+// Copyright 2013 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#ifndef CLOVER_UTIL_LAZY_HPP
+#define CLOVER_UTIL_LAZY_HPP
+
+#include <type_traits>
+#include <stdexcept>
+#include <memory>
+
+namespace clover {
+ namespace detail {
+ template<typename T>
+ class basic_lazy {
+ public:
+ virtual
+ ~basic_lazy() {
+ }
+
+ virtual basic_lazy *
+ clone() const = 0;
+
+ virtual
+ operator T() const = 0;
+ };
+
+ template<typename T, typename F>
+ class deferred_lazy : public basic_lazy<T> {
+ public:
+ template<typename G>
+ deferred_lazy(G &&f) : f(new F(std::forward<G>(f))) {
+ }
+
+ virtual basic_lazy<T> *
+ clone() const {
+ return new deferred_lazy(*this);
+ }
+
+ operator T() const {
+ if (f) {
+ x = (*f)();
+ f = {};
+ }
+
+ return x;
+ }
+
+ private:
+ mutable std::shared_ptr<F> f;
+ mutable T x;
+ };
+
+ template<typename T>
+ class strict_lazy : public basic_lazy<T> {
+ public:
+ template<typename S>
+ strict_lazy(S &&x) : x(std::forward<S>(x)) {
+ }
+
+ virtual basic_lazy<T> *
+ clone() const {
+ return new strict_lazy(*this);
+ }
+
+ operator T() const {
+ return x;
+ }
+
+ private:
+ T x;
+ };
+ }
+
+ ///
+ /// Object that represents a value of type \a T that is calculated
+ /// lazily as soon as it is required.
+ ///
+ template<typename T>
+ class lazy {
+ public:
+ class undefined_error : std::logic_error {
+ public:
+ undefined_error() : std::logic_error("") {
+ }
+ };
+
+ ///
+ /// Initialize to some fixed value \a x which isn't calculated
+ /// lazily.
+ ///
+ lazy(T x) : obj(new detail::strict_lazy<T>(x)) {
+ }
+
+ ///
+ /// Initialize by providing a functor \a f that will calculate
+ /// the value on-demand.
+ ///
+ template<typename F>
+ lazy(F &&f) : obj(new detail::deferred_lazy<
+ T, typename std::remove_reference<F>::type
+ >(std::forward<F>(f))) {
+ }
+
+ ///
+ /// Initialize to undefined.
+ ///
+ lazy() : lazy([]() {
+ throw undefined_error();
+ return T();
+ }) {
+ }
+
+ lazy(const lazy &other) : obj(obj->clone()) {
+ }
+
+ lazy(lazy &&other) : obj(NULL) {
+ std::swap(obj, other.obj);
+ }
+
+ ~lazy() {
+ delete obj;
+ }
+
+ lazy &
+ operator=(lazy other) {
+ std::swap(obj, other.obj);
+ return *this;
+ }
+
+ ///
+ /// Evaluate the value.
+ ///
+ operator T() const {
+ return *obj;
+ }
+
+ private:
+ detail::basic_lazy<T> *obj;
+ };
+}
+
+#endif
diff --git a/src/gallium/frontends/clover/util/pointer.hpp b/src/gallium/frontends/clover/util/pointer.hpp
new file mode 100644
index 00000000000..7bb9951aef6
--- /dev/null
+++ b/src/gallium/frontends/clover/util/pointer.hpp
@@ -0,0 +1,284 @@
+//
+// Copyright 2013 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#ifndef CLOVER_UTIL_POINTER_HPP
+#define CLOVER_UTIL_POINTER_HPP
+
+#include <atomic>
+
+namespace clover {
+ ///
+ /// Some helper functions for raw pointer operations
+ ///
+ template <class T>
+ static bool
+ ptr_is_aligned(const T *ptr, uintptr_t a) noexcept {
+ assert(a == (a & -a));
+ uintptr_t ptr_value = reinterpret_cast<uintptr_t>(ptr);
+ return (ptr_value & (a - 1)) == 0;
+ }
+
+ ///
+ /// Base class for objects that support reference counting.
+ ///
+ class ref_counter {
+ public:
+ ref_counter(unsigned value = 1) : _ref_count(value) {}
+
+ unsigned
+ ref_count() const {
+ return _ref_count;
+ }
+
+ void
+ retain() {
+ _ref_count++;
+ }
+
+ bool
+ release() {
+ return (--_ref_count) == 0;
+ }
+
+ private:
+ std::atomic<unsigned> _ref_count;
+ };
+
+ ///
+ /// Simple reference to a clover::ref_counter object. Unlike
+ /// clover::intrusive_ptr and clover::intrusive_ref, it does nothing
+ /// special when the reference count drops to zero.
+ ///
+ class ref_holder {
+ public:
+ ref_holder(ref_counter &o) : p(&o) {
+ p->retain();
+ }
+
+ ref_holder(const ref_holder &ref) :
+ ref_holder(*ref.p) {
+ }
+
+ ref_holder(ref_holder &&ref) :
+ p(ref.p) {
+ ref.p = NULL;
+ }
+
+ ~ref_holder() {
+ if (p)
+ p->release();
+ }
+
+ ref_holder &
+ operator=(ref_holder ref) {
+ std::swap(ref.p, p);
+ return *this;
+ }
+
+ bool
+ operator==(const ref_holder &ref) const {
+ return p == ref.p;
+ }
+
+ bool
+ operator!=(const ref_holder &ref) const {
+ return p != ref.p;
+ }
+
+ private:
+ ref_counter *p;
+ };
+
+ ///
+ /// Intrusive smart pointer for objects that implement the
+ /// clover::ref_counter interface.
+ ///
+ template<typename T>
+ class intrusive_ptr {
+ public:
+ intrusive_ptr(T *q = NULL) : p(q) {
+ if (p)
+ p->retain();
+ }
+
+ intrusive_ptr(const intrusive_ptr &ptr) :
+ intrusive_ptr(ptr.p) {
+ }
+
+ intrusive_ptr(intrusive_ptr &&ptr) :
+ p(ptr.p) {
+ ptr.p = NULL;
+ }
+
+ ~intrusive_ptr() {
+ if (p && p->release())
+ delete p;
+ }
+
+ intrusive_ptr &
+ operator=(intrusive_ptr ptr) {
+ std::swap(ptr.p, p);
+ return *this;
+ }
+
+ bool
+ operator==(const intrusive_ptr &ref) const {
+ return p == ref.p;
+ }
+
+ bool
+ operator!=(const intrusive_ptr &ref) const {
+ return p != ref.p;
+ }
+
+ T &
+ operator*() const {
+ return *p;
+ }
+
+ T *
+ operator->() const {
+ return p;
+ }
+
+ T *
+ operator()() const {
+ return p;
+ }
+
+ explicit operator bool() const {
+ return p;
+ }
+
+ explicit operator T *() const {
+ return p;
+ }
+
+ private:
+ T *p;
+ };
+
+ ///
+ /// Intrusive smart reference for objects that implement the
+ /// clover::ref_counter interface.
+ ///
+ template<typename T>
+ class intrusive_ref {
+ public:
+ intrusive_ref(T &o) : p(&o) {
+ p->retain();
+ }
+
+ intrusive_ref(const intrusive_ref &ref) :
+ intrusive_ref(*ref.p) {
+ }
+
+ intrusive_ref(intrusive_ref &&ref) :
+ p(ref.p) {
+ ref.p = NULL;
+ }
+
+ ~intrusive_ref() {
+ if (p && p->release())
+ delete p;
+ }
+
+ intrusive_ref &
+ operator=(intrusive_ref ref) {
+ std::swap(ref.p, p);
+ return *this;
+ }
+
+ bool
+ operator==(const intrusive_ref &ref) const {
+ return p == ref.p;
+ }
+
+ bool
+ operator!=(const intrusive_ref &ref) const {
+ return p != ref.p;
+ }
+
+ T &
+ operator()() const {
+ return *p;
+ }
+
+ operator T &() const {
+ return *p;
+ }
+
+ private:
+ T *p;
+ };
+
+ ///
+ /// Initialize a clover::intrusive_ref from a newly created object
+ /// using the specified constructor arguments.
+ ///
+ template<typename T, typename... As>
+ intrusive_ref<T>
+ create(As &&... as) {
+ intrusive_ref<T> ref { *new T(std::forward<As>(as)...) };
+ ref().release();
+ return ref;
+ }
+
+ ///
+ /// Class that implements the usual pointer interface but in fact
+ /// contains the object it seems to be pointing to.
+ ///
+ template<typename T>
+ class pseudo_ptr {
+ public:
+ pseudo_ptr(T x) : x(x) {
+ }
+
+ pseudo_ptr(const pseudo_ptr &p) : x(p.x) {
+ }
+
+ pseudo_ptr &
+ operator=(const pseudo_ptr &p) {
+ x = p.x;
+ return *this;
+ }
+
+ T &
+ operator*() {
+ return x;
+ }
+
+ T *
+ operator->() {
+ return &x;
+ }
+
+ explicit operator bool() const {
+ return true;
+ }
+
+ private:
+ T x;
+ };
+}
+
+#endif
diff --git a/src/gallium/frontends/clover/util/range.hpp b/src/gallium/frontends/clover/util/range.hpp
new file mode 100644
index 00000000000..b082359ee86
--- /dev/null
+++ b/src/gallium/frontends/clover/util/range.hpp
@@ -0,0 +1,419 @@
+//
+// Copyright 2013 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#ifndef CLOVER_UTIL_RANGE_HPP
+#define CLOVER_UTIL_RANGE_HPP
+
+#include <array>
+#include <vector>
+
+#include "util/adaptor.hpp"
+
+namespace clover {
+ ///
+ /// Class that identifies container types where the elements of a
+ /// range can be stored by the type conversion operator.
+ ///
+ /// \a T identifies the range element type.
+ ///
+ template<typename T, typename V>
+ struct range_store_traits;
+
+ template<typename T, typename S>
+ struct range_store_traits<T, std::vector<S>> {
+ typedef void enable;
+
+ template<typename R>
+ static std::vector<S>
+ create(const R &r) {
+ return { r.begin(), r.end() };
+ }
+ };
+
+ template<typename T, typename S, std::size_t N>
+ struct range_store_traits<T, std::array<S, N>> {
+ typedef void enable;
+
+ template<typename R>
+ static std::array<S, N>
+ create(const R &r) {
+ std::array<S, N> v;
+ assert(r.size() == v.size());
+ copy(r, v.begin());
+ return v;
+ }
+ };
+
+ namespace detail {
+ ///
+ /// Common functionality that is shared by other implementations
+ /// of the container concept.
+ ///
+ template<typename R, typename I, typename CI>
+ class basic_range {
+ public:
+ typedef I iterator;
+ typedef CI const_iterator;
+ typedef typename std::iterator_traits<iterator>::value_type value_type;
+ typedef typename std::iterator_traits<iterator>::reference
+ reference;
+ typedef typename std::iterator_traits<const_iterator>::reference
+ const_reference;
+ typedef typename std::iterator_traits<iterator>::difference_type
+ difference_type;
+ typedef std::size_t size_type;
+
+ bool
+ operator==(const basic_range &r) const {
+ return *static_cast<const R *>(this) == r;
+ }
+
+ bool
+ operator!=(const basic_range &r) const {
+ return !(*this == r);
+ }
+
+ iterator
+ begin() {
+ return static_cast<R *>(this)->begin();
+ }
+
+ iterator
+ end() {
+ return static_cast<R *>(this)->end();
+ }
+
+ const_iterator
+ begin() const {
+ return static_cast<const R *>(this)->begin();
+ }
+
+ const_iterator
+ end() const {
+ return static_cast<const R *>(this)->end();
+ }
+
+ std::reverse_iterator<iterator>
+ rbegin() {
+ return { begin() };
+ }
+
+ std::reverse_iterator<iterator>
+ rend() {
+ return { end() };
+ }
+
+ reference
+ front() {
+ return *begin();
+ }
+
+ reference
+ back() {
+ return *(end() - 1);
+ }
+
+ bool
+ empty() const {
+ return begin() == end();
+ }
+
+ reference
+ at(size_type i) {
+ if (i >= static_cast<const R *>(this)->size())
+ throw std::out_of_range("");
+
+ return begin()[i];
+ }
+
+ const_reference
+ at(size_type i) const {
+ if (i >= static_cast<const R *>(this)->size())
+ throw std::out_of_range("");
+
+ return begin()[i];
+ }
+
+ reference
+ operator[](size_type i) {
+ return begin()[i];
+ }
+
+ const_reference
+ operator[](size_type i) const {
+ return begin()[i];
+ }
+
+ template<typename V>
+ using store_traits = range_store_traits<
+ typename std::remove_cv<value_type>::type, V
+ >;
+
+ template<typename V,
+ typename = typename store_traits<V>::enable>
+ operator V() const {
+ return store_traits<V>::create(*static_cast<const R *>(this));
+ }
+ };
+ }
+
+ ///
+ /// Range that contains all elements delimited by an iterator pair
+ /// (\a i, \a j). Use range() as convenience constructor.
+ ///
+ template<typename I>
+ class iterator_range : public detail::basic_range<iterator_range<I>, I, I> {
+ public:
+ typedef detail::basic_range<iterator_range<I>, I, I> super;
+
+ iterator_range() : i(), j() {
+ }
+
+ iterator_range(I i, I j) : i(i), j(j) {
+ }
+
+ bool
+ operator==(const iterator_range &r) const {
+ return i == r.i && j == r.j;
+ }
+
+ I
+ begin() const {
+ return i;
+ }
+
+ I
+ end() const {
+ return j;
+ }
+
+ typename super::size_type
+ size() const {
+ return end() - begin();
+ }
+
+ private:
+ I i, j;
+ };
+
+ namespace detail {
+ template<typename T>
+ using preferred_iterator_type = decltype(std::declval<T>().begin());
+ }
+
+ ///
+ /// Range that transforms the contents of a number of source ranges
+ /// \a os element-wise by using the provided functor \a f. Use
+ /// map() as convenience constructor.
+ ///
+ template<typename F, typename... Os>
+ class adaptor_range :
+ public detail::basic_range<adaptor_range<F, Os...>,
+ detail::iterator_adaptor<
+ F, detail::preferred_iterator_type<Os>...>,
+ detail::iterator_adaptor<
+ F, detail::preferred_iterator_type<const Os>...>
+ > {
+ public:
+ typedef detail::basic_range<adaptor_range<F, Os...>,
+ detail::iterator_adaptor<
+ F, detail::preferred_iterator_type<Os>...>,
+ detail::iterator_adaptor<
+ F, detail::preferred_iterator_type<const Os>...>
+ > super;
+
+ template<typename G, typename... Rs>
+ adaptor_range(G &&f, Rs &&... os) :
+ f(std::forward<G>(f)), os(std::forward<Rs>(os)...) {
+ }
+
+ bool
+ operator==(const adaptor_range &r) const {
+ return f == r.f && os == r.os;
+ }
+
+ typename super::iterator
+ begin() {
+ return { f, tuple::map(begins(), os) };
+ }
+
+ typename super::iterator
+ end() {
+ return { f, tuple::map(advances_by(size()),
+ tuple::map(begins(), os)) };
+ }
+
+ typename super::const_iterator
+ begin() const {
+ return { f, tuple::map(begins(), os) };
+ }
+
+ typename super::const_iterator
+ end() const {
+ return { f, tuple::map(advances_by(size()),
+ tuple::map(begins(), os)) };
+ }
+
+ typename super::size_type
+ size() const {
+ return tuple::apply(minimum(), tuple::map(sizes(), os));
+ }
+
+ private:
+ F f;
+ std::tuple<Os...> os;
+ };
+
+ ///
+ /// Range that contains all elements delimited by the index pair
+ /// (\a i, \a j) in the source range \a r. Use slice() as
+ /// convenience constructor.
+ ///
+ template<typename O>
+ class slice_range :
+ public detail::basic_range<slice_range<O>,
+ detail::preferred_iterator_type<O>,
+ detail::preferred_iterator_type<const O>> {
+ public:
+ typedef detail::basic_range<slice_range<O>,
+ detail::preferred_iterator_type<O>,
+ detail::preferred_iterator_type<const O>
+ > super;
+
+ template<typename R>
+ slice_range(R &&r, typename super::size_type i,
+ typename super::size_type j) :
+ o(std::forward<R>(r)), i(i), j(j) {
+ }
+
+ bool
+ operator==(const slice_range &r) const {
+ return o == r.o && i == r.i && j == r.j;
+ }
+
+ typename super::iterator
+ begin() {
+ return std::next(o.begin(), i);
+ }
+
+ typename super::iterator
+ end() {
+ return std::next(o.begin(), j);
+ }
+
+ typename super::const_iterator
+ begin() const {
+ return std::next(o.begin(), i);
+ }
+
+ typename super::const_iterator
+ end() const {
+ return std::next(o.begin(), j);
+ }
+
+ typename super::size_type
+ size() const {
+ return j - i;
+ }
+
+ private:
+ O o;
+ typename super::size_type i, j;
+ };
+
+ ///
+ /// Create a range from an iterator pair (\a i, \a j).
+ ///
+ /// \sa iterator_range.
+ ///
+ template<typename T>
+ iterator_range<T>
+ range(T i, T j) {
+ return { i, j };
+ }
+
+ ///
+ /// Create a range of \a n elements starting from iterator \a i.
+ ///
+ /// \sa iterator_range.
+ ///
+ template<typename T>
+ iterator_range<T>
+ range(T i, typename std::iterator_traits<T>::difference_type n) {
+ return { i, i + n };
+ }
+
+ ///
+ /// Create a range by transforming the contents of a number of
+ /// source ranges \a rs element-wise using a provided functor \a f.
+ ///
+ /// \sa adaptor_range.
+ ///
+ template<typename F, typename... Rs>
+ adaptor_range<F, Rs...>
+ map(F &&f, Rs &&... rs) {
+ return { std::forward<F>(f), std::forward<Rs>(rs)... };
+ }
+
+ ///
+ /// Create a range identical to another range \a r.
+ ///
+ template<typename R>
+ adaptor_range<identity, R>
+ range(R &&r) {
+ return { identity(), std::forward<R>(r) };
+ }
+
+ ///
+ /// Create a range by taking the elements delimited by the index
+ /// pair (\a i, \a j) in a source range \a r.
+ ///
+ /// \sa slice_range.
+ ///
+ template<typename R>
+ slice_range<R>
+ slice(R &&r, typename slice_range<R>::size_type i,
+ typename slice_range<R>::size_type j) {
+ return { std::forward<R>(r), i, j };
+ }
+
+ ///
+ /// Range that behaves as a vector of references of type \a T.
+ ///
+ /// Useful because STL containers cannot contain references to
+ /// objects as elements.
+ ///
+ template<typename T>
+ class ref_vector : public adaptor_range<derefs, std::vector<T *>> {
+ public:
+ ref_vector(std::initializer_list<std::reference_wrapper<T>> il) :
+ adaptor_range<derefs, std::vector<T *>>(derefs(), map(addresses(), il)) {
+ }
+
+ template<typename R>
+ ref_vector(R &&r) : adaptor_range<derefs, std::vector<T *>>(
+ derefs(), map(addresses(), std::forward<R>(r))) {
+ }
+ };
+}
+
+#endif
diff --git a/src/gallium/frontends/clover/util/tuple.hpp b/src/gallium/frontends/clover/util/tuple.hpp
new file mode 100644
index 00000000000..bd49684a314
--- /dev/null
+++ b/src/gallium/frontends/clover/util/tuple.hpp
@@ -0,0 +1,117 @@
+//
+// Copyright 2013 Francisco Jerez
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#ifndef CLOVER_UTIL_TUPLE_HPP
+#define CLOVER_UTIL_TUPLE_HPP
+
+#include <tuple>
+
+namespace clover {
+ namespace tuple {
+ ///
+ /// Static sequence of integers.
+ ///
+ template<int... Is>
+ struct integral_sequence;
+
+ ///
+ /// Static sequence containing all integers from 0 to N-1.
+ ///
+ template<int N, int... Is>
+ struct enumerate {
+ typedef typename enumerate<N-1, N-1, Is...>::type
+ type;
+ };
+
+ template<int... Is>
+ struct enumerate<0, Is...> {
+ typedef integral_sequence<Is...> type;
+ };
+
+ namespace detail {
+ template<typename F, typename T,
+ typename E = typename enumerate<std::tuple_size<
+ typename std::remove_reference<T>::type>::value
+ >::type>
+ struct _apply;
+
+ template<typename F, typename T, int... Is>
+ struct _apply<F, T, integral_sequence<Is...>> {
+ typedef typename std::remove_reference<F>::type func_type;
+ typedef decltype(
+ std::declval<func_type>()(std::get<Is>(std::declval<T &&>())...)
+ ) value_type;
+
+ static value_type
+ eval(F &&f, T &&t) {
+ return f(std::get<Is>(std::forward<T>(t))...);
+ }
+ };
+ }
+
+ ///
+ /// Evaluate function \a f with the elements of tuple \a t
+ /// expanded as arguments.
+ ///
+ template<typename F, typename T>
+ typename detail::_apply<F, T>::value_type
+ apply(F &&f, T &&t) {
+ return detail::_apply<F, T>::eval(std::forward<F>(f),
+ std::forward<T>(t));
+ }
+
+ namespace detail {
+ template<typename F, typename T,
+ typename E = typename enumerate<std::tuple_size<
+ typename std::remove_reference<T>::type>::value
+ >::type>
+ struct _map;
+
+ template<typename F, typename T, int... Is>
+ struct _map<F, T, integral_sequence<Is...>> {
+ typedef typename std::remove_reference<F>::type func_type;
+ typedef std::tuple<
+ decltype(std::declval<func_type>()(
+ std::get<Is>(std::declval<T &&>())))...
+ > value_type;
+
+ static value_type
+ eval(F &&f, T &&t) {
+ return value_type(f(std::get<Is>(std::forward<T>(t)))...);
+ }
+ };
+ }
+
+ ///
+ /// Evaluate function \a f on each element of the tuple \a t and
+ /// return the resulting values as a new tuple.
+ ///
+ template<typename F, typename T>
+ typename detail::_map<F, T>::value_type
+ map(F &&f, T &&t) {
+ return detail::_map<F, T>::eval(std::forward<F>(f),
+ std::forward<T>(t));
+ }
+ }
+}
+
+#endif