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. +/// +/// \code{.cpp} +/// Error achieveWorldPeace() { +/// if (WorldPeaceAlreadyAchieved) { +/// return Error::success(); +/// } else { +/// return make_error("Can't someone else do it?"); +/// } +/// } +/// \endcode +/// +/// Error instances are implicitly convertable to bool. Error values convert to +/// true and successes convert to false. Error instances must have their boolean +/// values checked or they must be moved before they go out of scope, otherwise +/// their destruction 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 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 = achieveWorldPeace()) { +/// 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 = achieveWorldPeace()) { +/// 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 one constructor that takes a T value and another constructor +/// that takes an Error rvalue reference, so Expected instances can be +/// constructed either from values or from 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 operator*. If access to members of the value are desired +/// instead of the value itself, 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 +/// +/// The following snippet shows some examples of how Errors and Expected values +/// can be passed up the stack if they should not be handled in the current +/// function. +/// +/// \code{.cpp} +/// Expected doTask3() { +/// Error WorldPeaceError = achieveWorldPeace(); +/// if (!WorldPeaceError) { +/// return WorldPeaceError; +/// } +/// +/// Expected ExpectedMartian = getMyFavoriteMartian(); +/// if (!ExpectedMartian) { +/// // Must extract the error because martian cannot be converted to double. +/// return ExpectedMartian.takeError(): +/// } +/// +/// // It's fine to return Expected for Expected because int can +/// // be converted to double. +/// return getMyFavoriteInt(); +/// } +/// \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 "llvm/Support/Error.h" + +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. +// +// Assumes the input was created by the make_error function above. +std::string consumeAndGetMessage(Error &&E); + +} // 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,63 @@ +//===-- 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 "llvm/ADT/StringRef.h" + +namespace { + +// An error with a string message describing the cause. +class StreamExecutorError : public llvm::ErrorInfo { +public: + StreamExecutorError(llvm::StringRef Message) : Message(Message.str()) {} + + 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 "success"; + } + std::string Message; + llvm::handleAllErrors(std::move(E), + [&Message](const StreamExecutorError &SEE) { + Message = SEE.getErrorMessage(); + }); + return Message; +} + +} // namespace streamexecutor