Index: streamexecutor/CMakeLists.txt =================================================================== --- /dev/null +++ streamexecutor/CMakeLists.txt @@ -0,0 +1,50 @@ +cmake_minimum_required(VERSION 3.1) + +option(STREAM_EXECUTOR_UNIT_TESTS "enable unit tests" ON) + +# First find includes relative to the streamexecutor top-level source path. +include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}/include) + +# If we are not building as part of LLVM, build StreamExecutor as a standalone +# project using LLVM as an external library: +string( + COMPARE + EQUAL + "${CMAKE_SOURCE_DIR}" + "${CMAKE_CURRENT_SOURCE_DIR}" + STREAM_EXECUTOR_STANDALONE) + +if(STREAM_EXECUTOR_STANDALONE) + project(StreamExecutor) + + find_package(LLVM REQUIRED CONFIG) + message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") + message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") + + include_directories(${LLVM_INCLUDE_DIRS}) + add_definitions(${LLVM_DEFINITIONS}) + + # Find the libraries that correspond to the LLVM components + # that we wish to use + llvm_map_components_to_libnames(llvm_libs support) + + if(STREAM_EXECUTOR_UNIT_TESTS) + enable_testing() + find_package(GTest REQUIRED) + include_directories(${GTEST_INCLUDE_DIRS}) + endif() +else(NOT STREAM_EXECUTOR_STANDALONE) + if(STREAM_EXECUTOR_UNIT_TESTS) + include_directories( + "${LLVM_MAIN_SRC_DIR}/utils/unittest/googletest/include") + endif() +endif(STREAM_EXECUTOR_STANDALONE) + +# Insist on C++ 11 features. +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# Add warning flags. +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wno-unused-parameter") + +add_subdirectory(lib) Index: streamexecutor/README.txt =================================================================== --- streamexecutor/README.txt +++ streamexecutor/README.txt @@ -1 +1,11 @@ -This directory will contain the StreamExecutor library. +StreamExecutor +============== + +StreamExecutor is a wrapper around CUDA and OpenCL (host-side) programming +models (runtimes). This abstraction cleanly permits host code to target either +CUDA or OpenCL devices with identically-functioning data parallel kernels. It +manages the execution of concurrent work targeting the accelerator, similar to a +host-side Executor. + +This version of StreamExecutor can be built either as a sub-project of the LLVM +project or as a standalone project depending on LLVM as an external package. Index: streamexecutor/include/streamexecutor/Utils/Error.h =================================================================== --- /dev/null +++ streamexecutor/include/streamexecutor/Utils/Error.h @@ -0,0 +1,184 @@ +//===-- Error.h - Error handling --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Error types used in the public API and internally in StreamExecutor. +/// +/// StreamExecutor's error handling is based on the types streamexecutor::Error +/// and streamexecutor::Expected. +/// +/// +/// \section error The Error Class +/// +/// The Error class either represents success or contains an error message +/// describing the cause of the error. Error instances are created by calling +/// Error::success for successes or make_error for errors. Error instances are +/// also implicitly convertable to bool and are true for errors and false for +/// successes. +/// +/// Error instances must have their boolean value checked or they must be moved +/// before they go out of scope, otherwise they will cause the program to abort +/// with a warning about an unchecked error. If the error represents success, +/// then checking the boolean value is all that is required, but if the error +/// represents a real error, the error value must be consumed. The function +/// consumeAndGetMessage is the way to extract the error message from an error +/// message and consume the error at the same time, so typical error handling +/// will first check whether there was an error and then extract the error +/// message if so. Here is an example: +/// +/// \code{.cpp} +/// if (Error E = doSomething()) { +/// printf("An error occurred: %s\n", consumeAndGetMessage(E).c_str()); +/// exit(EXIT_FAILURE): +/// } +/// \endcode +/// +/// It is also common to simply pass an error along up the call stack if it +/// cannot be handled in the current function. +/// +/// \code{.cpp} +/// Error doTask() { +/// if (Error E = doSubTask()) { +/// return E; +/// } +/// ... +/// } +/// \endcode +/// +/// There is also a function consumeError that consumes an error value without +/// fetching the error message. This is useful when we want to ignore an error. +/// +/// +/// \section expected The Expected Class +/// +/// The Expected class either represents a value of type T or an Error. +/// Expected has a constructor that takes a T value and another constructor +/// that takes an Error rvalue reference, so Expected instances can be +/// constructed from values or errors: +/// +/// \code{.cpp} +/// Expected getMyFavoriteInt() { +/// int MyFavorite = 42; +/// if (IsThereAFavorite) { +/// return MyFavorite; +/// } else { +/// return make_error("I don't have a favorite"); +/// } +/// } +/// \endcode +/// +/// Expected instances are implicitly convertible to bool and are true if +/// they contain a value and false if they contain an error. Note that this is +/// the opposite convention of the Error type conversion to bool where true +/// meant error and false meant success. +/// +/// If the Expected instance is not an error, the stored value can be +/// obtained by using the * pointer dereference operator. If access to members +/// of the value are desired instead of the value itself, the -> pointer +/// dereference operator can be used as well. +/// +/// Expected instances must have their boolean value checked or they must be +/// moved before they go out of scope, otherwise they will cause the program to +/// abort with a warning about an unchecked error. If the Expected instance +/// contains a value, then checking the boolean value is all that is required, +/// but if it contains an Error object, that Error object must be handled by +/// calling Expected::takeError() to get the underlying error. +/// +/// Here is an example of the use of an Expected value returned from a +/// function: +/// +/// \code{.cpp} +/// Expected ExpectedInt = getMyFavoriteInt(); +/// if (ExpectedInt) { +/// printf("My favorite integer is %d\n", *ExpectedInt); +/// } else { +/// printf("An error occurred: %s\n", +/// consumeAndGetMessage(ExpectedInt.takeError())); +/// exit(EXIT_FAILURE); +/// } +/// \endcode +/// +/// It is also common to simply return the error if the expected value contains +/// an error inside a function that returns an Error: +/// +/// \code{.cpp} +/// Error doTask3() { +/// Expected ExpectedInt = getMyFavoriteInt(); +/// if (!ExpectedInt) { +/// return ExpectedInt.takeError(); +/// } +/// ... +/// } +/// \endcode +/// +/// Or if the containing function returns an Expected, the error can be +/// passed up the stack: +/// +/// \code{.cpp} +/// Expected doTask4() { +/// Expected ExpectedInt = getMyFavoriteInt(); +/// if (!ExpectedInt) { +/// return ExpectedInt.takeError(); +/// } +/// ... +/// } +/// \endcode +/// +/// +/// \section llvm Relation to llvm::Error and llvm::Expected +/// +/// The streamexecutor::Error and streamexecutor::Expected classes are actually +/// just their LLVM counterparts redeclared in the streamexectuor namespace, but +/// they should be treated as separate types, even so. +/// +/// StreamExecutor does not support any underlying llvm::ErrorInfo class except +/// the one it defines internally for itself, so a streamexecutor::Error can be +/// thought of as a restricted llvm::Error that is guaranteed to hold a specific +/// error type. +/// +/// Although code may compile if llvm functions used to handle these +/// StreamExecutor error types, it is likely that code will lead to runtime +/// errors, so it is strongly recommended that only the functions from the +/// streamexecutor namespace are used on these StreamExecutor error types. +/// +//===----------------------------------------------------------------------===// + +#ifndef STREAMEXECUTOR_UTILS_ERROR_H +#define STREAMEXECUTOR_UTILS_ERROR_H + +#include +#include + +#include + +namespace streamexecutor { + +using llvm::consumeError; +using llvm::Error; +using llvm::Expected; +using llvm::StringRef; + +// Makes an Error object from an error message. +Error make_error(StringRef Message); + +// Consumes the input error and returns its error message. +// +// If there is no error, the returned message matches the return value of +// getSuccessMessage. +// +// Assumes the input was created by the make_error function above. +std::string consumeAndGetMessage(Error &&E); + +// Gets the message which is returned by consumeAndGetMessage if there is no +// error. +static inline std::string getSuccessMessage() { return "success"; } + +} // namespace streamexecutor + +#endif // STREAMEXECUTOR_UTILS_ERROR_H Index: streamexecutor/include/streamexecutor/Utils/Mutex.h =================================================================== --- /dev/null +++ streamexecutor/include/streamexecutor/Utils/Mutex.h @@ -0,0 +1,28 @@ +//===-- Mutex.h - Mutex utilities -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Declares macros for use with mutexes. +/// +//===----------------------------------------------------------------------===// + +#ifndef STREAMEXECUTOR_UTILS_MUTEX_H +#define STREAMEXECUTOR_UTILS_MUTEX_H + +/// Document if a shared variable/field needs to be protected by a mutex. +/// +/// SE_GUARDED_BY allows the user to specify a particular mutex that should be +/// held when accessing the annotated variable. +#if defined(__clang__) && (!defined(SWIG)) +#define SE_GUARDED_BY(x) __attribute__((guarded_by(x))) +#else +#define SE_GUARDED_BY(x) // no-op +#endif + +#endif // STREAMEXECUTOR_UTILS_MUTEX_H Index: streamexecutor/lib/CMakeLists.txt =================================================================== --- /dev/null +++ streamexecutor/lib/CMakeLists.txt @@ -0,0 +1,4 @@ +add_library( + utils + OBJECT + Utils/Error.cpp) Index: streamexecutor/lib/Utils/Error.cpp =================================================================== --- /dev/null +++ streamexecutor/lib/Utils/Error.cpp @@ -0,0 +1,64 @@ +//===-- Error.cpp - Error handling ----------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Types for returning recoverable errors. +/// +//===----------------------------------------------------------------------===// + +#include "streamexecutor/Utils/Error.h" + +#include + +namespace { + +// An error with a string message describing the cause. +class StreamExecutorError : public llvm::ErrorInfo { +public: + StreamExecutorError(llvm::StringRef Message) + : Message(Message.begin(), Message.end()) {} + + void log(llvm::raw_ostream &OS) const override { OS << Message; } + + std::error_code convertToErrorCode() const override { + llvm_unreachable( + "StreamExecutorError does not support convertion to std::error_code"); + } + + std::string getErrorMessage() const { return Message; } + + static char ID; + +private: + std::string Message; +}; + +char StreamExecutorError::ID = 0; + +} // namespace + +namespace streamexecutor { + +Error make_error(StringRef Message) { + return llvm::make_error(Message); +} + +std::string consumeAndGetMessage(Error &&E) { + if (!E) { + return getSuccessMessage(); + } + std::string Message; + llvm::handleAllErrors(std::move(E), + [&Message](const StreamExecutorError &SEE) { + Message = SEE.getErrorMessage(); + }); + return Message; +} + +} // namespace streamexecutor