Skip to content

Commit

Permalink
[StreamExecutor] Add kernel types
Browse files Browse the repository at this point in the history
Summary: Add StreamExecutor kernel types.

Reviewers: jlebar, tra

Subscribers: parallel_libs-commits

Differential Revision: https://reviews.llvm.org/D23138

llvm-svn: 277827
  • Loading branch information
henline committed Aug 5, 2016
1 parent 2a06f48 commit 7b1fbea
Show file tree
Hide file tree
Showing 9 changed files with 383 additions and 1 deletion.
2 changes: 1 addition & 1 deletion parallel-libs/streamexecutor/CMakeLists.txt
Expand Up @@ -26,7 +26,7 @@ if(STREAM_EXECUTOR_STANDALONE)

# Find the libraries that correspond to the LLVM components
# that we wish to use
llvm_map_components_to_libnames(llvm_libs support)
llvm_map_components_to_libnames(llvm_libs support symbolize)

if(STREAM_EXECUTOR_UNIT_TESTS)
enable_testing()
Expand Down
29 changes: 29 additions & 0 deletions parallel-libs/streamexecutor/include/streamexecutor/Interfaces.h
@@ -0,0 +1,29 @@
//===-- Interfaces.h - Interfaces to platform-specific impls ----*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// Interfaces to platform-specific StreamExecutor type implementations.
///
//===----------------------------------------------------------------------===//

#ifndef STREAMEXECUTOR_INTERFACES_H
#define STREAMEXECUTOR_INTERFACES_H

namespace streamexecutor {

/// Methods supported by device kernel function objects on all platforms.
class KernelInterface {
// TODO(jhen): Add methods.
};

// TODO(jhen): Add other interfaces such as Stream.

} // namespace streamexecutor

#endif // STREAMEXECUTOR_INTERFACES_H
158 changes: 158 additions & 0 deletions parallel-libs/streamexecutor/include/streamexecutor/Kernel.h
@@ -0,0 +1,158 @@
//===-- Kernel.h - StreamExecutor kernel types ------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// Types to represent device kernels (code compiled to run on GPU or other
/// accelerator).
///
/// The TypedKernel class is used to provide type safety to the user API's
/// launch functions, and the KernelBase class is used like a void* function
/// pointer to perform type-unsafe operations inside StreamExecutor.
///
/// With the kernel parameter types recorded in the TypedKernel template
/// parameters, type-safe kernel launch functions can be written with signatures
/// like the following:
/// \code
/// template <typename... ParameterTs>
/// void Launch(
/// const TypedKernel<ParameterTs...> &Kernel, ParamterTs... Arguments);
/// \endcode
/// and the compiler will check that the user passes in arguments with types
/// matching the corresponding kernel parameters.
///
/// A problem is that a TypedKernel template specialization with the right
/// parameter types must be passed as the first argument to the Launch function,
/// and it's just as hard to get the types right in that template specialization
/// as it is to get them right for the kernel arguments.
///
/// With this problem in mind, it is not recommended for users to specialize the
/// TypedKernel template class themselves, but instead to let the compiler do it
/// for them. When the compiler encounters a device kernel function, it can
/// create a TypedKernel template specialization in the host code that has the
/// right parameter types for that kernel and which has a type name based on the
/// name of the kernel function.
///
/// For example, if a CUDA device kernel function with the following signature
/// has been defined:
/// \code
/// void Saxpy(float *A, float *X, float *Y);
/// \endcode
/// the compiler can insert the following declaration in the host code:
/// \code
/// namespace compiler_cuda_namespace {
/// using SaxpyKernel =
/// streamexecutor::TypedKernel<float *, float *, float *>;
/// } // namespace compiler_cuda_namespace
/// \endcode
/// and then the user can launch the kernel by calling the StreamExecutor launch
/// function as follows:
/// \code
/// namespace ccn = compiler_cuda_namespace;
/// // Assumes Executor is a pointer to the StreamExecutor on which to
/// // launch the kernel.
/// //
/// // See KernelSpec.h for details on how the compiler can create a
/// // MultiKernelLoaderSpec instance like SaxpyKernelLoaderSpec below.
/// Expected<ccn::SaxpyKernel> MaybeKernel =
/// ccn::SaxpyKernel::create(Executor, ccn::SaxpyKernelLoaderSpec);
/// if (!MaybeKernel) { /* Handle error */ }
/// ccn::SaxpyKernel SaxpyKernel = *MaybeKernel;
/// Launch(SaxpyKernel, A, X, Y);
/// \endcode
///
/// With the compiler's help in specializing TypedKernel for each device kernel
/// function (and generating a MultiKernelLoaderSpec instance for each kernel),
/// the user can safely launch the device kernel from the host and get an error
/// message at compile time if the argument types don't match the kernel
/// parameter types.
///
//===----------------------------------------------------------------------===//

#ifndef STREAMEXECUTOR_KERNEL_H
#define STREAMEXECUTOR_KERNEL_H

#include "streamexecutor/KernelSpec.h"
#include "streamexecutor/Utils/Error.h"

#include <memory>

namespace streamexecutor {

class KernelInterface;
class StreamExecutor;

/// The base class for device kernel functions.
///
/// This class has no information about the types of the parameters taken by the
/// kernel, so it is analogous to a void* pointer to a device function.
///
/// See the TypedKernel class below for the subclass which does have information
/// about parameter types.
class KernelBase {
public:
KernelBase(KernelBase &&) = default;
KernelBase &operator=(KernelBase &&) = default;
~KernelBase();

/// Creates a kernel object from a StreamExecutor and a MultiKernelLoaderSpec.
///
/// The StreamExecutor knows which platform it belongs to and the
/// MultiKernelLoaderSpec knows how to find the kernel code for different
/// platforms, so the combined information is enough to get the kernel code
/// for the appropriate platform.
static Expected<KernelBase> create(StreamExecutor *ParentExecutor,
const MultiKernelLoaderSpec &Spec);

const std::string &getName() const { return Name; }
const std::string &getDemangledName() const { return DemangledName; }

/// Gets a pointer to the platform-specific implementation of this kernel.
KernelInterface *getImplementation() { return Implementation.get(); }

private:
KernelBase(StreamExecutor *ParentExecutor, const std::string &Name,
const std::string &DemangledName,
std::unique_ptr<KernelInterface> Implementation);

StreamExecutor *ParentExecutor;
std::string Name;
std::string DemangledName;
std::unique_ptr<KernelInterface> Implementation;

KernelBase(const KernelBase &) = delete;
KernelBase &operator=(const KernelBase &) = delete;
};

/// A device kernel function with specified parameter types.
template <typename... ParameterTs> class TypedKernel : public KernelBase {
public:
TypedKernel(TypedKernel &&) = default;
TypedKernel &operator=(TypedKernel &&) = default;

/// Parameters here have the same meaning as in KernelBase::create.
static Expected<TypedKernel> create(StreamExecutor *ParentExecutor,
const MultiKernelLoaderSpec &Spec) {
auto MaybeBase = KernelBase::create(ParentExecutor, Spec);
if (!MaybeBase) {
return MaybeBase.takeError();
}
TypedKernel Instance(std::move(*MaybeBase));
return std::move(Instance);
}

private:
TypedKernel(KernelBase &&Base) : KernelBase(std::move(Base)) {}

TypedKernel(const TypedKernel &) = delete;
TypedKernel &operator=(const TypedKernel &) = delete;
};

} // namespace streamexecutor

#endif // STREAMEXECUTOR_KERNEL_H
Expand Up @@ -198,6 +198,13 @@ class OpenCLTextInMemorySpec : public KernelLoaderSpec {
/// than doing it by hand.
class MultiKernelLoaderSpec {
public:
std::string getKernelName() const {
if (TheKernelName) {
return *TheKernelName;
}
return "";
}

// Convenience getters for testing whether these platform variants have
// kernel loader specifications available.

Expand Down
@@ -0,0 +1,39 @@
//===-- StreamExecutor.h - The StreamExecutor class -------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// The StreamExecutor class which represents a single device of a specific
/// platform.
///
//===----------------------------------------------------------------------===//

#ifndef STREAMEXECUTOR_STREAMEXECUTOR_H
#define STREAMEXECUTOR_STREAMEXECUTOR_H

#include "streamexecutor/Utils/Error.h"

namespace streamexecutor {

class KernelInterface;

class StreamExecutor {
public:
/// Gets the kernel implementation for the underlying platform.
virtual Expected<std::unique_ptr<KernelInterface>>
getKernelImplementation(const MultiKernelLoaderSpec &Spec) {
// TODO(jhen): Implement this.
return nullptr;
}

// TODO(jhen): Add other methods.
};

} // namespace streamexecutor

#endif // STREAMEXECUTOR_STREAMEXECUTOR_H
2 changes: 2 additions & 0 deletions parallel-libs/streamexecutor/lib/CMakeLists.txt
Expand Up @@ -6,7 +6,9 @@ add_library(
add_library(
streamexecutor
$<TARGET_OBJECTS:utils>
Kernel.cpp
KernelSpec.cpp)
target_link_libraries(streamexecutor ${llvm_libs})

if(STREAM_EXECUTOR_UNIT_TESTS)
add_subdirectory(unittests)
Expand Down
45 changes: 45 additions & 0 deletions parallel-libs/streamexecutor/lib/Kernel.cpp
@@ -0,0 +1,45 @@
//===-- Kernel.cpp - General kernel implementation ------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// This file contains the implementation details for kernel types.
///
//===----------------------------------------------------------------------===//

#include "streamexecutor/Kernel.h"
#include "streamexecutor/Interfaces.h"
#include "streamexecutor/StreamExecutor.h"

#include "llvm/DebugInfo/Symbolize/Symbolize.h"

namespace streamexecutor {

KernelBase::KernelBase(StreamExecutor *ParentExecutor, const std::string &Name,
const std::string &DemangledName,
std::unique_ptr<KernelInterface> Implementation)
: ParentExecutor(ParentExecutor), Name(Name), DemangledName(DemangledName),
Implementation(std::move(Implementation)) {}

KernelBase::~KernelBase() = default;

Expected<KernelBase> KernelBase::create(StreamExecutor *ParentExecutor,
const MultiKernelLoaderSpec &Spec) {
auto MaybeImplementation = ParentExecutor->getKernelImplementation(Spec);
if (!MaybeImplementation) {
return MaybeImplementation.takeError();
}
std::string Name = Spec.getKernelName();
std::string DemangledName =
llvm::symbolize::LLVMSymbolizer::DemangleName(Name, nullptr);
KernelBase Instance(ParentExecutor, Name, DemangledName,
std::move(*MaybeImplementation));
return std::move(Instance);
}

} // namespace streamexecutor
10 changes: 10 additions & 0 deletions parallel-libs/streamexecutor/lib/unittests/CMakeLists.txt
@@ -1,3 +1,13 @@
add_executable(
kernel_test
KernelTest.cpp)
target_link_libraries(
kernel_test
streamexecutor
${GTEST_BOTH_LIBRARIES}
${CMAKE_THREAD_LIBS_INIT})
add_test(KernelTest kernel_test)

add_executable(
kernel_spec_test
KernelSpecTest.cpp)
Expand Down

0 comments on commit 7b1fbea

Please sign in to comment.