2016-07-13 23:54:58 +08:00
|
|
|
//===------ PPCGCodeGeneration.cpp - Polly Accelerator Code Generation. ---===//
|
|
|
|
//
|
|
|
|
// The LLVM Compiler Infrastructure
|
|
|
|
//
|
|
|
|
// This file is distributed under the University of Illinois Open Source
|
|
|
|
// License. See LICENSE.TXT for details.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
|
|
|
// Take a scop created by ScopInfo and map it to GPU code using the ppcg
|
|
|
|
// GPU mapping strategy.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
[Polly] Added OpenCL Runtime to GPURuntime Library for GPGPU CodeGen
Summary:
When compiling for GPU, one can now choose to compile for OpenCL or CUDA,
with the corresponding polly-gpu-runtime flag (libopencl / libcudart). The
GPURuntime library (GPUJIT) has been extended with the OpenCL Runtime library
for that purpose, correctly choosing the corresponding library calls to the
option chosen when compiling (via different initialization calls).
Additionally, a specific GPU Target architecture can now be chosen with -polly-gpu-arch (only nvptx64 implemented thus far).
Reviewers: grosser, bollu, Meinersbur, etherzhhb, singam-sanjay
Reviewed By: grosser, Meinersbur
Subscribers: singam-sanjay, llvm-commits, pollydev, nemanjai, mgorny, yaxunl, Anastasia
Tags: #polly
Differential Revision: https://reviews.llvm.org/D32431
llvm-svn: 302379
2017-05-08 05:03:46 +08:00
|
|
|
#include "polly/CodeGen/PPCGCodeGeneration.h"
|
2016-08-09 01:35:55 +08:00
|
|
|
#include "polly/CodeGen/IslAst.h"
|
2016-07-13 23:54:58 +08:00
|
|
|
#include "polly/CodeGen/IslNodeBuilder.h"
|
2016-07-18 19:56:39 +08:00
|
|
|
#include "polly/CodeGen/Utils.h"
|
2016-07-13 23:54:58 +08:00
|
|
|
#include "polly/DependenceInfo.h"
|
|
|
|
#include "polly/LinkAllPasses.h"
|
2016-07-14 18:22:25 +08:00
|
|
|
#include "polly/Options.h"
|
2016-08-03 20:00:07 +08:00
|
|
|
#include "polly/ScopDetection.h"
|
2016-07-13 23:54:58 +08:00
|
|
|
#include "polly/ScopInfo.h"
|
2016-07-21 21:15:59 +08:00
|
|
|
#include "polly/Support/SCEVValidator.h"
|
2016-07-22 15:11:12 +08:00
|
|
|
#include "llvm/ADT/PostOrderIterator.h"
|
2016-07-13 23:54:58 +08:00
|
|
|
#include "llvm/Analysis/AliasAnalysis.h"
|
|
|
|
#include "llvm/Analysis/BasicAliasAnalysis.h"
|
|
|
|
#include "llvm/Analysis/GlobalsModRef.h"
|
|
|
|
#include "llvm/Analysis/ScalarEvolutionAliasAnalysis.h"
|
2016-07-22 15:11:12 +08:00
|
|
|
#include "llvm/Analysis/TargetLibraryInfo.h"
|
|
|
|
#include "llvm/Analysis/TargetTransformInfo.h"
|
|
|
|
#include "llvm/IR/LegacyPassManager.h"
|
2016-07-24 14:43:17 +08:00
|
|
|
#include "llvm/IR/Verifier.h"
|
2016-07-22 15:11:12 +08:00
|
|
|
#include "llvm/Support/TargetRegistry.h"
|
|
|
|
#include "llvm/Support/TargetSelect.h"
|
|
|
|
#include "llvm/Target/TargetMachine.h"
|
2016-07-24 14:43:21 +08:00
|
|
|
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
|
2016-08-09 23:35:03 +08:00
|
|
|
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
|
2016-07-13 23:54:58 +08:00
|
|
|
|
2016-07-14 18:22:25 +08:00
|
|
|
#include "isl/union_map.h"
|
|
|
|
|
2016-07-14 18:22:19 +08:00
|
|
|
extern "C" {
|
2016-07-15 15:50:36 +08:00
|
|
|
#include "ppcg/cuda.h"
|
|
|
|
#include "ppcg/gpu.h"
|
|
|
|
#include "ppcg/gpu_print.h"
|
|
|
|
#include "ppcg/ppcg.h"
|
|
|
|
#include "ppcg/schedule.h"
|
2016-07-14 18:22:19 +08:00
|
|
|
}
|
|
|
|
|
2016-07-13 23:54:58 +08:00
|
|
|
#include "llvm/Support/Debug.h"
|
|
|
|
|
|
|
|
using namespace polly;
|
|
|
|
using namespace llvm;
|
|
|
|
|
|
|
|
#define DEBUG_TYPE "polly-codegen-ppcg"
|
|
|
|
|
2016-07-14 18:22:25 +08:00
|
|
|
static cl::opt<bool> DumpSchedule("polly-acc-dump-schedule",
|
|
|
|
cl::desc("Dump the computed GPU Schedule"),
|
2016-07-14 18:51:47 +08:00
|
|
|
cl::Hidden, cl::init(false), cl::ZeroOrMore,
|
2016-07-14 18:22:25 +08:00
|
|
|
cl::cat(PollyCategory));
|
2016-07-14 23:51:37 +08:00
|
|
|
|
|
|
|
static cl::opt<bool>
|
|
|
|
DumpCode("polly-acc-dump-code",
|
|
|
|
cl::desc("Dump C code describing the GPU mapping"), cl::Hidden,
|
|
|
|
cl::init(false), cl::ZeroOrMore, cl::cat(PollyCategory));
|
|
|
|
|
2016-07-19 15:32:38 +08:00
|
|
|
static cl::opt<bool> DumpKernelIR("polly-acc-dump-kernel-ir",
|
|
|
|
cl::desc("Dump the kernel LLVM-IR"),
|
|
|
|
cl::Hidden, cl::init(false), cl::ZeroOrMore,
|
|
|
|
cl::cat(PollyCategory));
|
|
|
|
|
2016-07-22 15:11:12 +08:00
|
|
|
static cl::opt<bool> DumpKernelASM("polly-acc-dump-kernel-asm",
|
|
|
|
cl::desc("Dump the kernel assembly code"),
|
|
|
|
cl::Hidden, cl::init(false), cl::ZeroOrMore,
|
|
|
|
cl::cat(PollyCategory));
|
|
|
|
|
|
|
|
static cl::opt<bool> FastMath("polly-acc-fastmath",
|
|
|
|
cl::desc("Allow unsafe math optimizations"),
|
|
|
|
cl::Hidden, cl::init(false), cl::ZeroOrMore,
|
|
|
|
cl::cat(PollyCategory));
|
2016-08-04 20:18:14 +08:00
|
|
|
static cl::opt<bool> SharedMemory("polly-acc-use-shared",
|
|
|
|
cl::desc("Use shared memory"), cl::Hidden,
|
|
|
|
cl::init(false), cl::ZeroOrMore,
|
|
|
|
cl::cat(PollyCategory));
|
2016-08-04 20:39:03 +08:00
|
|
|
static cl::opt<bool> PrivateMemory("polly-acc-use-private",
|
|
|
|
cl::desc("Use private memory"), cl::Hidden,
|
|
|
|
cl::init(false), cl::ZeroOrMore,
|
|
|
|
cl::cat(PollyCategory));
|
2016-07-22 15:11:12 +08:00
|
|
|
|
2017-04-28 19:16:30 +08:00
|
|
|
static cl::opt<bool> ManagedMemory("polly-acc-codegen-managed-memory",
|
|
|
|
cl::desc("Generate Host kernel code assuming"
|
|
|
|
" that all memory has been"
|
|
|
|
" declared as managed memory"),
|
|
|
|
cl::Hidden, cl::init(false), cl::ZeroOrMore,
|
|
|
|
cl::cat(PollyCategory));
|
|
|
|
|
2016-07-22 15:11:12 +08:00
|
|
|
static cl::opt<std::string>
|
|
|
|
CudaVersion("polly-acc-cuda-version",
|
|
|
|
cl::desc("The CUDA version to compile for"), cl::Hidden,
|
|
|
|
cl::init("sm_30"), cl::ZeroOrMore, cl::cat(PollyCategory));
|
|
|
|
|
2016-09-18 14:50:35 +08:00
|
|
|
static cl::opt<int>
|
|
|
|
MinCompute("polly-acc-mincompute",
|
|
|
|
cl::desc("Minimal number of compute statements to run on GPU."),
|
|
|
|
cl::Hidden, cl::init(10 * 512 * 512));
|
|
|
|
|
2016-07-14 23:51:32 +08:00
|
|
|
/// Create the ast expressions for a ScopStmt.
|
|
|
|
///
|
|
|
|
/// This function is a callback for to generate the ast expressions for each
|
|
|
|
/// of the scheduled ScopStmts.
|
|
|
|
static __isl_give isl_id_to_ast_expr *pollyBuildAstExprForStmt(
|
2016-07-21 21:15:59 +08:00
|
|
|
void *StmtT, isl_ast_build *Build,
|
2016-07-14 23:51:32 +08:00
|
|
|
isl_multi_pw_aff *(*FunctionIndex)(__isl_take isl_multi_pw_aff *MPA,
|
|
|
|
isl_id *Id, void *User),
|
|
|
|
void *UserIndex,
|
|
|
|
isl_ast_expr *(*FunctionExpr)(isl_ast_expr *Expr, isl_id *Id, void *User),
|
2016-07-21 21:15:59 +08:00
|
|
|
void *UserExpr) {
|
2016-07-14 23:51:32 +08:00
|
|
|
|
2016-07-21 21:15:59 +08:00
|
|
|
ScopStmt *Stmt = (ScopStmt *)StmtT;
|
2016-07-14 23:51:32 +08:00
|
|
|
|
2016-07-21 21:15:59 +08:00
|
|
|
isl_ctx *Ctx;
|
|
|
|
|
|
|
|
if (!Stmt || !Build)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
Ctx = isl_ast_build_get_ctx(Build);
|
|
|
|
isl_id_to_ast_expr *RefToExpr = isl_id_to_ast_expr_alloc(Ctx, 0);
|
|
|
|
|
|
|
|
for (MemoryAccess *Acc : *Stmt) {
|
|
|
|
isl_map *AddrFunc = Acc->getAddressFunction();
|
|
|
|
AddrFunc = isl_map_intersect_domain(AddrFunc, Stmt->getDomain());
|
|
|
|
isl_id *RefId = Acc->getId();
|
|
|
|
isl_pw_multi_aff *PMA = isl_pw_multi_aff_from_map(AddrFunc);
|
|
|
|
isl_multi_pw_aff *MPA = isl_multi_pw_aff_from_pw_multi_aff(PMA);
|
|
|
|
MPA = isl_multi_pw_aff_coalesce(MPA);
|
|
|
|
MPA = FunctionIndex(MPA, RefId, UserIndex);
|
|
|
|
isl_ast_expr *Access = isl_ast_build_access_from_multi_pw_aff(Build, MPA);
|
|
|
|
Access = FunctionExpr(Access, RefId, UserExpr);
|
|
|
|
RefToExpr = isl_id_to_ast_expr_set(RefToExpr, RefId, Access);
|
|
|
|
}
|
|
|
|
|
|
|
|
return RefToExpr;
|
2016-07-14 23:51:32 +08:00
|
|
|
}
|
2016-07-14 18:22:25 +08:00
|
|
|
|
2017-05-09 18:45:52 +08:00
|
|
|
/// Given a LLVM Type, compute its size in bytes,
|
|
|
|
static int computeSizeInBytes(const Type *T) {
|
|
|
|
int bytes = T->getPrimitiveSizeInBits() / 8;
|
|
|
|
if (bytes == 0)
|
|
|
|
bytes = T->getScalarSizeInBits() / 8;
|
|
|
|
return bytes;
|
|
|
|
}
|
|
|
|
|
2016-07-18 19:56:39 +08:00
|
|
|
/// Generate code for a GPU specific isl AST.
|
|
|
|
///
|
|
|
|
/// The GPUNodeBuilder augments the general existing IslNodeBuilder, which
|
|
|
|
/// generates code for general-prupose AST nodes, with special functionality
|
|
|
|
/// for generating GPU specific user nodes.
|
|
|
|
///
|
|
|
|
/// @see GPUNodeBuilder::createUser
|
|
|
|
class GPUNodeBuilder : public IslNodeBuilder {
|
|
|
|
public:
|
2017-04-04 18:01:53 +08:00
|
|
|
GPUNodeBuilder(PollyIRBuilder &Builder, ScopAnnotator &Annotator,
|
2016-07-18 19:56:39 +08:00
|
|
|
const DataLayout &DL, LoopInfo &LI, ScalarEvolution &SE,
|
2016-11-03 06:32:23 +08:00
|
|
|
DominatorTree &DT, Scop &S, BasicBlock *StartBlock,
|
[Polly] Added OpenCL Runtime to GPURuntime Library for GPGPU CodeGen
Summary:
When compiling for GPU, one can now choose to compile for OpenCL or CUDA,
with the corresponding polly-gpu-runtime flag (libopencl / libcudart). The
GPURuntime library (GPUJIT) has been extended with the OpenCL Runtime library
for that purpose, correctly choosing the corresponding library calls to the
option chosen when compiling (via different initialization calls).
Additionally, a specific GPU Target architecture can now be chosen with -polly-gpu-arch (only nvptx64 implemented thus far).
Reviewers: grosser, bollu, Meinersbur, etherzhhb, singam-sanjay
Reviewed By: grosser, Meinersbur
Subscribers: singam-sanjay, llvm-commits, pollydev, nemanjai, mgorny, yaxunl, Anastasia
Tags: #polly
Differential Revision: https://reviews.llvm.org/D32431
llvm-svn: 302379
2017-05-08 05:03:46 +08:00
|
|
|
gpu_prog *Prog, GPURuntime Runtime, GPUArch Arch)
|
2017-04-04 18:01:53 +08:00
|
|
|
: IslNodeBuilder(Builder, Annotator, DL, LI, SE, DT, S, StartBlock),
|
[Polly] Added OpenCL Runtime to GPURuntime Library for GPGPU CodeGen
Summary:
When compiling for GPU, one can now choose to compile for OpenCL or CUDA,
with the corresponding polly-gpu-runtime flag (libopencl / libcudart). The
GPURuntime library (GPUJIT) has been extended with the OpenCL Runtime library
for that purpose, correctly choosing the corresponding library calls to the
option chosen when compiling (via different initialization calls).
Additionally, a specific GPU Target architecture can now be chosen with -polly-gpu-arch (only nvptx64 implemented thus far).
Reviewers: grosser, bollu, Meinersbur, etherzhhb, singam-sanjay
Reviewed By: grosser, Meinersbur
Subscribers: singam-sanjay, llvm-commits, pollydev, nemanjai, mgorny, yaxunl, Anastasia
Tags: #polly
Differential Revision: https://reviews.llvm.org/D32431
llvm-svn: 302379
2017-05-08 05:03:46 +08:00
|
|
|
Prog(Prog), Runtime(Runtime), Arch(Arch) {
|
2016-07-21 21:15:59 +08:00
|
|
|
getExprBuilder().setIDToSAI(&IDToSAI);
|
|
|
|
}
|
2016-07-18 19:56:39 +08:00
|
|
|
|
2016-07-25 17:16:01 +08:00
|
|
|
/// Create after-run-time-check initialization code.
|
|
|
|
void initializeAfterRTH();
|
|
|
|
|
|
|
|
/// Finalize the generated scop.
|
|
|
|
virtual void finalize();
|
|
|
|
|
2016-09-12 14:06:31 +08:00
|
|
|
/// Track if the full build process was successful.
|
|
|
|
///
|
|
|
|
/// This value is set to false, if throughout the build process an error
|
|
|
|
/// occurred which prevents us from generating valid GPU code.
|
|
|
|
bool BuildSuccessful = true;
|
|
|
|
|
2016-09-18 16:31:09 +08:00
|
|
|
/// The maximal number of loops surrounding a sequential kernel.
|
|
|
|
unsigned DeepestSequential = 0;
|
|
|
|
|
|
|
|
/// The maximal number of loops surrounding a parallel kernel.
|
|
|
|
unsigned DeepestParallel = 0;
|
|
|
|
|
2016-07-18 19:56:39 +08:00
|
|
|
private:
|
2016-07-22 15:11:12 +08:00
|
|
|
/// A vector of array base pointers for which a new ScopArrayInfo was created.
|
|
|
|
///
|
|
|
|
/// This vector is used to delete the ScopArrayInfo when it is not needed any
|
|
|
|
/// more.
|
|
|
|
std::vector<Value *> LocalArrays;
|
|
|
|
|
2016-07-25 20:47:39 +08:00
|
|
|
/// A map from ScopArrays to their corresponding device allocations.
|
|
|
|
std::map<ScopArrayInfo *, Value *> DeviceAllocations;
|
2016-07-25 20:47:33 +08:00
|
|
|
|
2016-07-25 17:16:01 +08:00
|
|
|
/// The current GPU context.
|
|
|
|
Value *GPUContext;
|
|
|
|
|
2016-08-04 20:18:14 +08:00
|
|
|
/// The set of isl_ids allocated in the kernel
|
|
|
|
std::vector<isl_id *> KernelIds;
|
|
|
|
|
2016-07-19 15:32:38 +08:00
|
|
|
/// A module containing GPU code.
|
|
|
|
///
|
|
|
|
/// This pointer is only set in case we are currently generating GPU code.
|
|
|
|
std::unique_ptr<Module> GPUModule;
|
|
|
|
|
|
|
|
/// The GPU program we generate code for.
|
|
|
|
gpu_prog *Prog;
|
|
|
|
|
[Polly] Added OpenCL Runtime to GPURuntime Library for GPGPU CodeGen
Summary:
When compiling for GPU, one can now choose to compile for OpenCL or CUDA,
with the corresponding polly-gpu-runtime flag (libopencl / libcudart). The
GPURuntime library (GPUJIT) has been extended with the OpenCL Runtime library
for that purpose, correctly choosing the corresponding library calls to the
option chosen when compiling (via different initialization calls).
Additionally, a specific GPU Target architecture can now be chosen with -polly-gpu-arch (only nvptx64 implemented thus far).
Reviewers: grosser, bollu, Meinersbur, etherzhhb, singam-sanjay
Reviewed By: grosser, Meinersbur
Subscribers: singam-sanjay, llvm-commits, pollydev, nemanjai, mgorny, yaxunl, Anastasia
Tags: #polly
Differential Revision: https://reviews.llvm.org/D32431
llvm-svn: 302379
2017-05-08 05:03:46 +08:00
|
|
|
/// The GPU Runtime implementation to use (OpenCL or CUDA).
|
|
|
|
GPURuntime Runtime;
|
|
|
|
|
|
|
|
/// The GPU Architecture to target.
|
|
|
|
GPUArch Arch;
|
|
|
|
|
2016-07-19 15:32:44 +08:00
|
|
|
/// Class to free isl_ids.
|
|
|
|
class IslIdDeleter {
|
|
|
|
public:
|
|
|
|
void operator()(__isl_take isl_id *Id) { isl_id_free(Id); };
|
|
|
|
};
|
|
|
|
|
|
|
|
/// A set containing all isl_ids allocated in a GPU kernel.
|
|
|
|
///
|
|
|
|
/// By releasing this set all isl_ids will be freed.
|
|
|
|
std::set<std::unique_ptr<isl_id, IslIdDeleter>> KernelIDs;
|
|
|
|
|
2016-07-21 21:15:59 +08:00
|
|
|
IslExprBuilder::IDToScopArrayInfoTy IDToSAI;
|
|
|
|
|
2016-07-18 19:56:39 +08:00
|
|
|
/// Create code for user-defined AST nodes.
|
|
|
|
///
|
|
|
|
/// These AST nodes can be of type:
|
|
|
|
///
|
|
|
|
/// - ScopStmt: A computational statement (TODO)
|
|
|
|
/// - Kernel: A GPU kernel call (TODO)
|
2016-07-25 20:47:39 +08:00
|
|
|
/// - Data-Transfer: A GPU <-> CPU data-transfer
|
2016-07-19 15:33:16 +08:00
|
|
|
/// - In-kernel synchronization
|
|
|
|
/// - In-kernel memory copy statement
|
2016-07-18 19:56:39 +08:00
|
|
|
///
|
2016-07-18 23:44:25 +08:00
|
|
|
/// @param UserStmt The ast node to generate code for.
|
|
|
|
virtual void createUser(__isl_take isl_ast_node *UserStmt);
|
2016-07-19 15:32:38 +08:00
|
|
|
|
2016-07-25 20:47:39 +08:00
|
|
|
enum DataDirection { HOST_TO_DEVICE, DEVICE_TO_HOST };
|
|
|
|
|
|
|
|
/// Create code for a data transfer statement
|
|
|
|
///
|
|
|
|
/// @param TransferStmt The data transfer statement.
|
|
|
|
/// @param Direction The direction in which to transfer data.
|
|
|
|
void createDataTransfer(__isl_take isl_ast_node *TransferStmt,
|
|
|
|
enum DataDirection Direction);
|
|
|
|
|
2016-07-21 21:15:59 +08:00
|
|
|
/// Find llvm::Values referenced in GPU kernel.
|
|
|
|
///
|
|
|
|
/// @param Kernel The kernel to scan for llvm::Values
|
|
|
|
///
|
|
|
|
/// @returns A set of values referenced by the kernel.
|
|
|
|
SetVector<Value *> getReferencesInKernel(ppcg_kernel *Kernel);
|
|
|
|
|
2016-07-27 21:20:16 +08:00
|
|
|
/// Compute the sizes of the execution grid for a given kernel.
|
|
|
|
///
|
|
|
|
/// @param Kernel The kernel to compute grid sizes for.
|
|
|
|
///
|
|
|
|
/// @returns A tuple with grid sizes for X and Y dimension
|
|
|
|
std::tuple<Value *, Value *> getGridSizes(ppcg_kernel *Kernel);
|
|
|
|
|
2017-04-28 19:16:30 +08:00
|
|
|
/// Creates a array that can be sent to the kernel on the device using a
|
|
|
|
/// host pointer. This is required for managed memory, when we directly send
|
|
|
|
/// host pointers to the device.
|
|
|
|
/// \note
|
|
|
|
/// This is to be used only with managed memory
|
|
|
|
Value *getOrCreateManagedDeviceArray(gpu_array_info *Array,
|
|
|
|
ScopArrayInfo *ArrayInfo);
|
|
|
|
|
2016-07-27 21:20:16 +08:00
|
|
|
/// Compute the sizes of the thread blocks for a given kernel.
|
|
|
|
///
|
|
|
|
/// @param Kernel The kernel to compute thread block sizes for.
|
|
|
|
///
|
|
|
|
/// @returns A tuple with thread block sizes for X, Y, and Z dimensions.
|
|
|
|
std::tuple<Value *, Value *, Value *> getBlockSizes(ppcg_kernel *Kernel);
|
|
|
|
|
2017-05-09 18:45:52 +08:00
|
|
|
/// Store a specific kernel launch parameter in the array of kernel launch
|
|
|
|
/// parameters.
|
|
|
|
///
|
|
|
|
/// @param Parameters The list of parameters in which to store.
|
|
|
|
/// @param Param The kernel launch parameter to store.
|
|
|
|
/// @param Index The index in the parameter list, at which to store the
|
|
|
|
/// parameter.
|
|
|
|
void insertStoreParameter(Instruction *Parameters, Instruction *Param,
|
|
|
|
int Index);
|
|
|
|
|
2016-07-27 21:20:16 +08:00
|
|
|
/// Create kernel launch parameters.
|
|
|
|
///
|
2016-08-04 14:55:49 +08:00
|
|
|
/// @param Kernel The kernel to create parameters for.
|
|
|
|
/// @param F The kernel function that has been created.
|
|
|
|
/// @param SubtreeValues The set of llvm::Values referenced by this kernel.
|
2016-07-27 21:20:16 +08:00
|
|
|
///
|
|
|
|
/// @returns A stack allocated array with pointers to the parameter
|
|
|
|
/// values that are passed to the kernel.
|
2016-08-04 14:55:49 +08:00
|
|
|
Value *createLaunchParameters(ppcg_kernel *Kernel, Function *F,
|
|
|
|
SetVector<Value *> SubtreeValues);
|
2016-07-27 21:20:16 +08:00
|
|
|
|
2016-08-04 20:18:14 +08:00
|
|
|
/// Create declarations for kernel variable.
|
|
|
|
///
|
|
|
|
/// This includes shared memory declarations.
|
|
|
|
///
|
|
|
|
/// @param Kernel The kernel definition to create variables for.
|
|
|
|
/// @param FN The function into which to generate the variables.
|
|
|
|
void createKernelVariables(ppcg_kernel *Kernel, Function *FN);
|
|
|
|
|
2016-08-05 14:47:43 +08:00
|
|
|
/// Add CUDA annotations to module.
|
|
|
|
///
|
|
|
|
/// Add a set of CUDA annotations that declares the maximal block dimensions
|
|
|
|
/// that will be used to execute the CUDA kernel. This allows the NVIDIA
|
|
|
|
/// PTX compiler to bound the number of allocated registers to ensure the
|
|
|
|
/// resulting kernel is known to run with up to as many block dimensions
|
|
|
|
/// as specified here.
|
|
|
|
///
|
|
|
|
/// @param M The module to add the annotations to.
|
|
|
|
/// @param BlockDimX The size of block dimension X.
|
|
|
|
/// @param BlockDimY The size of block dimension Y.
|
|
|
|
/// @param BlockDimZ The size of block dimension Z.
|
|
|
|
void addCUDAAnnotations(Module *M, Value *BlockDimX, Value *BlockDimY,
|
|
|
|
Value *BlockDimZ);
|
|
|
|
|
2016-07-19 15:32:38 +08:00
|
|
|
/// Create GPU kernel.
|
|
|
|
///
|
|
|
|
/// Code generate the kernel described by @p KernelStmt.
|
|
|
|
///
|
|
|
|
/// @param KernelStmt The ast node to generate kernel code for.
|
|
|
|
void createKernel(__isl_take isl_ast_node *KernelStmt);
|
|
|
|
|
2016-07-25 20:47:39 +08:00
|
|
|
/// Generate code that computes the size of an array.
|
|
|
|
///
|
|
|
|
/// @param Array The array for which to compute a size.
|
|
|
|
Value *getArraySize(gpu_array_info *Array);
|
|
|
|
|
2016-09-15 22:05:58 +08:00
|
|
|
/// Generate code to compute the minimal offset at which an array is accessed.
|
|
|
|
///
|
|
|
|
/// The offset of an array is the minimal array location accessed in a scop.
|
|
|
|
///
|
|
|
|
/// Example:
|
|
|
|
///
|
|
|
|
/// for (long i = 0; i < 100; i++)
|
|
|
|
/// A[i + 42] += ...
|
|
|
|
///
|
|
|
|
/// getArrayOffset(A) results in 42.
|
|
|
|
///
|
|
|
|
/// @param Array The array for which to compute the offset.
|
|
|
|
/// @returns An llvm::Value that contains the offset of the array.
|
|
|
|
Value *getArrayOffset(gpu_array_info *Array);
|
|
|
|
|
2016-08-04 14:55:59 +08:00
|
|
|
/// Prepare the kernel arguments for kernel code generation
|
|
|
|
///
|
|
|
|
/// @param Kernel The kernel to generate code for.
|
|
|
|
/// @param FN The function created for the kernel.
|
|
|
|
void prepareKernelArguments(ppcg_kernel *Kernel, Function *FN);
|
|
|
|
|
2016-07-19 15:32:38 +08:00
|
|
|
/// Create kernel function.
|
|
|
|
///
|
|
|
|
/// Create a kernel function located in a newly created module that can serve
|
|
|
|
/// as target for device code generation. Set the Builder to point to the
|
|
|
|
/// start block of this newly created function.
|
|
|
|
///
|
|
|
|
/// @param Kernel The kernel to generate code for.
|
2016-07-21 21:15:59 +08:00
|
|
|
/// @param SubtreeValues The set of llvm::Values referenced by this kernel.
|
|
|
|
void createKernelFunction(ppcg_kernel *Kernel,
|
|
|
|
SetVector<Value *> &SubtreeValues);
|
2016-07-19 15:32:38 +08:00
|
|
|
|
|
|
|
/// Create the declaration of a kernel function.
|
|
|
|
///
|
|
|
|
/// The kernel function takes as arguments:
|
|
|
|
///
|
|
|
|
/// - One i8 pointer for each external array reference used in the kernel.
|
2016-07-19 15:32:55 +08:00
|
|
|
/// - Host iterators
|
2016-07-19 15:33:06 +08:00
|
|
|
/// - Parameters
|
2016-07-19 15:32:38 +08:00
|
|
|
/// - Other LLVM Value references (TODO)
|
|
|
|
///
|
|
|
|
/// @param Kernel The kernel to generate the function declaration for.
|
2016-07-21 21:15:59 +08:00
|
|
|
/// @param SubtreeValues The set of llvm::Values referenced by this kernel.
|
|
|
|
///
|
2016-07-19 15:32:38 +08:00
|
|
|
/// @returns The newly declared function.
|
2016-07-21 21:15:59 +08:00
|
|
|
Function *createKernelFunctionDecl(ppcg_kernel *Kernel,
|
|
|
|
SetVector<Value *> &SubtreeValues);
|
2016-07-19 15:32:38 +08:00
|
|
|
|
2016-07-19 15:32:44 +08:00
|
|
|
/// Insert intrinsic functions to obtain thread and block ids.
|
|
|
|
///
|
|
|
|
/// @param The kernel to generate the intrinsic functions for.
|
|
|
|
void insertKernelIntrinsics(ppcg_kernel *Kernel);
|
|
|
|
|
2016-08-04 20:18:14 +08:00
|
|
|
/// Create a global-to-shared or shared-to-global copy statement.
|
|
|
|
///
|
|
|
|
/// @param CopyStmt The copy statement to generate code for
|
|
|
|
void createKernelCopy(ppcg_kernel_stmt *CopyStmt);
|
|
|
|
|
2016-07-21 21:15:59 +08:00
|
|
|
/// Create code for a ScopStmt called in @p Expr.
|
|
|
|
///
|
|
|
|
/// @param Expr The expression containing the call.
|
|
|
|
/// @param KernelStmt The kernel statement referenced in the call.
|
|
|
|
void createScopStmt(isl_ast_expr *Expr, ppcg_kernel_stmt *KernelStmt);
|
|
|
|
|
2016-07-19 15:33:16 +08:00
|
|
|
/// Create an in-kernel synchronization call.
|
|
|
|
void createKernelSync();
|
|
|
|
|
2016-07-22 15:11:12 +08:00
|
|
|
/// Create a PTX assembly string for the current GPU kernel.
|
|
|
|
///
|
|
|
|
/// @returns A string containing the corresponding PTX assembly code.
|
|
|
|
std::string createKernelASM();
|
|
|
|
|
|
|
|
/// Remove references from the dominator tree to the kernel function @p F.
|
|
|
|
///
|
|
|
|
/// @param F The function to remove references to.
|
|
|
|
void clearDominators(Function *F);
|
|
|
|
|
|
|
|
/// Remove references from scalar evolution to the kernel function @p F.
|
|
|
|
///
|
|
|
|
/// @param F The function to remove references to.
|
|
|
|
void clearScalarEvolution(Function *F);
|
|
|
|
|
|
|
|
/// Remove references from loop info to the kernel function @p F.
|
|
|
|
///
|
|
|
|
/// @param F The function to remove references to.
|
|
|
|
void clearLoops(Function *F);
|
|
|
|
|
2016-07-19 15:32:38 +08:00
|
|
|
/// Finalize the generation of the kernel function.
|
|
|
|
///
|
|
|
|
/// Free the LLVM-IR module corresponding to the kernel and -- if requested --
|
|
|
|
/// dump its IR to stderr.
|
2016-07-26 00:31:21 +08:00
|
|
|
///
|
|
|
|
/// @returns The Assembly string of the kernel.
|
|
|
|
std::string finalizeKernelFunction();
|
2016-07-25 17:16:01 +08:00
|
|
|
|
2016-09-18 03:22:31 +08:00
|
|
|
/// Finalize the generation of the kernel arguments.
|
|
|
|
///
|
|
|
|
/// This function ensures that not-read-only scalars used in a kernel are
|
|
|
|
/// stored back to the global memory location they ared backed up with before
|
|
|
|
/// the kernel terminates.
|
|
|
|
///
|
|
|
|
/// @params Kernel The kernel to finalize kernel arguments for.
|
|
|
|
void finalizeKernelArguments(ppcg_kernel *Kernel);
|
|
|
|
|
2016-07-25 20:47:33 +08:00
|
|
|
/// Create code that allocates memory to store arrays on device.
|
2016-07-25 17:16:01 +08:00
|
|
|
void allocateDeviceArrays();
|
|
|
|
|
2016-07-25 20:47:33 +08:00
|
|
|
/// Free all allocated device arrays.
|
|
|
|
void freeDeviceArrays();
|
|
|
|
|
2016-07-25 17:16:01 +08:00
|
|
|
/// Create a call to initialize the GPU context.
|
|
|
|
///
|
|
|
|
/// @returns A pointer to the newly initialized context.
|
|
|
|
Value *createCallInitContext();
|
|
|
|
|
2016-07-27 21:20:16 +08:00
|
|
|
/// Create a call to get the device pointer for a kernel allocation.
|
|
|
|
///
|
|
|
|
/// @param Allocation The Polly GPU allocation
|
|
|
|
///
|
|
|
|
/// @returns The device parameter corresponding to this allocation.
|
|
|
|
Value *createCallGetDevicePtr(Value *Allocation);
|
|
|
|
|
2016-07-25 17:16:01 +08:00
|
|
|
/// Create a call to free the GPU context.
|
|
|
|
///
|
|
|
|
/// @param Context A pointer to an initialized GPU context.
|
|
|
|
void createCallFreeContext(Value *Context);
|
|
|
|
|
2016-07-25 20:47:33 +08:00
|
|
|
/// Create a call to allocate memory on the device.
|
|
|
|
///
|
|
|
|
/// @param Size The size of memory to allocate
|
|
|
|
///
|
|
|
|
/// @returns A pointer that identifies this allocation.
|
2016-07-25 17:16:01 +08:00
|
|
|
Value *createCallAllocateMemoryForDevice(Value *Size);
|
2016-07-25 20:47:33 +08:00
|
|
|
|
|
|
|
/// Create a call to free a device array.
|
|
|
|
///
|
|
|
|
/// @param Array The device array to free.
|
|
|
|
void createCallFreeDeviceMemory(Value *Array);
|
2016-07-25 20:47:39 +08:00
|
|
|
|
|
|
|
/// Create a call to copy data from host to device.
|
|
|
|
///
|
|
|
|
/// @param HostPtr A pointer to the host data that should be copied.
|
|
|
|
/// @param DevicePtr A device pointer specifying the location to copy to.
|
|
|
|
void createCallCopyFromHostToDevice(Value *HostPtr, Value *DevicePtr,
|
|
|
|
Value *Size);
|
|
|
|
|
|
|
|
/// Create a call to copy data from device to host.
|
|
|
|
///
|
|
|
|
/// @param DevicePtr A pointer to the device data that should be copied.
|
|
|
|
/// @param HostPtr A host pointer specifying the location to copy to.
|
|
|
|
void createCallCopyFromDeviceToHost(Value *DevicePtr, Value *HostPtr,
|
|
|
|
Value *Size);
|
2016-07-26 00:31:21 +08:00
|
|
|
|
2017-04-28 19:16:30 +08:00
|
|
|
/// Create a call to synchronize Host & Device.
|
|
|
|
/// \note
|
|
|
|
/// This is to be used only with managed memory.
|
|
|
|
void createCallSynchronizeDevice();
|
|
|
|
|
2016-07-26 00:31:21 +08:00
|
|
|
/// Create a call to get a kernel from an assembly string.
|
|
|
|
///
|
|
|
|
/// @param Buffer The string describing the kernel.
|
|
|
|
/// @param Entry The name of the kernel function to call.
|
|
|
|
///
|
|
|
|
/// @returns A pointer to a kernel object
|
|
|
|
Value *createCallGetKernel(Value *Buffer, Value *Entry);
|
|
|
|
|
|
|
|
/// Create a call to free a GPU kernel.
|
|
|
|
///
|
|
|
|
/// @param GPUKernel THe kernel to free.
|
|
|
|
void createCallFreeKernel(Value *GPUKernel);
|
2016-07-27 21:20:16 +08:00
|
|
|
|
|
|
|
/// Create a call to launch a GPU kernel.
|
|
|
|
///
|
|
|
|
/// @param GPUKernel The kernel to launch.
|
|
|
|
/// @param GridDimX The size of the first grid dimension.
|
|
|
|
/// @param GridDimY The size of the second grid dimension.
|
|
|
|
/// @param GridBlockX The size of the first block dimension.
|
|
|
|
/// @param GridBlockY The size of the second block dimension.
|
|
|
|
/// @param GridBlockZ The size of the third block dimension.
|
|
|
|
/// @param Paramters A pointer to an array that contains itself pointers to
|
|
|
|
/// the parameter values passed for each kernel argument.
|
|
|
|
void createCallLaunchKernel(Value *GPUKernel, Value *GridDimX,
|
|
|
|
Value *GridDimY, Value *BlockDimX,
|
|
|
|
Value *BlockDimY, Value *BlockDimZ,
|
|
|
|
Value *Parameters);
|
2016-07-18 19:56:39 +08:00
|
|
|
};
|
|
|
|
|
2016-07-25 17:16:01 +08:00
|
|
|
void GPUNodeBuilder::initializeAfterRTH() {
|
2016-08-09 23:35:03 +08:00
|
|
|
BasicBlock *NewBB = SplitBlock(Builder.GetInsertBlock(),
|
|
|
|
&*Builder.GetInsertPoint(), &DT, &LI);
|
|
|
|
NewBB->setName("polly.acc.initialize");
|
|
|
|
Builder.SetInsertPoint(&NewBB->front());
|
|
|
|
|
2016-07-25 17:16:01 +08:00
|
|
|
GPUContext = createCallInitContext();
|
2017-04-28 19:16:30 +08:00
|
|
|
|
|
|
|
if (!ManagedMemory)
|
|
|
|
allocateDeviceArrays();
|
2016-07-25 17:16:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void GPUNodeBuilder::finalize() {
|
2017-04-28 19:16:30 +08:00
|
|
|
if (!ManagedMemory)
|
|
|
|
freeDeviceArrays();
|
|
|
|
|
2016-07-25 17:16:01 +08:00
|
|
|
createCallFreeContext(GPUContext);
|
|
|
|
IslNodeBuilder::finalize();
|
|
|
|
}
|
|
|
|
|
|
|
|
void GPUNodeBuilder::allocateDeviceArrays() {
|
2017-04-28 19:16:30 +08:00
|
|
|
assert(!ManagedMemory && "Managed memory will directly send host pointers "
|
|
|
|
"to the kernel. There is no need for device arrays");
|
2016-07-25 17:16:01 +08:00
|
|
|
isl_ast_build *Build = isl_ast_build_from_context(S.getContext());
|
|
|
|
|
|
|
|
for (int i = 0; i < Prog->n_array; ++i) {
|
|
|
|
gpu_array_info *Array = &Prog->array[i];
|
2016-07-25 20:47:39 +08:00
|
|
|
auto *ScopArray = (ScopArrayInfo *)Array->user;
|
2016-07-25 20:47:33 +08:00
|
|
|
std::string DevArrayName("p_dev_array_");
|
|
|
|
DevArrayName.append(Array->name);
|
2016-07-25 17:16:01 +08:00
|
|
|
|
2016-07-25 20:47:39 +08:00
|
|
|
Value *ArraySize = getArraySize(Array);
|
2016-09-15 22:05:58 +08:00
|
|
|
Value *Offset = getArrayOffset(Array);
|
|
|
|
if (Offset)
|
|
|
|
ArraySize = Builder.CreateSub(
|
|
|
|
ArraySize,
|
|
|
|
Builder.CreateMul(Offset,
|
|
|
|
Builder.getInt64(ScopArray->getElemSizeInBytes())));
|
2016-07-25 20:47:33 +08:00
|
|
|
Value *DevArray = createCallAllocateMemoryForDevice(ArraySize);
|
|
|
|
DevArray->setName(DevArrayName);
|
2016-07-25 20:47:39 +08:00
|
|
|
DeviceAllocations[ScopArray] = DevArray;
|
2016-07-25 17:16:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
isl_ast_build_free(Build);
|
|
|
|
}
|
|
|
|
|
2016-08-05 14:47:43 +08:00
|
|
|
void GPUNodeBuilder::addCUDAAnnotations(Module *M, Value *BlockDimX,
|
|
|
|
Value *BlockDimY, Value *BlockDimZ) {
|
|
|
|
auto AnnotationNode = M->getOrInsertNamedMetadata("nvvm.annotations");
|
|
|
|
|
|
|
|
for (auto &F : *M) {
|
|
|
|
if (F.getCallingConv() != CallingConv::PTX_Kernel)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
Value *V[] = {BlockDimX, BlockDimY, BlockDimZ};
|
|
|
|
|
|
|
|
Metadata *Elements[] = {
|
|
|
|
ValueAsMetadata::get(&F), MDString::get(M->getContext(), "maxntidx"),
|
|
|
|
ValueAsMetadata::get(V[0]), MDString::get(M->getContext(), "maxntidy"),
|
|
|
|
ValueAsMetadata::get(V[1]), MDString::get(M->getContext(), "maxntidz"),
|
|
|
|
ValueAsMetadata::get(V[2]),
|
|
|
|
};
|
|
|
|
MDNode *Node = MDNode::get(M->getContext(), Elements);
|
|
|
|
AnnotationNode->addOperand(Node);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-25 20:47:33 +08:00
|
|
|
void GPUNodeBuilder::freeDeviceArrays() {
|
2017-04-28 19:16:30 +08:00
|
|
|
assert(!ManagedMemory && "Managed memory does not use device arrays");
|
2016-07-25 20:47:39 +08:00
|
|
|
for (auto &Array : DeviceAllocations)
|
|
|
|
createCallFreeDeviceMemory(Array.second);
|
2016-07-25 20:47:33 +08:00
|
|
|
}
|
|
|
|
|
2016-07-26 00:31:21 +08:00
|
|
|
Value *GPUNodeBuilder::createCallGetKernel(Value *Buffer, Value *Entry) {
|
|
|
|
const char *Name = "polly_getKernel";
|
|
|
|
Module *M = Builder.GetInsertBlock()->getParent()->getParent();
|
|
|
|
Function *F = M->getFunction(Name);
|
|
|
|
|
|
|
|
// If F is not available, declare it.
|
|
|
|
if (!F) {
|
|
|
|
GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
|
|
|
|
std::vector<Type *> Args;
|
|
|
|
Args.push_back(Builder.getInt8PtrTy());
|
|
|
|
Args.push_back(Builder.getInt8PtrTy());
|
|
|
|
FunctionType *Ty = FunctionType::get(Builder.getInt8PtrTy(), Args, false);
|
|
|
|
F = Function::Create(Ty, Linkage, Name, M);
|
|
|
|
}
|
|
|
|
|
|
|
|
return Builder.CreateCall(F, {Buffer, Entry});
|
|
|
|
}
|
|
|
|
|
2016-07-27 21:20:16 +08:00
|
|
|
Value *GPUNodeBuilder::createCallGetDevicePtr(Value *Allocation) {
|
|
|
|
const char *Name = "polly_getDevicePtr";
|
|
|
|
Module *M = Builder.GetInsertBlock()->getParent()->getParent();
|
|
|
|
Function *F = M->getFunction(Name);
|
|
|
|
|
|
|
|
// If F is not available, declare it.
|
|
|
|
if (!F) {
|
|
|
|
GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
|
|
|
|
std::vector<Type *> Args;
|
|
|
|
Args.push_back(Builder.getInt8PtrTy());
|
|
|
|
FunctionType *Ty = FunctionType::get(Builder.getInt8PtrTy(), Args, false);
|
|
|
|
F = Function::Create(Ty, Linkage, Name, M);
|
|
|
|
}
|
|
|
|
|
|
|
|
return Builder.CreateCall(F, {Allocation});
|
|
|
|
}
|
|
|
|
|
|
|
|
void GPUNodeBuilder::createCallLaunchKernel(Value *GPUKernel, Value *GridDimX,
|
|
|
|
Value *GridDimY, Value *BlockDimX,
|
|
|
|
Value *BlockDimY, Value *BlockDimZ,
|
|
|
|
Value *Parameters) {
|
|
|
|
const char *Name = "polly_launchKernel";
|
|
|
|
Module *M = Builder.GetInsertBlock()->getParent()->getParent();
|
|
|
|
Function *F = M->getFunction(Name);
|
|
|
|
|
|
|
|
// If F is not available, declare it.
|
|
|
|
if (!F) {
|
|
|
|
GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
|
|
|
|
std::vector<Type *> Args;
|
|
|
|
Args.push_back(Builder.getInt8PtrTy());
|
|
|
|
Args.push_back(Builder.getInt32Ty());
|
|
|
|
Args.push_back(Builder.getInt32Ty());
|
|
|
|
Args.push_back(Builder.getInt32Ty());
|
|
|
|
Args.push_back(Builder.getInt32Ty());
|
|
|
|
Args.push_back(Builder.getInt32Ty());
|
|
|
|
Args.push_back(Builder.getInt8PtrTy());
|
|
|
|
FunctionType *Ty = FunctionType::get(Builder.getVoidTy(), Args, false);
|
|
|
|
F = Function::Create(Ty, Linkage, Name, M);
|
|
|
|
}
|
|
|
|
|
2017-02-01 18:12:09 +08:00
|
|
|
Builder.CreateCall(F, {GPUKernel, GridDimX, GridDimY, BlockDimX, BlockDimY,
|
|
|
|
BlockDimZ, Parameters});
|
2016-07-27 21:20:16 +08:00
|
|
|
}
|
|
|
|
|
2016-07-26 00:31:21 +08:00
|
|
|
void GPUNodeBuilder::createCallFreeKernel(Value *GPUKernel) {
|
|
|
|
const char *Name = "polly_freeKernel";
|
|
|
|
Module *M = Builder.GetInsertBlock()->getParent()->getParent();
|
|
|
|
Function *F = M->getFunction(Name);
|
|
|
|
|
|
|
|
// If F is not available, declare it.
|
|
|
|
if (!F) {
|
|
|
|
GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
|
|
|
|
std::vector<Type *> Args;
|
|
|
|
Args.push_back(Builder.getInt8PtrTy());
|
|
|
|
FunctionType *Ty = FunctionType::get(Builder.getVoidTy(), Args, false);
|
|
|
|
F = Function::Create(Ty, Linkage, Name, M);
|
|
|
|
}
|
|
|
|
|
|
|
|
Builder.CreateCall(F, {GPUKernel});
|
|
|
|
}
|
|
|
|
|
2016-07-25 20:47:33 +08:00
|
|
|
void GPUNodeBuilder::createCallFreeDeviceMemory(Value *Array) {
|
2017-04-28 19:16:30 +08:00
|
|
|
assert(!ManagedMemory && "Managed memory does not allocate or free memory "
|
|
|
|
"for device");
|
2016-07-25 20:47:33 +08:00
|
|
|
const char *Name = "polly_freeDeviceMemory";
|
|
|
|
Module *M = Builder.GetInsertBlock()->getParent()->getParent();
|
|
|
|
Function *F = M->getFunction(Name);
|
|
|
|
|
|
|
|
// If F is not available, declare it.
|
|
|
|
if (!F) {
|
|
|
|
GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
|
|
|
|
std::vector<Type *> Args;
|
|
|
|
Args.push_back(Builder.getInt8PtrTy());
|
|
|
|
FunctionType *Ty = FunctionType::get(Builder.getVoidTy(), Args, false);
|
|
|
|
F = Function::Create(Ty, Linkage, Name, M);
|
|
|
|
}
|
|
|
|
|
|
|
|
Builder.CreateCall(F, {Array});
|
|
|
|
}
|
|
|
|
|
2016-07-25 17:16:01 +08:00
|
|
|
Value *GPUNodeBuilder::createCallAllocateMemoryForDevice(Value *Size) {
|
2017-04-28 19:16:30 +08:00
|
|
|
assert(!ManagedMemory && "Managed memory does not allocate or free memory "
|
|
|
|
"for device");
|
2016-07-25 17:16:01 +08:00
|
|
|
const char *Name = "polly_allocateMemoryForDevice";
|
|
|
|
Module *M = Builder.GetInsertBlock()->getParent()->getParent();
|
|
|
|
Function *F = M->getFunction(Name);
|
|
|
|
|
|
|
|
// If F is not available, declare it.
|
|
|
|
if (!F) {
|
|
|
|
GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
|
|
|
|
std::vector<Type *> Args;
|
|
|
|
Args.push_back(Builder.getInt64Ty());
|
|
|
|
FunctionType *Ty = FunctionType::get(Builder.getInt8PtrTy(), Args, false);
|
|
|
|
F = Function::Create(Ty, Linkage, Name, M);
|
|
|
|
}
|
|
|
|
|
|
|
|
return Builder.CreateCall(F, {Size});
|
|
|
|
}
|
|
|
|
|
2016-07-25 20:47:39 +08:00
|
|
|
void GPUNodeBuilder::createCallCopyFromHostToDevice(Value *HostData,
|
|
|
|
Value *DeviceData,
|
|
|
|
Value *Size) {
|
2017-04-28 19:16:30 +08:00
|
|
|
assert(!ManagedMemory && "Managed memory does not transfer memory between "
|
|
|
|
"device and host");
|
2016-07-25 20:47:39 +08:00
|
|
|
const char *Name = "polly_copyFromHostToDevice";
|
|
|
|
Module *M = Builder.GetInsertBlock()->getParent()->getParent();
|
|
|
|
Function *F = M->getFunction(Name);
|
|
|
|
|
|
|
|
// If F is not available, declare it.
|
|
|
|
if (!F) {
|
|
|
|
GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
|
|
|
|
std::vector<Type *> Args;
|
|
|
|
Args.push_back(Builder.getInt8PtrTy());
|
|
|
|
Args.push_back(Builder.getInt8PtrTy());
|
|
|
|
Args.push_back(Builder.getInt64Ty());
|
|
|
|
FunctionType *Ty = FunctionType::get(Builder.getVoidTy(), Args, false);
|
|
|
|
F = Function::Create(Ty, Linkage, Name, M);
|
|
|
|
}
|
|
|
|
|
|
|
|
Builder.CreateCall(F, {HostData, DeviceData, Size});
|
|
|
|
}
|
|
|
|
|
|
|
|
void GPUNodeBuilder::createCallCopyFromDeviceToHost(Value *DeviceData,
|
|
|
|
Value *HostData,
|
|
|
|
Value *Size) {
|
2017-04-28 19:16:30 +08:00
|
|
|
assert(!ManagedMemory && "Managed memory does not transfer memory between "
|
|
|
|
"device and host");
|
2016-07-25 20:47:39 +08:00
|
|
|
const char *Name = "polly_copyFromDeviceToHost";
|
|
|
|
Module *M = Builder.GetInsertBlock()->getParent()->getParent();
|
|
|
|
Function *F = M->getFunction(Name);
|
|
|
|
|
|
|
|
// If F is not available, declare it.
|
|
|
|
if (!F) {
|
|
|
|
GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
|
|
|
|
std::vector<Type *> Args;
|
|
|
|
Args.push_back(Builder.getInt8PtrTy());
|
|
|
|
Args.push_back(Builder.getInt8PtrTy());
|
|
|
|
Args.push_back(Builder.getInt64Ty());
|
|
|
|
FunctionType *Ty = FunctionType::get(Builder.getVoidTy(), Args, false);
|
|
|
|
F = Function::Create(Ty, Linkage, Name, M);
|
|
|
|
}
|
|
|
|
|
|
|
|
Builder.CreateCall(F, {DeviceData, HostData, Size});
|
|
|
|
}
|
|
|
|
|
2017-04-28 19:16:30 +08:00
|
|
|
void GPUNodeBuilder::createCallSynchronizeDevice() {
|
|
|
|
assert(ManagedMemory && "explicit synchronization is only necessary for "
|
|
|
|
"managed memory");
|
|
|
|
const char *Name = "polly_synchronizeDevice";
|
|
|
|
Module *M = Builder.GetInsertBlock()->getParent()->getParent();
|
|
|
|
Function *F = M->getFunction(Name);
|
|
|
|
|
|
|
|
// If F is not available, declare it.
|
|
|
|
if (!F) {
|
|
|
|
GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
|
|
|
|
FunctionType *Ty = FunctionType::get(Builder.getVoidTy(), false);
|
|
|
|
F = Function::Create(Ty, Linkage, Name, M);
|
|
|
|
}
|
|
|
|
|
|
|
|
Builder.CreateCall(F);
|
|
|
|
}
|
|
|
|
|
2016-07-25 17:16:01 +08:00
|
|
|
Value *GPUNodeBuilder::createCallInitContext() {
|
[Polly] Added OpenCL Runtime to GPURuntime Library for GPGPU CodeGen
Summary:
When compiling for GPU, one can now choose to compile for OpenCL or CUDA,
with the corresponding polly-gpu-runtime flag (libopencl / libcudart). The
GPURuntime library (GPUJIT) has been extended with the OpenCL Runtime library
for that purpose, correctly choosing the corresponding library calls to the
option chosen when compiling (via different initialization calls).
Additionally, a specific GPU Target architecture can now be chosen with -polly-gpu-arch (only nvptx64 implemented thus far).
Reviewers: grosser, bollu, Meinersbur, etherzhhb, singam-sanjay
Reviewed By: grosser, Meinersbur
Subscribers: singam-sanjay, llvm-commits, pollydev, nemanjai, mgorny, yaxunl, Anastasia
Tags: #polly
Differential Revision: https://reviews.llvm.org/D32431
llvm-svn: 302379
2017-05-08 05:03:46 +08:00
|
|
|
const char *Name;
|
|
|
|
|
|
|
|
switch (Runtime) {
|
|
|
|
case GPURuntime::CUDA:
|
|
|
|
Name = "polly_initContextCUDA";
|
|
|
|
break;
|
|
|
|
case GPURuntime::OpenCL:
|
|
|
|
Name = "polly_initContextCL";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2016-07-25 17:16:01 +08:00
|
|
|
Module *M = Builder.GetInsertBlock()->getParent()->getParent();
|
|
|
|
Function *F = M->getFunction(Name);
|
|
|
|
|
|
|
|
// If F is not available, declare it.
|
|
|
|
if (!F) {
|
|
|
|
GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
|
|
|
|
std::vector<Type *> Args;
|
|
|
|
FunctionType *Ty = FunctionType::get(Builder.getInt8PtrTy(), Args, false);
|
|
|
|
F = Function::Create(Ty, Linkage, Name, M);
|
|
|
|
}
|
|
|
|
|
|
|
|
return Builder.CreateCall(F, {});
|
|
|
|
}
|
|
|
|
|
|
|
|
void GPUNodeBuilder::createCallFreeContext(Value *Context) {
|
|
|
|
const char *Name = "polly_freeContext";
|
|
|
|
Module *M = Builder.GetInsertBlock()->getParent()->getParent();
|
|
|
|
Function *F = M->getFunction(Name);
|
|
|
|
|
|
|
|
// If F is not available, declare it.
|
|
|
|
if (!F) {
|
|
|
|
GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
|
|
|
|
std::vector<Type *> Args;
|
|
|
|
Args.push_back(Builder.getInt8PtrTy());
|
|
|
|
FunctionType *Ty = FunctionType::get(Builder.getVoidTy(), Args, false);
|
|
|
|
F = Function::Create(Ty, Linkage, Name, M);
|
|
|
|
}
|
|
|
|
|
|
|
|
Builder.CreateCall(F, {Context});
|
|
|
|
}
|
|
|
|
|
2016-07-19 15:33:16 +08:00
|
|
|
/// Check if one string is a prefix of another.
|
|
|
|
///
|
|
|
|
/// @param String The string in which to look for the prefix.
|
|
|
|
/// @param Prefix The prefix to look for.
|
|
|
|
static bool isPrefix(std::string String, std::string Prefix) {
|
|
|
|
return String.find(Prefix) == 0;
|
|
|
|
}
|
|
|
|
|
2016-07-25 20:47:39 +08:00
|
|
|
Value *GPUNodeBuilder::getArraySize(gpu_array_info *Array) {
|
|
|
|
isl_ast_build *Build = isl_ast_build_from_context(S.getContext());
|
|
|
|
Value *ArraySize = ConstantInt::get(Builder.getInt64Ty(), Array->size);
|
|
|
|
|
|
|
|
if (!gpu_array_is_scalar(Array)) {
|
|
|
|
auto OffsetDimZero = isl_pw_aff_copy(Array->bound[0]);
|
|
|
|
isl_ast_expr *Res = isl_ast_build_expr_from_pw_aff(Build, OffsetDimZero);
|
|
|
|
|
|
|
|
for (unsigned int i = 1; i < Array->n_index; i++) {
|
|
|
|
isl_pw_aff *Bound_I = isl_pw_aff_copy(Array->bound[i]);
|
|
|
|
isl_ast_expr *Expr = isl_ast_build_expr_from_pw_aff(Build, Bound_I);
|
|
|
|
Res = isl_ast_expr_mul(Res, Expr);
|
|
|
|
}
|
|
|
|
|
|
|
|
Value *NumElements = ExprBuilder.create(Res);
|
2016-09-13 16:02:14 +08:00
|
|
|
if (NumElements->getType() != ArraySize->getType())
|
|
|
|
NumElements = Builder.CreateSExt(NumElements, ArraySize->getType());
|
2016-07-25 20:47:39 +08:00
|
|
|
ArraySize = Builder.CreateMul(ArraySize, NumElements);
|
|
|
|
}
|
|
|
|
isl_ast_build_free(Build);
|
|
|
|
return ArraySize;
|
|
|
|
}
|
|
|
|
|
2016-09-15 22:05:58 +08:00
|
|
|
Value *GPUNodeBuilder::getArrayOffset(gpu_array_info *Array) {
|
|
|
|
if (gpu_array_is_scalar(Array))
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
isl_ast_build *Build = isl_ast_build_from_context(S.getContext());
|
|
|
|
|
|
|
|
isl_set *Min = isl_set_lexmin(isl_set_copy(Array->extent));
|
|
|
|
|
|
|
|
isl_set *ZeroSet = isl_set_universe(isl_set_get_space(Min));
|
|
|
|
|
|
|
|
for (long i = 0; i < isl_set_dim(Min, isl_dim_set); i++)
|
|
|
|
ZeroSet = isl_set_fix_si(ZeroSet, isl_dim_set, i, 0);
|
|
|
|
|
|
|
|
if (isl_set_is_subset(Min, ZeroSet)) {
|
|
|
|
isl_set_free(Min);
|
|
|
|
isl_set_free(ZeroSet);
|
|
|
|
isl_ast_build_free(Build);
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
isl_set_free(ZeroSet);
|
|
|
|
|
|
|
|
isl_ast_expr *Result =
|
|
|
|
isl_ast_expr_from_val(isl_val_int_from_si(isl_set_get_ctx(Min), 0));
|
|
|
|
|
|
|
|
for (long i = 0; i < isl_set_dim(Min, isl_dim_set); i++) {
|
|
|
|
if (i > 0) {
|
|
|
|
isl_pw_aff *Bound_I = isl_pw_aff_copy(Array->bound[i - 1]);
|
|
|
|
isl_ast_expr *BExpr = isl_ast_build_expr_from_pw_aff(Build, Bound_I);
|
|
|
|
Result = isl_ast_expr_mul(Result, BExpr);
|
|
|
|
}
|
|
|
|
isl_pw_aff *DimMin = isl_set_dim_min(isl_set_copy(Min), i);
|
|
|
|
isl_ast_expr *MExpr = isl_ast_build_expr_from_pw_aff(Build, DimMin);
|
|
|
|
Result = isl_ast_expr_add(Result, MExpr);
|
|
|
|
}
|
|
|
|
|
|
|
|
Value *ResultValue = ExprBuilder.create(Result);
|
|
|
|
isl_set_free(Min);
|
|
|
|
isl_ast_build_free(Build);
|
|
|
|
|
|
|
|
return ResultValue;
|
|
|
|
}
|
|
|
|
|
2017-04-28 19:16:30 +08:00
|
|
|
Value *GPUNodeBuilder::getOrCreateManagedDeviceArray(gpu_array_info *Array,
|
|
|
|
ScopArrayInfo *ArrayInfo) {
|
|
|
|
|
|
|
|
assert(ManagedMemory && "Only used when you wish to get a host "
|
|
|
|
"pointer for sending data to the kernel, "
|
|
|
|
"with managed memory");
|
|
|
|
std::map<ScopArrayInfo *, Value *>::iterator it;
|
|
|
|
if ((it = DeviceAllocations.find(ArrayInfo)) != DeviceAllocations.end()) {
|
|
|
|
return it->second;
|
|
|
|
} else {
|
|
|
|
Value *HostPtr;
|
|
|
|
|
|
|
|
if (gpu_array_is_scalar(Array))
|
|
|
|
HostPtr = BlockGen.getOrCreateAlloca(ArrayInfo);
|
|
|
|
else
|
|
|
|
HostPtr = ArrayInfo->getBasePtr();
|
|
|
|
|
|
|
|
Value *Offset = getArrayOffset(Array);
|
|
|
|
if (Offset) {
|
|
|
|
HostPtr = Builder.CreatePointerCast(
|
|
|
|
HostPtr, ArrayInfo->getElementType()->getPointerTo());
|
|
|
|
HostPtr = Builder.CreateGEP(HostPtr, Offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
HostPtr = Builder.CreatePointerCast(HostPtr, Builder.getInt8PtrTy());
|
|
|
|
DeviceAllocations[ArrayInfo] = HostPtr;
|
|
|
|
return HostPtr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-25 20:47:39 +08:00
|
|
|
void GPUNodeBuilder::createDataTransfer(__isl_take isl_ast_node *TransferStmt,
|
|
|
|
enum DataDirection Direction) {
|
2017-04-28 19:16:30 +08:00
|
|
|
assert(!ManagedMemory && "Managed memory needs no data transfers");
|
2016-07-25 20:47:39 +08:00
|
|
|
isl_ast_expr *Expr = isl_ast_node_user_get_expr(TransferStmt);
|
|
|
|
isl_ast_expr *Arg = isl_ast_expr_get_op_arg(Expr, 0);
|
|
|
|
isl_id *Id = isl_ast_expr_get_id(Arg);
|
|
|
|
auto Array = (gpu_array_info *)isl_id_get_user(Id);
|
|
|
|
auto ScopArray = (ScopArrayInfo *)(Array->user);
|
|
|
|
|
|
|
|
Value *Size = getArraySize(Array);
|
2016-09-15 22:05:58 +08:00
|
|
|
Value *Offset = getArrayOffset(Array);
|
2016-07-25 20:47:39 +08:00
|
|
|
Value *DevPtr = DeviceAllocations[ScopArray];
|
|
|
|
|
2016-08-09 23:35:06 +08:00
|
|
|
Value *HostPtr;
|
|
|
|
|
|
|
|
if (gpu_array_is_scalar(Array))
|
|
|
|
HostPtr = BlockGen.getOrCreateAlloca(ScopArray);
|
|
|
|
else
|
|
|
|
HostPtr = ScopArray->getBasePtr();
|
2016-07-25 20:47:39 +08:00
|
|
|
|
2016-09-15 22:05:58 +08:00
|
|
|
if (Offset) {
|
|
|
|
HostPtr = Builder.CreatePointerCast(
|
|
|
|
HostPtr, ScopArray->getElementType()->getPointerTo());
|
|
|
|
HostPtr = Builder.CreateGEP(HostPtr, Offset);
|
|
|
|
}
|
|
|
|
|
2016-07-25 20:47:39 +08:00
|
|
|
HostPtr = Builder.CreatePointerCast(HostPtr, Builder.getInt8PtrTy());
|
|
|
|
|
2016-09-15 22:05:58 +08:00
|
|
|
if (Offset) {
|
|
|
|
Size = Builder.CreateSub(
|
2017-02-01 18:12:09 +08:00
|
|
|
Size, Builder.CreateMul(
|
|
|
|
Offset, Builder.getInt64(ScopArray->getElemSizeInBytes())));
|
2016-09-15 22:05:58 +08:00
|
|
|
}
|
|
|
|
|
2016-07-25 20:47:39 +08:00
|
|
|
if (Direction == HOST_TO_DEVICE)
|
|
|
|
createCallCopyFromHostToDevice(HostPtr, DevPtr, Size);
|
|
|
|
else
|
|
|
|
createCallCopyFromDeviceToHost(DevPtr, HostPtr, Size);
|
|
|
|
|
|
|
|
isl_id_free(Id);
|
|
|
|
isl_ast_expr_free(Arg);
|
|
|
|
isl_ast_expr_free(Expr);
|
|
|
|
isl_ast_node_free(TransferStmt);
|
|
|
|
}
|
|
|
|
|
2016-07-18 23:44:25 +08:00
|
|
|
void GPUNodeBuilder::createUser(__isl_take isl_ast_node *UserStmt) {
|
2016-07-19 15:32:38 +08:00
|
|
|
isl_ast_expr *Expr = isl_ast_node_user_get_expr(UserStmt);
|
|
|
|
isl_ast_expr *StmtExpr = isl_ast_expr_get_op_arg(Expr, 0);
|
|
|
|
isl_id *Id = isl_ast_expr_get_id(StmtExpr);
|
|
|
|
isl_id_free(Id);
|
|
|
|
isl_ast_expr_free(StmtExpr);
|
|
|
|
|
|
|
|
const char *Str = isl_id_get_name(Id);
|
|
|
|
if (!strcmp(Str, "kernel")) {
|
|
|
|
createKernel(UserStmt);
|
|
|
|
isl_ast_expr_free(Expr);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-07-25 20:47:39 +08:00
|
|
|
if (isPrefix(Str, "to_device")) {
|
2017-04-28 19:16:30 +08:00
|
|
|
if (!ManagedMemory)
|
|
|
|
createDataTransfer(UserStmt, HOST_TO_DEVICE);
|
|
|
|
else
|
|
|
|
isl_ast_node_free(UserStmt);
|
|
|
|
|
2016-07-25 20:47:39 +08:00
|
|
|
isl_ast_expr_free(Expr);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isPrefix(Str, "from_device")) {
|
2017-04-28 19:16:30 +08:00
|
|
|
if (!ManagedMemory) {
|
|
|
|
createDataTransfer(UserStmt, DEVICE_TO_HOST);
|
|
|
|
} else {
|
|
|
|
createCallSynchronizeDevice();
|
|
|
|
isl_ast_node_free(UserStmt);
|
|
|
|
}
|
2016-07-19 15:33:16 +08:00
|
|
|
isl_ast_expr_free(Expr);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
isl_id *Anno = isl_ast_node_get_annotation(UserStmt);
|
|
|
|
struct ppcg_kernel_stmt *KernelStmt =
|
|
|
|
(struct ppcg_kernel_stmt *)isl_id_get_user(Anno);
|
|
|
|
isl_id_free(Anno);
|
|
|
|
|
|
|
|
switch (KernelStmt->type) {
|
|
|
|
case ppcg_kernel_domain:
|
2016-07-21 21:15:59 +08:00
|
|
|
createScopStmt(Expr, KernelStmt);
|
2016-07-19 15:33:16 +08:00
|
|
|
isl_ast_node_free(UserStmt);
|
|
|
|
return;
|
|
|
|
case ppcg_kernel_copy:
|
2016-08-04 20:18:14 +08:00
|
|
|
createKernelCopy(KernelStmt);
|
2016-07-19 15:33:16 +08:00
|
|
|
isl_ast_expr_free(Expr);
|
|
|
|
isl_ast_node_free(UserStmt);
|
|
|
|
return;
|
|
|
|
case ppcg_kernel_sync:
|
|
|
|
createKernelSync();
|
|
|
|
isl_ast_expr_free(Expr);
|
|
|
|
isl_ast_node_free(UserStmt);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-07-19 15:32:38 +08:00
|
|
|
isl_ast_expr_free(Expr);
|
2016-07-18 23:44:25 +08:00
|
|
|
isl_ast_node_free(UserStmt);
|
|
|
|
return;
|
|
|
|
}
|
2016-08-04 20:18:14 +08:00
|
|
|
void GPUNodeBuilder::createKernelCopy(ppcg_kernel_stmt *KernelStmt) {
|
|
|
|
isl_ast_expr *LocalIndex = isl_ast_expr_copy(KernelStmt->u.c.local_index);
|
|
|
|
LocalIndex = isl_ast_expr_address_of(LocalIndex);
|
|
|
|
Value *LocalAddr = ExprBuilder.create(LocalIndex);
|
|
|
|
isl_ast_expr *Index = isl_ast_expr_copy(KernelStmt->u.c.index);
|
|
|
|
Index = isl_ast_expr_address_of(Index);
|
|
|
|
Value *GlobalAddr = ExprBuilder.create(Index);
|
|
|
|
|
|
|
|
if (KernelStmt->u.c.read) {
|
|
|
|
LoadInst *Load = Builder.CreateLoad(GlobalAddr, "shared.read");
|
|
|
|
Builder.CreateStore(Load, LocalAddr);
|
|
|
|
} else {
|
|
|
|
LoadInst *Load = Builder.CreateLoad(LocalAddr, "shared.write");
|
|
|
|
Builder.CreateStore(Load, GlobalAddr);
|
|
|
|
}
|
|
|
|
}
|
2016-07-18 23:44:25 +08:00
|
|
|
|
2016-07-21 21:15:59 +08:00
|
|
|
void GPUNodeBuilder::createScopStmt(isl_ast_expr *Expr,
|
|
|
|
ppcg_kernel_stmt *KernelStmt) {
|
|
|
|
auto Stmt = (ScopStmt *)KernelStmt->u.d.stmt->stmt;
|
|
|
|
isl_id_to_ast_expr *Indexes = KernelStmt->u.d.ref2expr;
|
|
|
|
|
|
|
|
LoopToScevMapT LTS;
|
|
|
|
LTS.insert(OutsideLoopIterations.begin(), OutsideLoopIterations.end());
|
|
|
|
|
|
|
|
createSubstitutions(Expr, Stmt, LTS);
|
|
|
|
|
|
|
|
if (Stmt->isBlockStmt())
|
|
|
|
BlockGen.copyStmt(*Stmt, LTS, Indexes);
|
|
|
|
else
|
2016-09-13 16:42:10 +08:00
|
|
|
RegionGen.copyStmt(*Stmt, LTS, Indexes);
|
2016-07-21 21:15:59 +08:00
|
|
|
}
|
|
|
|
|
2016-07-19 15:33:16 +08:00
|
|
|
void GPUNodeBuilder::createKernelSync() {
|
|
|
|
Module *M = Builder.GetInsertBlock()->getParent()->getParent();
|
[Polly] Added OpenCL Runtime to GPURuntime Library for GPGPU CodeGen
Summary:
When compiling for GPU, one can now choose to compile for OpenCL or CUDA,
with the corresponding polly-gpu-runtime flag (libopencl / libcudart). The
GPURuntime library (GPUJIT) has been extended with the OpenCL Runtime library
for that purpose, correctly choosing the corresponding library calls to the
option chosen when compiling (via different initialization calls).
Additionally, a specific GPU Target architecture can now be chosen with -polly-gpu-arch (only nvptx64 implemented thus far).
Reviewers: grosser, bollu, Meinersbur, etherzhhb, singam-sanjay
Reviewed By: grosser, Meinersbur
Subscribers: singam-sanjay, llvm-commits, pollydev, nemanjai, mgorny, yaxunl, Anastasia
Tags: #polly
Differential Revision: https://reviews.llvm.org/D32431
llvm-svn: 302379
2017-05-08 05:03:46 +08:00
|
|
|
|
|
|
|
Function *Sync;
|
|
|
|
|
|
|
|
switch (Arch) {
|
|
|
|
case GPUArch::NVPTX64:
|
|
|
|
Sync = Intrinsic::getDeclaration(M, Intrinsic::nvvm_barrier0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2016-07-19 15:33:16 +08:00
|
|
|
Builder.CreateCall(Sync, {});
|
|
|
|
}
|
|
|
|
|
2016-07-21 21:15:59 +08:00
|
|
|
/// Collect llvm::Values referenced from @p Node
|
|
|
|
///
|
|
|
|
/// This function only applies to isl_ast_nodes that are user_nodes referring
|
|
|
|
/// to a ScopStmt. All other node types are ignore.
|
|
|
|
///
|
|
|
|
/// @param Node The node to collect references for.
|
|
|
|
/// @param User A user pointer used as storage for the data that is collected.
|
|
|
|
///
|
|
|
|
/// @returns isl_bool_true if data could be collected successfully.
|
|
|
|
isl_bool collectReferencesInGPUStmt(__isl_keep isl_ast_node *Node, void *User) {
|
|
|
|
if (isl_ast_node_get_type(Node) != isl_ast_node_user)
|
|
|
|
return isl_bool_true;
|
|
|
|
|
|
|
|
isl_ast_expr *Expr = isl_ast_node_user_get_expr(Node);
|
|
|
|
isl_ast_expr *StmtExpr = isl_ast_expr_get_op_arg(Expr, 0);
|
|
|
|
isl_id *Id = isl_ast_expr_get_id(StmtExpr);
|
|
|
|
const char *Str = isl_id_get_name(Id);
|
|
|
|
isl_id_free(Id);
|
|
|
|
isl_ast_expr_free(StmtExpr);
|
|
|
|
isl_ast_expr_free(Expr);
|
|
|
|
|
|
|
|
if (!isPrefix(Str, "Stmt"))
|
|
|
|
return isl_bool_true;
|
|
|
|
|
|
|
|
Id = isl_ast_node_get_annotation(Node);
|
|
|
|
auto *KernelStmt = (ppcg_kernel_stmt *)isl_id_get_user(Id);
|
|
|
|
auto Stmt = (ScopStmt *)KernelStmt->u.d.stmt->stmt;
|
|
|
|
isl_id_free(Id);
|
|
|
|
|
2016-08-04 14:55:59 +08:00
|
|
|
addReferencesFromStmt(Stmt, User, false /* CreateScalarRefs */);
|
2016-07-21 21:15:59 +08:00
|
|
|
|
|
|
|
return isl_bool_true;
|
|
|
|
}
|
|
|
|
|
|
|
|
SetVector<Value *> GPUNodeBuilder::getReferencesInKernel(ppcg_kernel *Kernel) {
|
|
|
|
SetVector<Value *> SubtreeValues;
|
|
|
|
SetVector<const SCEV *> SCEVs;
|
|
|
|
SetVector<const Loop *> Loops;
|
|
|
|
SubtreeReferences References = {
|
|
|
|
LI, SE, S, ValueMap, SubtreeValues, SCEVs, getBlockGenerator()};
|
|
|
|
|
|
|
|
for (const auto &I : IDToValue)
|
|
|
|
SubtreeValues.insert(I.second);
|
|
|
|
|
|
|
|
isl_ast_node_foreach_descendant_top_down(
|
|
|
|
Kernel->tree, collectReferencesInGPUStmt, &References);
|
|
|
|
|
|
|
|
for (const SCEV *Expr : SCEVs)
|
|
|
|
findValues(Expr, SE, SubtreeValues);
|
|
|
|
|
|
|
|
for (auto &SAI : S.arrays())
|
2016-07-30 17:25:51 +08:00
|
|
|
SubtreeValues.remove(SAI->getBasePtr());
|
2016-07-21 21:15:59 +08:00
|
|
|
|
|
|
|
isl_space *Space = S.getParamSpace();
|
|
|
|
for (long i = 0; i < isl_space_dim(Space, isl_dim_param); i++) {
|
|
|
|
isl_id *Id = isl_space_get_dim_id(Space, isl_dim_param, i);
|
|
|
|
assert(IDToValue.count(Id));
|
|
|
|
Value *Val = IDToValue[Id];
|
|
|
|
SubtreeValues.remove(Val);
|
|
|
|
isl_id_free(Id);
|
|
|
|
}
|
|
|
|
isl_space_free(Space);
|
|
|
|
|
|
|
|
for (long i = 0; i < isl_space_dim(Kernel->space, isl_dim_set); i++) {
|
|
|
|
isl_id *Id = isl_space_get_dim_id(Kernel->space, isl_dim_set, i);
|
|
|
|
assert(IDToValue.count(Id));
|
|
|
|
Value *Val = IDToValue[Id];
|
|
|
|
SubtreeValues.remove(Val);
|
|
|
|
isl_id_free(Id);
|
|
|
|
}
|
|
|
|
|
|
|
|
return SubtreeValues;
|
|
|
|
}
|
|
|
|
|
2016-07-22 15:11:12 +08:00
|
|
|
void GPUNodeBuilder::clearDominators(Function *F) {
|
|
|
|
DomTreeNode *N = DT.getNode(&F->getEntryBlock());
|
|
|
|
std::vector<BasicBlock *> Nodes;
|
|
|
|
for (po_iterator<DomTreeNode *> I = po_begin(N), E = po_end(N); I != E; ++I)
|
|
|
|
Nodes.push_back(I->getBlock());
|
|
|
|
|
|
|
|
for (BasicBlock *BB : Nodes)
|
|
|
|
DT.eraseNode(BB);
|
|
|
|
}
|
|
|
|
|
|
|
|
void GPUNodeBuilder::clearScalarEvolution(Function *F) {
|
|
|
|
for (BasicBlock &BB : *F) {
|
|
|
|
Loop *L = LI.getLoopFor(&BB);
|
|
|
|
if (L)
|
|
|
|
SE.forgetLoop(L);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void GPUNodeBuilder::clearLoops(Function *F) {
|
|
|
|
for (BasicBlock &BB : *F) {
|
|
|
|
Loop *L = LI.getLoopFor(&BB);
|
|
|
|
if (L)
|
|
|
|
SE.forgetLoop(L);
|
|
|
|
LI.removeBlock(&BB);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-27 21:20:16 +08:00
|
|
|
std::tuple<Value *, Value *> GPUNodeBuilder::getGridSizes(ppcg_kernel *Kernel) {
|
|
|
|
std::vector<Value *> Sizes;
|
|
|
|
isl_ast_build *Context = isl_ast_build_from_context(S.getContext());
|
|
|
|
|
|
|
|
for (long i = 0; i < Kernel->n_grid; i++) {
|
|
|
|
isl_pw_aff *Size = isl_multi_pw_aff_get_pw_aff(Kernel->grid_size, i);
|
|
|
|
isl_ast_expr *GridSize = isl_ast_build_expr_from_pw_aff(Context, Size);
|
|
|
|
Value *Res = ExprBuilder.create(GridSize);
|
|
|
|
Res = Builder.CreateTrunc(Res, Builder.getInt32Ty());
|
|
|
|
Sizes.push_back(Res);
|
|
|
|
}
|
|
|
|
isl_ast_build_free(Context);
|
|
|
|
|
|
|
|
for (long i = Kernel->n_grid; i < 3; i++)
|
|
|
|
Sizes.push_back(ConstantInt::get(Builder.getInt32Ty(), 1));
|
|
|
|
|
|
|
|
return std::make_tuple(Sizes[0], Sizes[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::tuple<Value *, Value *, Value *>
|
|
|
|
GPUNodeBuilder::getBlockSizes(ppcg_kernel *Kernel) {
|
|
|
|
std::vector<Value *> Sizes;
|
|
|
|
|
|
|
|
for (long i = 0; i < Kernel->n_block; i++) {
|
|
|
|
Value *Res = ConstantInt::get(Builder.getInt32Ty(), Kernel->block_dim[i]);
|
|
|
|
Sizes.push_back(Res);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (long i = Kernel->n_block; i < 3; i++)
|
|
|
|
Sizes.push_back(ConstantInt::get(Builder.getInt32Ty(), 1));
|
|
|
|
|
|
|
|
return std::make_tuple(Sizes[0], Sizes[1], Sizes[2]);
|
|
|
|
}
|
|
|
|
|
2017-05-09 18:45:52 +08:00
|
|
|
void GPUNodeBuilder::insertStoreParameter(Instruction *Parameters,
|
|
|
|
Instruction *Param, int Index) {
|
|
|
|
Value *Slot = Builder.CreateGEP(
|
|
|
|
Parameters, {Builder.getInt64(0), Builder.getInt64(Index)});
|
|
|
|
Value *ParamTyped = Builder.CreatePointerCast(Param, Builder.getInt8PtrTy());
|
|
|
|
Builder.CreateStore(ParamTyped, Slot);
|
|
|
|
}
|
|
|
|
|
2016-08-04 14:55:49 +08:00
|
|
|
Value *
|
|
|
|
GPUNodeBuilder::createLaunchParameters(ppcg_kernel *Kernel, Function *F,
|
|
|
|
SetVector<Value *> SubtreeValues) {
|
2017-05-09 18:45:52 +08:00
|
|
|
const int NumArgs = F->arg_size();
|
|
|
|
std::vector<int> ArgSizes(NumArgs);
|
|
|
|
|
|
|
|
Type *ArrayTy = ArrayType::get(Builder.getInt8PtrTy(), 2 * NumArgs);
|
2016-07-27 21:20:16 +08:00
|
|
|
|
|
|
|
BasicBlock *EntryBlock =
|
|
|
|
&Builder.GetInsertBlock()->getParent()->getEntryBlock();
|
2017-04-11 12:23:38 +08:00
|
|
|
auto AddressSpace = F->getParent()->getDataLayout().getAllocaAddrSpace();
|
2016-07-27 21:20:16 +08:00
|
|
|
std::string Launch = "polly_launch_" + std::to_string(Kernel->id);
|
2017-04-11 12:23:38 +08:00
|
|
|
Instruction *Parameters = new AllocaInst(
|
|
|
|
ArrayTy, AddressSpace, Launch + "_params", EntryBlock->getTerminator());
|
2016-07-27 21:20:16 +08:00
|
|
|
|
|
|
|
int Index = 0;
|
|
|
|
for (long i = 0; i < Prog->n_array; i++) {
|
|
|
|
if (!ppcg_kernel_requires_array_argument(Kernel, i))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
isl_id *Id = isl_space_get_tuple_id(Prog->array[i].space, isl_dim_set);
|
|
|
|
const ScopArrayInfo *SAI = ScopArrayInfo::getFromId(Id);
|
|
|
|
|
2017-05-09 18:45:52 +08:00
|
|
|
ArgSizes[Index] = SAI->getElemSizeInBytes();
|
|
|
|
|
2017-04-28 19:16:30 +08:00
|
|
|
Value *DevArray = nullptr;
|
|
|
|
if (ManagedMemory) {
|
|
|
|
DevArray = getOrCreateManagedDeviceArray(
|
|
|
|
&Prog->array[i], const_cast<ScopArrayInfo *>(SAI));
|
|
|
|
} else {
|
|
|
|
DevArray = DeviceAllocations[const_cast<ScopArrayInfo *>(SAI)];
|
|
|
|
DevArray = createCallGetDevicePtr(DevArray);
|
|
|
|
}
|
|
|
|
assert(DevArray != nullptr && "Array to be offloaded to device not "
|
|
|
|
"initialized");
|
2016-09-15 22:05:58 +08:00
|
|
|
Value *Offset = getArrayOffset(&Prog->array[i]);
|
|
|
|
|
|
|
|
if (Offset) {
|
|
|
|
DevArray = Builder.CreatePointerCast(
|
|
|
|
DevArray, SAI->getElementType()->getPointerTo());
|
|
|
|
DevArray = Builder.CreateGEP(DevArray, Builder.CreateNeg(Offset));
|
|
|
|
DevArray = Builder.CreatePointerCast(DevArray, Builder.getInt8PtrTy());
|
|
|
|
}
|
2016-07-28 14:47:53 +08:00
|
|
|
Value *Slot = Builder.CreateGEP(
|
|
|
|
Parameters, {Builder.getInt64(0), Builder.getInt64(Index)});
|
2016-09-18 03:22:18 +08:00
|
|
|
|
|
|
|
if (gpu_array_is_read_only_scalar(&Prog->array[i])) {
|
2017-04-28 19:16:30 +08:00
|
|
|
Value *ValPtr = nullptr;
|
|
|
|
if (ManagedMemory)
|
|
|
|
ValPtr = DevArray;
|
|
|
|
else
|
|
|
|
ValPtr = BlockGen.getOrCreateAlloca(SAI);
|
|
|
|
|
|
|
|
assert(ValPtr != nullptr && "ValPtr that should point to a valid object"
|
|
|
|
" to be stored into Parameters");
|
2016-09-18 03:22:18 +08:00
|
|
|
Value *ValPtrCast =
|
|
|
|
Builder.CreatePointerCast(ValPtr, Builder.getInt8PtrTy());
|
|
|
|
Builder.CreateStore(ValPtrCast, Slot);
|
|
|
|
} else {
|
2017-04-11 12:23:38 +08:00
|
|
|
Instruction *Param =
|
|
|
|
new AllocaInst(Builder.getInt8PtrTy(), AddressSpace,
|
|
|
|
Launch + "_param_" + std::to_string(Index),
|
|
|
|
EntryBlock->getTerminator());
|
2016-09-18 03:22:18 +08:00
|
|
|
Builder.CreateStore(DevArray, Param);
|
|
|
|
Value *ParamTyped =
|
|
|
|
Builder.CreatePointerCast(Param, Builder.getInt8PtrTy());
|
|
|
|
Builder.CreateStore(ParamTyped, Slot);
|
|
|
|
}
|
2016-07-27 21:20:16 +08:00
|
|
|
Index++;
|
|
|
|
}
|
|
|
|
|
2016-07-28 14:47:56 +08:00
|
|
|
int NumHostIters = isl_space_dim(Kernel->space, isl_dim_set);
|
|
|
|
|
|
|
|
for (long i = 0; i < NumHostIters; i++) {
|
|
|
|
isl_id *Id = isl_space_get_dim_id(Kernel->space, isl_dim_set, i);
|
|
|
|
Value *Val = IDToValue[Id];
|
2016-07-28 14:47:59 +08:00
|
|
|
isl_id_free(Id);
|
2017-05-09 18:45:52 +08:00
|
|
|
|
|
|
|
ArgSizes[Index] = computeSizeInBytes(Val->getType());
|
|
|
|
|
2017-04-11 12:23:38 +08:00
|
|
|
Instruction *Param =
|
|
|
|
new AllocaInst(Val->getType(), AddressSpace,
|
|
|
|
Launch + "_param_" + std::to_string(Index),
|
|
|
|
EntryBlock->getTerminator());
|
2016-07-28 14:47:59 +08:00
|
|
|
Builder.CreateStore(Val, Param);
|
2017-05-09 18:45:52 +08:00
|
|
|
insertStoreParameter(Parameters, Param, Index);
|
2016-07-28 14:47:59 +08:00
|
|
|
Index++;
|
|
|
|
}
|
|
|
|
|
|
|
|
int NumVars = isl_space_dim(Kernel->space, isl_dim_param);
|
|
|
|
|
|
|
|
for (long i = 0; i < NumVars; i++) {
|
|
|
|
isl_id *Id = isl_space_get_dim_id(Kernel->space, isl_dim_param, i);
|
|
|
|
Value *Val = IDToValue[Id];
|
2016-07-28 14:47:56 +08:00
|
|
|
isl_id_free(Id);
|
2017-05-09 18:45:52 +08:00
|
|
|
|
|
|
|
ArgSizes[Index] = computeSizeInBytes(Val->getType());
|
|
|
|
|
2017-04-11 12:23:38 +08:00
|
|
|
Instruction *Param =
|
|
|
|
new AllocaInst(Val->getType(), AddressSpace,
|
|
|
|
Launch + "_param_" + std::to_string(Index),
|
|
|
|
EntryBlock->getTerminator());
|
2016-07-28 14:47:56 +08:00
|
|
|
Builder.CreateStore(Val, Param);
|
2017-05-09 18:45:52 +08:00
|
|
|
insertStoreParameter(Parameters, Param, Index);
|
2016-07-28 14:47:56 +08:00
|
|
|
Index++;
|
|
|
|
}
|
|
|
|
|
2016-08-04 14:55:49 +08:00
|
|
|
for (auto Val : SubtreeValues) {
|
2017-05-09 18:45:52 +08:00
|
|
|
ArgSizes[Index] = computeSizeInBytes(Val->getType());
|
|
|
|
|
2017-04-11 12:23:38 +08:00
|
|
|
Instruction *Param =
|
|
|
|
new AllocaInst(Val->getType(), AddressSpace,
|
|
|
|
Launch + "_param_" + std::to_string(Index),
|
|
|
|
EntryBlock->getTerminator());
|
2016-08-04 14:55:49 +08:00
|
|
|
Builder.CreateStore(Val, Param);
|
2017-05-09 18:45:52 +08:00
|
|
|
insertStoreParameter(Parameters, Param, Index);
|
|
|
|
Index++;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i < NumArgs; i++) {
|
|
|
|
Value *Val = ConstantInt::get(Builder.getInt32Ty(), ArgSizes[i]);
|
|
|
|
Instruction *Param =
|
|
|
|
new AllocaInst(Builder.getInt32Ty(), AddressSpace,
|
|
|
|
Launch + "_param_size_" + std::to_string(i),
|
|
|
|
EntryBlock->getTerminator());
|
|
|
|
Builder.CreateStore(Val, Param);
|
|
|
|
insertStoreParameter(Parameters, Param, Index);
|
2016-08-04 14:55:49 +08:00
|
|
|
Index++;
|
|
|
|
}
|
|
|
|
|
2016-07-27 21:20:16 +08:00
|
|
|
auto Location = EntryBlock->getTerminator();
|
|
|
|
return new BitCastInst(Parameters, Builder.getInt8PtrTy(),
|
|
|
|
Launch + "_params_i8ptr", Location);
|
|
|
|
}
|
|
|
|
|
2016-07-19 15:32:38 +08:00
|
|
|
void GPUNodeBuilder::createKernel(__isl_take isl_ast_node *KernelStmt) {
|
|
|
|
isl_id *Id = isl_ast_node_get_annotation(KernelStmt);
|
|
|
|
ppcg_kernel *Kernel = (ppcg_kernel *)isl_id_get_user(Id);
|
|
|
|
isl_id_free(Id);
|
|
|
|
isl_ast_node_free(KernelStmt);
|
|
|
|
|
2016-09-18 16:31:09 +08:00
|
|
|
if (Kernel->n_grid > 1)
|
|
|
|
DeepestParallel =
|
|
|
|
std::max(DeepestParallel, isl_space_dim(Kernel->space, isl_dim_set));
|
|
|
|
else
|
|
|
|
DeepestSequential =
|
|
|
|
std::max(DeepestSequential, isl_space_dim(Kernel->space, isl_dim_set));
|
|
|
|
|
2016-08-05 14:47:43 +08:00
|
|
|
Value *BlockDimX, *BlockDimY, *BlockDimZ;
|
|
|
|
std::tie(BlockDimX, BlockDimY, BlockDimZ) = getBlockSizes(Kernel);
|
|
|
|
|
2016-07-21 21:15:59 +08:00
|
|
|
SetVector<Value *> SubtreeValues = getReferencesInKernel(Kernel);
|
|
|
|
|
2016-07-19 15:32:38 +08:00
|
|
|
assert(Kernel->tree && "Device AST of kernel node is empty");
|
|
|
|
|
|
|
|
Instruction &HostInsertPoint = *Builder.GetInsertPoint();
|
2016-07-19 15:32:44 +08:00
|
|
|
IslExprBuilder::IDToValueTy HostIDs = IDToValue;
|
2016-07-21 21:15:59 +08:00
|
|
|
ValueMapT HostValueMap = ValueMap;
|
[Polly] [BlockGenerator] Unify ScalarMap and PhiOpsMap
Instead of keeping two separate maps from Value to Allocas, one for
MemoryType::Value and the other for MemoryType::PHI, we introduce a single map
from ScopArrayInfo to the corresponding Alloca. This change is intended, both as
a general simplification and cleanup, but also to reduce our use of
MemoryAccess::getBaseAddr(). Moving away from using getBaseAddr() makes sure
we have only a single place where the array (and its base pointer) for which we
generate code for is specified, which means we can more easily introduce new
access functions that use a different ScopArrayInfo as base. We already today
experiment with modifiable access functions, so this change does not address
a specific bug, but it just reduces the scope one needs to reason about.
Another motivation for this patch is https://reviews.llvm.org/D28518, where
memory accesses with different base pointers could possibly be mapped to a
single ScopArrayInfo object. Such a mapping is currently not possible, as we
currently generate alloca instructions according to the base addresses of the
memory accesses, not according to the ScopArrayInfo object they belong to. By
making allocas ScopArrayInfo specific, a mapping to a single ScopArrayInfo
object will automatically mean that the same stack slot is used for these
arrays. For D28518 this is not a problem, as only MemoryType::Array objects are
mapping, but resolving this inconsistency will hopefully avoid confusion.
llvm-svn: 293374
2017-01-28 15:42:10 +08:00
|
|
|
BlockGenerator::AllocaMapTy HostScalarMap = ScalarMap;
|
2016-08-09 23:35:06 +08:00
|
|
|
ScalarMap.clear();
|
2016-07-21 21:15:59 +08:00
|
|
|
|
|
|
|
SetVector<const Loop *> Loops;
|
|
|
|
|
|
|
|
// Create for all loops we depend on values that contain the current loop
|
|
|
|
// iteration. These values are necessary to generate code for SCEVs that
|
|
|
|
// depend on such loops. As a result we need to pass them to the subfunction.
|
|
|
|
for (const Loop *L : Loops) {
|
|
|
|
const SCEV *OuterLIV = SE.getAddRecExpr(SE.getUnknown(Builder.getInt64(0)),
|
|
|
|
SE.getUnknown(Builder.getInt64(1)),
|
|
|
|
L, SCEV::FlagAnyWrap);
|
|
|
|
Value *V = generateSCEV(OuterLIV);
|
|
|
|
OutsideLoopIterations[L] = SE.getUnknown(V);
|
|
|
|
SubtreeValues.insert(V);
|
|
|
|
}
|
2016-07-19 15:32:38 +08:00
|
|
|
|
2016-07-21 21:15:59 +08:00
|
|
|
createKernelFunction(Kernel, SubtreeValues);
|
2016-07-19 15:32:38 +08:00
|
|
|
|
2016-07-19 15:33:11 +08:00
|
|
|
create(isl_ast_node_copy(Kernel->tree));
|
|
|
|
|
2016-09-18 03:22:31 +08:00
|
|
|
finalizeKernelArguments(Kernel);
|
2016-07-22 15:11:12 +08:00
|
|
|
Function *F = Builder.GetInsertBlock()->getParent();
|
2016-08-05 14:47:43 +08:00
|
|
|
addCUDAAnnotations(F->getParent(), BlockDimX, BlockDimY, BlockDimZ);
|
2016-07-22 15:11:12 +08:00
|
|
|
clearDominators(F);
|
|
|
|
clearScalarEvolution(F);
|
|
|
|
clearLoops(F);
|
|
|
|
|
2016-07-19 15:32:44 +08:00
|
|
|
IDToValue = HostIDs;
|
2016-07-19 15:32:38 +08:00
|
|
|
|
2016-08-09 23:35:06 +08:00
|
|
|
ValueMap = std::move(HostValueMap);
|
|
|
|
ScalarMap = std::move(HostScalarMap);
|
2016-07-21 21:15:59 +08:00
|
|
|
EscapeMap.clear();
|
|
|
|
IDToSAI.clear();
|
2016-07-22 15:11:12 +08:00
|
|
|
Annotator.resetAlternativeAliasBases();
|
|
|
|
for (auto &BasePtr : LocalArrays)
|
2017-01-15 04:25:44 +08:00
|
|
|
S.invalidateScopArrayInfo(BasePtr, MemoryKind::Array);
|
2016-07-22 15:11:12 +08:00
|
|
|
LocalArrays.clear();
|
2016-07-21 21:15:59 +08:00
|
|
|
|
2016-09-18 03:22:31 +08:00
|
|
|
std::string ASMString = finalizeKernelFunction();
|
|
|
|
Builder.SetInsertPoint(&HostInsertPoint);
|
2016-08-04 14:55:49 +08:00
|
|
|
Value *Parameters = createLaunchParameters(Kernel, F, SubtreeValues);
|
2016-07-27 21:20:16 +08:00
|
|
|
|
2016-07-26 00:31:21 +08:00
|
|
|
std::string Name = "kernel_" + std::to_string(Kernel->id);
|
|
|
|
Value *KernelString = Builder.CreateGlobalStringPtr(ASMString, Name);
|
|
|
|
Value *NameString = Builder.CreateGlobalStringPtr(Name, Name + "_name");
|
|
|
|
Value *GPUKernel = createCallGetKernel(KernelString, NameString);
|
2016-07-27 21:20:16 +08:00
|
|
|
|
|
|
|
Value *GridDimX, *GridDimY;
|
|
|
|
std::tie(GridDimX, GridDimY) = getGridSizes(Kernel);
|
|
|
|
|
|
|
|
createCallLaunchKernel(GPUKernel, GridDimX, GridDimY, BlockDimX, BlockDimY,
|
|
|
|
BlockDimZ, Parameters);
|
2016-07-26 00:31:21 +08:00
|
|
|
createCallFreeKernel(GPUKernel);
|
2016-08-04 20:18:14 +08:00
|
|
|
|
|
|
|
for (auto Id : KernelIds)
|
|
|
|
isl_id_free(Id);
|
|
|
|
|
|
|
|
KernelIds.clear();
|
2016-07-19 15:32:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Compute the DataLayout string for the NVPTX backend.
|
|
|
|
///
|
|
|
|
/// @param is64Bit Are we looking for a 64 bit architecture?
|
|
|
|
static std::string computeNVPTXDataLayout(bool is64Bit) {
|
[PPCGCodeGeneration] Update PPCG Code Generation for OpenCL compatibility
Added a small change to the way pointer arguments are set in the kernel
code generation. The way the pointer is retrieved now, specifically requests
global address space to be annotated. This is necessary, if the IR should be
run through NVPTX to generate OpenCL compatible PTX.
The changes do not affect the PTX Strings generated for the CUDA target
(nvptx64-nvidia-cuda), but are necessary for OpenCL (nvptx64-nvidia-nvcl).
Additionally, the data layout has been updated to what the NVPTX Backend requests/recommends.
Contributed-by: Philipp Schaad
Reviewers: Meinersbur, grosser, bollu
Reviewed By: grosser, bollu
Subscribers: jlebar, pollydev, llvm-commits, nemanjai, yaxunl, Anastasia
Tags: #polly
Differential Revision: https://reviews.llvm.org/D32215
llvm-svn: 301299
2017-04-25 16:08:29 +08:00
|
|
|
std::string Ret = "";
|
2016-07-19 15:32:38 +08:00
|
|
|
|
[PPCGCodeGeneration] Update PPCG Code Generation for OpenCL compatibility
Added a small change to the way pointer arguments are set in the kernel
code generation. The way the pointer is retrieved now, specifically requests
global address space to be annotated. This is necessary, if the IR should be
run through NVPTX to generate OpenCL compatible PTX.
The changes do not affect the PTX Strings generated for the CUDA target
(nvptx64-nvidia-cuda), but are necessary for OpenCL (nvptx64-nvidia-nvcl).
Additionally, the data layout has been updated to what the NVPTX Backend requests/recommends.
Contributed-by: Philipp Schaad
Reviewers: Meinersbur, grosser, bollu
Reviewed By: grosser, bollu
Subscribers: jlebar, pollydev, llvm-commits, nemanjai, yaxunl, Anastasia
Tags: #polly
Differential Revision: https://reviews.llvm.org/D32215
llvm-svn: 301299
2017-04-25 16:08:29 +08:00
|
|
|
if (!is64Bit) {
|
|
|
|
Ret += "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:"
|
|
|
|
"64-f32:32:32-f64:64:64-v16:16:16-v32:32:32-v64:64:"
|
|
|
|
"64-v128:128:128-n16:32:64";
|
|
|
|
} else {
|
|
|
|
Ret += "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:"
|
|
|
|
"64-f32:32:32-f64:64:64-v16:16:16-v32:32:32-v64:64:"
|
|
|
|
"64-v128:128:128-n16:32:64";
|
|
|
|
}
|
2016-07-19 15:32:38 +08:00
|
|
|
|
|
|
|
return Ret;
|
|
|
|
}
|
|
|
|
|
2016-07-21 21:15:59 +08:00
|
|
|
Function *
|
|
|
|
GPUNodeBuilder::createKernelFunctionDecl(ppcg_kernel *Kernel,
|
|
|
|
SetVector<Value *> &SubtreeValues) {
|
2016-07-19 15:32:38 +08:00
|
|
|
std::vector<Type *> Args;
|
|
|
|
std::string Identifier = "kernel_" + std::to_string(Kernel->id);
|
|
|
|
|
|
|
|
for (long i = 0; i < Prog->n_array; i++) {
|
|
|
|
if (!ppcg_kernel_requires_array_argument(Kernel, i))
|
|
|
|
continue;
|
|
|
|
|
2016-09-18 03:22:18 +08:00
|
|
|
if (gpu_array_is_read_only_scalar(&Prog->array[i])) {
|
|
|
|
isl_id *Id = isl_space_get_tuple_id(Prog->array[i].space, isl_dim_set);
|
|
|
|
const ScopArrayInfo *SAI = ScopArrayInfo::getFromId(Id);
|
|
|
|
Args.push_back(SAI->getElementType());
|
|
|
|
} else {
|
[PPCGCodeGeneration] Update PPCG Code Generation for OpenCL compatibility
Added a small change to the way pointer arguments are set in the kernel
code generation. The way the pointer is retrieved now, specifically requests
global address space to be annotated. This is necessary, if the IR should be
run through NVPTX to generate OpenCL compatible PTX.
The changes do not affect the PTX Strings generated for the CUDA target
(nvptx64-nvidia-cuda), but are necessary for OpenCL (nvptx64-nvidia-nvcl).
Additionally, the data layout has been updated to what the NVPTX Backend requests/recommends.
Contributed-by: Philipp Schaad
Reviewers: Meinersbur, grosser, bollu
Reviewed By: grosser, bollu
Subscribers: jlebar, pollydev, llvm-commits, nemanjai, yaxunl, Anastasia
Tags: #polly
Differential Revision: https://reviews.llvm.org/D32215
llvm-svn: 301299
2017-04-25 16:08:29 +08:00
|
|
|
static const int UseGlobalMemory = 1;
|
|
|
|
Args.push_back(Builder.getInt8PtrTy(UseGlobalMemory));
|
2016-09-18 03:22:18 +08:00
|
|
|
}
|
2016-07-19 15:32:38 +08:00
|
|
|
}
|
|
|
|
|
2016-07-19 15:32:55 +08:00
|
|
|
int NumHostIters = isl_space_dim(Kernel->space, isl_dim_set);
|
|
|
|
|
|
|
|
for (long i = 0; i < NumHostIters; i++)
|
|
|
|
Args.push_back(Builder.getInt64Ty());
|
|
|
|
|
2016-07-19 15:33:06 +08:00
|
|
|
int NumVars = isl_space_dim(Kernel->space, isl_dim_param);
|
|
|
|
|
2016-08-09 15:22:08 +08:00
|
|
|
for (long i = 0; i < NumVars; i++) {
|
|
|
|
isl_id *Id = isl_space_get_dim_id(Kernel->space, isl_dim_param, i);
|
|
|
|
Value *Val = IDToValue[Id];
|
|
|
|
isl_id_free(Id);
|
|
|
|
Args.push_back(Val->getType());
|
|
|
|
}
|
2016-07-19 15:33:06 +08:00
|
|
|
|
2016-07-21 21:15:59 +08:00
|
|
|
for (auto *V : SubtreeValues)
|
|
|
|
Args.push_back(V->getType());
|
|
|
|
|
2016-07-19 15:32:38 +08:00
|
|
|
auto *FT = FunctionType::get(Builder.getVoidTy(), Args, false);
|
|
|
|
auto *FN = Function::Create(FT, Function::ExternalLinkage, Identifier,
|
|
|
|
GPUModule.get());
|
[Polly] Added OpenCL Runtime to GPURuntime Library for GPGPU CodeGen
Summary:
When compiling for GPU, one can now choose to compile for OpenCL or CUDA,
with the corresponding polly-gpu-runtime flag (libopencl / libcudart). The
GPURuntime library (GPUJIT) has been extended with the OpenCL Runtime library
for that purpose, correctly choosing the corresponding library calls to the
option chosen when compiling (via different initialization calls).
Additionally, a specific GPU Target architecture can now be chosen with -polly-gpu-arch (only nvptx64 implemented thus far).
Reviewers: grosser, bollu, Meinersbur, etherzhhb, singam-sanjay
Reviewed By: grosser, Meinersbur
Subscribers: singam-sanjay, llvm-commits, pollydev, nemanjai, mgorny, yaxunl, Anastasia
Tags: #polly
Differential Revision: https://reviews.llvm.org/D32431
llvm-svn: 302379
2017-05-08 05:03:46 +08:00
|
|
|
|
|
|
|
switch (Arch) {
|
|
|
|
case GPUArch::NVPTX64:
|
|
|
|
FN->setCallingConv(CallingConv::PTX_Kernel);
|
|
|
|
break;
|
|
|
|
}
|
2016-07-19 15:32:38 +08:00
|
|
|
|
|
|
|
auto Arg = FN->arg_begin();
|
|
|
|
for (long i = 0; i < Kernel->n_array; i++) {
|
|
|
|
if (!ppcg_kernel_requires_array_argument(Kernel, i))
|
|
|
|
continue;
|
|
|
|
|
2016-07-21 21:15:59 +08:00
|
|
|
Arg->setName(Kernel->array[i].array->name);
|
|
|
|
|
|
|
|
isl_id *Id = isl_space_get_tuple_id(Prog->array[i].space, isl_dim_set);
|
|
|
|
const ScopArrayInfo *SAI = ScopArrayInfo::getFromId(isl_id_copy(Id));
|
|
|
|
Type *EleTy = SAI->getElementType();
|
|
|
|
Value *Val = &*Arg;
|
|
|
|
SmallVector<const SCEV *, 4> Sizes;
|
|
|
|
isl_ast_build *Build =
|
|
|
|
isl_ast_build_from_context(isl_set_copy(Prog->context));
|
2016-09-13 01:08:31 +08:00
|
|
|
Sizes.push_back(nullptr);
|
2016-07-21 21:15:59 +08:00
|
|
|
for (long j = 1; j < Kernel->array[i].array->n_index; j++) {
|
|
|
|
isl_ast_expr *DimSize = isl_ast_build_expr_from_pw_aff(
|
|
|
|
Build, isl_pw_aff_copy(Kernel->array[i].array->bound[j]));
|
|
|
|
auto V = ExprBuilder.create(DimSize);
|
|
|
|
Sizes.push_back(SE.getSCEV(V));
|
|
|
|
}
|
|
|
|
const ScopArrayInfo *SAIRep =
|
2017-01-15 04:25:44 +08:00
|
|
|
S.getOrCreateScopArrayInfo(Val, EleTy, Sizes, MemoryKind::Array);
|
2016-07-22 15:11:12 +08:00
|
|
|
LocalArrays.push_back(Val);
|
2016-07-21 21:15:59 +08:00
|
|
|
|
|
|
|
isl_ast_build_free(Build);
|
2016-08-04 20:18:14 +08:00
|
|
|
KernelIds.push_back(Id);
|
2016-07-21 21:15:59 +08:00
|
|
|
IDToSAI[Id] = SAIRep;
|
2016-07-19 15:32:38 +08:00
|
|
|
Arg++;
|
|
|
|
}
|
|
|
|
|
2016-07-19 15:32:55 +08:00
|
|
|
for (long i = 0; i < NumHostIters; i++) {
|
|
|
|
isl_id *Id = isl_space_get_dim_id(Kernel->space, isl_dim_set, i);
|
|
|
|
Arg->setName(isl_id_get_name(Id));
|
|
|
|
IDToValue[Id] = &*Arg;
|
|
|
|
KernelIDs.insert(std::unique_ptr<isl_id, IslIdDeleter>(Id));
|
|
|
|
Arg++;
|
|
|
|
}
|
|
|
|
|
2016-07-19 15:33:06 +08:00
|
|
|
for (long i = 0; i < NumVars; i++) {
|
|
|
|
isl_id *Id = isl_space_get_dim_id(Kernel->space, isl_dim_param, i);
|
|
|
|
Arg->setName(isl_id_get_name(Id));
|
2016-08-09 03:22:19 +08:00
|
|
|
Value *Val = IDToValue[Id];
|
|
|
|
ValueMap[Val] = &*Arg;
|
2016-07-19 15:33:06 +08:00
|
|
|
IDToValue[Id] = &*Arg;
|
|
|
|
KernelIDs.insert(std::unique_ptr<isl_id, IslIdDeleter>(Id));
|
|
|
|
Arg++;
|
|
|
|
}
|
|
|
|
|
2016-07-21 21:15:59 +08:00
|
|
|
for (auto *V : SubtreeValues) {
|
|
|
|
Arg->setName(V->getName());
|
|
|
|
ValueMap[V] = &*Arg;
|
|
|
|
Arg++;
|
|
|
|
}
|
|
|
|
|
2016-07-19 15:32:38 +08:00
|
|
|
return FN;
|
|
|
|
}
|
|
|
|
|
2016-07-19 15:32:44 +08:00
|
|
|
void GPUNodeBuilder::insertKernelIntrinsics(ppcg_kernel *Kernel) {
|
[Polly] Added OpenCL Runtime to GPURuntime Library for GPGPU CodeGen
Summary:
When compiling for GPU, one can now choose to compile for OpenCL or CUDA,
with the corresponding polly-gpu-runtime flag (libopencl / libcudart). The
GPURuntime library (GPUJIT) has been extended with the OpenCL Runtime library
for that purpose, correctly choosing the corresponding library calls to the
option chosen when compiling (via different initialization calls).
Additionally, a specific GPU Target architecture can now be chosen with -polly-gpu-arch (only nvptx64 implemented thus far).
Reviewers: grosser, bollu, Meinersbur, etherzhhb, singam-sanjay
Reviewed By: grosser, Meinersbur
Subscribers: singam-sanjay, llvm-commits, pollydev, nemanjai, mgorny, yaxunl, Anastasia
Tags: #polly
Differential Revision: https://reviews.llvm.org/D32431
llvm-svn: 302379
2017-05-08 05:03:46 +08:00
|
|
|
Intrinsic::ID IntrinsicsBID[2];
|
|
|
|
Intrinsic::ID IntrinsicsTID[3];
|
|
|
|
|
|
|
|
switch (Arch) {
|
|
|
|
case GPUArch::NVPTX64:
|
|
|
|
IntrinsicsBID[0] = Intrinsic::nvvm_read_ptx_sreg_ctaid_x;
|
|
|
|
IntrinsicsBID[1] = Intrinsic::nvvm_read_ptx_sreg_ctaid_y;
|
2016-07-19 15:32:44 +08:00
|
|
|
|
[Polly] Added OpenCL Runtime to GPURuntime Library for GPGPU CodeGen
Summary:
When compiling for GPU, one can now choose to compile for OpenCL or CUDA,
with the corresponding polly-gpu-runtime flag (libopencl / libcudart). The
GPURuntime library (GPUJIT) has been extended with the OpenCL Runtime library
for that purpose, correctly choosing the corresponding library calls to the
option chosen when compiling (via different initialization calls).
Additionally, a specific GPU Target architecture can now be chosen with -polly-gpu-arch (only nvptx64 implemented thus far).
Reviewers: grosser, bollu, Meinersbur, etherzhhb, singam-sanjay
Reviewed By: grosser, Meinersbur
Subscribers: singam-sanjay, llvm-commits, pollydev, nemanjai, mgorny, yaxunl, Anastasia
Tags: #polly
Differential Revision: https://reviews.llvm.org/D32431
llvm-svn: 302379
2017-05-08 05:03:46 +08:00
|
|
|
IntrinsicsTID[0] = Intrinsic::nvvm_read_ptx_sreg_tid_x;
|
|
|
|
IntrinsicsTID[1] = Intrinsic::nvvm_read_ptx_sreg_tid_y;
|
|
|
|
IntrinsicsTID[2] = Intrinsic::nvvm_read_ptx_sreg_tid_z;
|
|
|
|
break;
|
|
|
|
}
|
2016-07-19 15:32:44 +08:00
|
|
|
|
|
|
|
auto addId = [this](__isl_take isl_id *Id, Intrinsic::ID Intr) mutable {
|
|
|
|
std::string Name = isl_id_get_name(Id);
|
|
|
|
Module *M = Builder.GetInsertBlock()->getParent()->getParent();
|
|
|
|
Function *IntrinsicFn = Intrinsic::getDeclaration(M, Intr);
|
|
|
|
Value *Val = Builder.CreateCall(IntrinsicFn, {});
|
|
|
|
Val = Builder.CreateIntCast(Val, Builder.getInt64Ty(), false, Name);
|
|
|
|
IDToValue[Id] = Val;
|
|
|
|
KernelIDs.insert(std::unique_ptr<isl_id, IslIdDeleter>(Id));
|
|
|
|
};
|
|
|
|
|
|
|
|
for (int i = 0; i < Kernel->n_grid; ++i) {
|
|
|
|
isl_id *Id = isl_id_list_get_id(Kernel->block_ids, i);
|
|
|
|
addId(Id, IntrinsicsBID[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i < Kernel->n_block; ++i) {
|
|
|
|
isl_id *Id = isl_id_list_get_id(Kernel->thread_ids, i);
|
|
|
|
addId(Id, IntrinsicsTID[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-04 14:55:59 +08:00
|
|
|
void GPUNodeBuilder::prepareKernelArguments(ppcg_kernel *Kernel, Function *FN) {
|
|
|
|
auto Arg = FN->arg_begin();
|
|
|
|
for (long i = 0; i < Kernel->n_array; i++) {
|
|
|
|
if (!ppcg_kernel_requires_array_argument(Kernel, i))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
isl_id *Id = isl_space_get_tuple_id(Prog->array[i].space, isl_dim_set);
|
|
|
|
const ScopArrayInfo *SAI = ScopArrayInfo::getFromId(isl_id_copy(Id));
|
|
|
|
isl_id_free(Id);
|
|
|
|
|
|
|
|
if (SAI->getNumberOfDimensions() > 0) {
|
|
|
|
Arg++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2016-09-18 03:22:18 +08:00
|
|
|
Value *Val = &*Arg;
|
|
|
|
|
|
|
|
if (!gpu_array_is_read_only_scalar(&Prog->array[i])) {
|
|
|
|
Type *TypePtr = SAI->getElementType()->getPointerTo();
|
|
|
|
Value *TypedArgPtr = Builder.CreatePointerCast(Val, TypePtr);
|
|
|
|
Val = Builder.CreateLoad(TypedArgPtr);
|
|
|
|
}
|
|
|
|
|
2016-08-09 23:35:06 +08:00
|
|
|
Value *Alloca = BlockGen.getOrCreateAlloca(SAI);
|
2016-08-04 14:55:59 +08:00
|
|
|
Builder.CreateStore(Val, Alloca);
|
|
|
|
|
|
|
|
Arg++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-18 03:22:31 +08:00
|
|
|
void GPUNodeBuilder::finalizeKernelArguments(ppcg_kernel *Kernel) {
|
|
|
|
auto *FN = Builder.GetInsertBlock()->getParent();
|
|
|
|
auto Arg = FN->arg_begin();
|
|
|
|
|
|
|
|
bool StoredScalar = false;
|
|
|
|
for (long i = 0; i < Kernel->n_array; i++) {
|
|
|
|
if (!ppcg_kernel_requires_array_argument(Kernel, i))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
isl_id *Id = isl_space_get_tuple_id(Prog->array[i].space, isl_dim_set);
|
|
|
|
const ScopArrayInfo *SAI = ScopArrayInfo::getFromId(isl_id_copy(Id));
|
|
|
|
isl_id_free(Id);
|
|
|
|
|
|
|
|
if (SAI->getNumberOfDimensions() > 0) {
|
|
|
|
Arg++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (gpu_array_is_read_only_scalar(&Prog->array[i])) {
|
|
|
|
Arg++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
Value *Alloca = BlockGen.getOrCreateAlloca(SAI);
|
|
|
|
Value *ArgPtr = &*Arg;
|
|
|
|
Type *TypePtr = SAI->getElementType()->getPointerTo();
|
|
|
|
Value *TypedArgPtr = Builder.CreatePointerCast(ArgPtr, TypePtr);
|
|
|
|
Value *Val = Builder.CreateLoad(Alloca);
|
|
|
|
Builder.CreateStore(Val, TypedArgPtr);
|
|
|
|
StoredScalar = true;
|
|
|
|
|
|
|
|
Arg++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (StoredScalar)
|
|
|
|
/// In case more than one thread contains scalar stores, the generated
|
|
|
|
/// code might be incorrect, if we only store at the end of the kernel.
|
|
|
|
/// To support this case we need to store these scalars back at each
|
|
|
|
/// memory store or at least before each kernel barrier.
|
|
|
|
if (Kernel->n_block != 0 || Kernel->n_grid != 0)
|
|
|
|
BuildSuccessful = 0;
|
|
|
|
}
|
|
|
|
|
2016-08-04 20:18:14 +08:00
|
|
|
void GPUNodeBuilder::createKernelVariables(ppcg_kernel *Kernel, Function *FN) {
|
|
|
|
Module *M = Builder.GetInsertBlock()->getParent()->getParent();
|
|
|
|
|
|
|
|
for (int i = 0; i < Kernel->n_var; ++i) {
|
|
|
|
struct ppcg_kernel_var &Var = Kernel->var[i];
|
|
|
|
isl_id *Id = isl_space_get_tuple_id(Var.array->space, isl_dim_set);
|
|
|
|
Type *EleTy = ScopArrayInfo::getFromId(Id)->getElementType();
|
|
|
|
|
2016-08-04 21:57:29 +08:00
|
|
|
Type *ArrayTy = EleTy;
|
2016-08-04 20:18:14 +08:00
|
|
|
SmallVector<const SCEV *, 4> Sizes;
|
|
|
|
|
2016-09-13 01:08:31 +08:00
|
|
|
Sizes.push_back(nullptr);
|
2016-08-05 16:27:24 +08:00
|
|
|
for (unsigned int j = 1; j < Var.array->n_index; ++j) {
|
2016-08-04 20:18:14 +08:00
|
|
|
isl_val *Val = isl_vec_get_element_val(Var.size, j);
|
2016-08-04 21:57:29 +08:00
|
|
|
long Bound = isl_val_get_num_si(Val);
|
2016-08-04 20:18:14 +08:00
|
|
|
isl_val_free(Val);
|
|
|
|
Sizes.push_back(S.getSE()->getConstant(Builder.getInt64Ty(), Bound));
|
2016-08-05 16:27:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
for (int j = Var.array->n_index - 1; j >= 0; --j) {
|
|
|
|
isl_val *Val = isl_vec_get_element_val(Var.size, j);
|
|
|
|
long Bound = isl_val_get_num_si(Val);
|
|
|
|
isl_val_free(Val);
|
2016-08-04 20:18:14 +08:00
|
|
|
ArrayTy = ArrayType::get(ArrayTy, Bound);
|
|
|
|
}
|
|
|
|
|
2016-08-04 20:39:03 +08:00
|
|
|
const ScopArrayInfo *SAI;
|
|
|
|
Value *Allocation;
|
|
|
|
if (Var.type == ppcg_access_shared) {
|
|
|
|
auto GlobalVar = new GlobalVariable(
|
|
|
|
*M, ArrayTy, false, GlobalValue::InternalLinkage, 0, Var.name,
|
|
|
|
nullptr, GlobalValue::ThreadLocalMode::NotThreadLocal, 3);
|
|
|
|
GlobalVar->setAlignment(EleTy->getPrimitiveSizeInBits() / 8);
|
2016-08-04 21:57:29 +08:00
|
|
|
GlobalVar->setInitializer(Constant::getNullValue(ArrayTy));
|
|
|
|
|
2016-08-04 20:39:03 +08:00
|
|
|
Allocation = GlobalVar;
|
|
|
|
} else if (Var.type == ppcg_access_private) {
|
|
|
|
Allocation = Builder.CreateAlloca(ArrayTy, 0, "private_array");
|
|
|
|
} else {
|
|
|
|
llvm_unreachable("unknown variable type");
|
|
|
|
}
|
2017-01-15 04:25:44 +08:00
|
|
|
SAI =
|
|
|
|
S.getOrCreateScopArrayInfo(Allocation, EleTy, Sizes, MemoryKind::Array);
|
2016-08-04 20:18:14 +08:00
|
|
|
Id = isl_id_alloc(S.getIslCtx(), Var.name, nullptr);
|
2016-08-04 20:39:03 +08:00
|
|
|
IDToValue[Id] = Allocation;
|
|
|
|
LocalArrays.push_back(Allocation);
|
2016-08-04 20:18:14 +08:00
|
|
|
KernelIds.push_back(Id);
|
|
|
|
IDToSAI[Id] = SAI;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-21 21:15:59 +08:00
|
|
|
void GPUNodeBuilder::createKernelFunction(ppcg_kernel *Kernel,
|
|
|
|
SetVector<Value *> &SubtreeValues) {
|
2016-07-19 15:32:38 +08:00
|
|
|
std::string Identifier = "kernel_" + std::to_string(Kernel->id);
|
|
|
|
GPUModule.reset(new Module(Identifier, Builder.getContext()));
|
[Polly] Added OpenCL Runtime to GPURuntime Library for GPGPU CodeGen
Summary:
When compiling for GPU, one can now choose to compile for OpenCL or CUDA,
with the corresponding polly-gpu-runtime flag (libopencl / libcudart). The
GPURuntime library (GPUJIT) has been extended with the OpenCL Runtime library
for that purpose, correctly choosing the corresponding library calls to the
option chosen when compiling (via different initialization calls).
Additionally, a specific GPU Target architecture can now be chosen with -polly-gpu-arch (only nvptx64 implemented thus far).
Reviewers: grosser, bollu, Meinersbur, etherzhhb, singam-sanjay
Reviewed By: grosser, Meinersbur
Subscribers: singam-sanjay, llvm-commits, pollydev, nemanjai, mgorny, yaxunl, Anastasia
Tags: #polly
Differential Revision: https://reviews.llvm.org/D32431
llvm-svn: 302379
2017-05-08 05:03:46 +08:00
|
|
|
|
|
|
|
switch (Arch) {
|
|
|
|
case GPUArch::NVPTX64:
|
|
|
|
if (Runtime == GPURuntime::CUDA)
|
|
|
|
GPUModule->setTargetTriple(Triple::normalize("nvptx64-nvidia-cuda"));
|
|
|
|
else if (Runtime == GPURuntime::OpenCL)
|
|
|
|
GPUModule->setTargetTriple(Triple::normalize("nvptx64-nvidia-nvcl"));
|
|
|
|
GPUModule->setDataLayout(computeNVPTXDataLayout(true /* is64Bit */));
|
|
|
|
break;
|
|
|
|
}
|
2016-07-19 15:32:38 +08:00
|
|
|
|
2016-07-21 21:15:59 +08:00
|
|
|
Function *FN = createKernelFunctionDecl(Kernel, SubtreeValues);
|
2016-07-19 15:32:38 +08:00
|
|
|
|
2016-07-19 15:33:11 +08:00
|
|
|
BasicBlock *PrevBlock = Builder.GetInsertBlock();
|
2016-07-19 15:32:38 +08:00
|
|
|
auto EntryBlock = BasicBlock::Create(Builder.getContext(), "entry", FN);
|
|
|
|
|
2016-07-19 15:33:11 +08:00
|
|
|
DT.addNewBlock(EntryBlock, PrevBlock);
|
|
|
|
|
2016-07-19 15:32:38 +08:00
|
|
|
Builder.SetInsertPoint(EntryBlock);
|
|
|
|
Builder.CreateRetVoid();
|
|
|
|
Builder.SetInsertPoint(EntryBlock, EntryBlock->begin());
|
2016-07-19 15:32:44 +08:00
|
|
|
|
2016-08-03 20:00:07 +08:00
|
|
|
ScopDetection::markFunctionAsInvalid(FN);
|
|
|
|
|
2016-08-04 14:55:59 +08:00
|
|
|
prepareKernelArguments(Kernel, FN);
|
2016-08-04 20:18:14 +08:00
|
|
|
createKernelVariables(Kernel, FN);
|
2016-07-19 15:32:44 +08:00
|
|
|
insertKernelIntrinsics(Kernel);
|
2016-07-19 15:32:38 +08:00
|
|
|
}
|
|
|
|
|
2016-07-22 15:11:12 +08:00
|
|
|
std::string GPUNodeBuilder::createKernelASM() {
|
[Polly] Added OpenCL Runtime to GPURuntime Library for GPGPU CodeGen
Summary:
When compiling for GPU, one can now choose to compile for OpenCL or CUDA,
with the corresponding polly-gpu-runtime flag (libopencl / libcudart). The
GPURuntime library (GPUJIT) has been extended with the OpenCL Runtime library
for that purpose, correctly choosing the corresponding library calls to the
option chosen when compiling (via different initialization calls).
Additionally, a specific GPU Target architecture can now be chosen with -polly-gpu-arch (only nvptx64 implemented thus far).
Reviewers: grosser, bollu, Meinersbur, etherzhhb, singam-sanjay
Reviewed By: grosser, Meinersbur
Subscribers: singam-sanjay, llvm-commits, pollydev, nemanjai, mgorny, yaxunl, Anastasia
Tags: #polly
Differential Revision: https://reviews.llvm.org/D32431
llvm-svn: 302379
2017-05-08 05:03:46 +08:00
|
|
|
llvm::Triple GPUTriple;
|
|
|
|
|
|
|
|
switch (Arch) {
|
|
|
|
case GPUArch::NVPTX64:
|
|
|
|
switch (Runtime) {
|
|
|
|
case GPURuntime::CUDA:
|
|
|
|
GPUTriple = llvm::Triple(Triple::normalize("nvptx64-nvidia-cuda"));
|
|
|
|
break;
|
|
|
|
case GPURuntime::OpenCL:
|
|
|
|
GPUTriple = llvm::Triple(Triple::normalize("nvptx64-nvidia-nvcl"));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2016-07-22 15:11:12 +08:00
|
|
|
std::string ErrMsg;
|
|
|
|
auto GPUTarget = TargetRegistry::lookupTarget(GPUTriple.getTriple(), ErrMsg);
|
|
|
|
|
|
|
|
if (!GPUTarget) {
|
|
|
|
errs() << ErrMsg << "\n";
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
TargetOptions Options;
|
|
|
|
Options.UnsafeFPMath = FastMath;
|
[Polly] Added OpenCL Runtime to GPURuntime Library for GPGPU CodeGen
Summary:
When compiling for GPU, one can now choose to compile for OpenCL or CUDA,
with the corresponding polly-gpu-runtime flag (libopencl / libcudart). The
GPURuntime library (GPUJIT) has been extended with the OpenCL Runtime library
for that purpose, correctly choosing the corresponding library calls to the
option chosen when compiling (via different initialization calls).
Additionally, a specific GPU Target architecture can now be chosen with -polly-gpu-arch (only nvptx64 implemented thus far).
Reviewers: grosser, bollu, Meinersbur, etherzhhb, singam-sanjay
Reviewed By: grosser, Meinersbur
Subscribers: singam-sanjay, llvm-commits, pollydev, nemanjai, mgorny, yaxunl, Anastasia
Tags: #polly
Differential Revision: https://reviews.llvm.org/D32431
llvm-svn: 302379
2017-05-08 05:03:46 +08:00
|
|
|
|
|
|
|
std::string subtarget;
|
|
|
|
|
|
|
|
switch (Arch) {
|
|
|
|
case GPUArch::NVPTX64:
|
|
|
|
subtarget = CudaVersion;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::unique_ptr<TargetMachine> TargetM(GPUTarget->createTargetMachine(
|
|
|
|
GPUTriple.getTriple(), subtarget, "", Options, Optional<Reloc::Model>()));
|
2016-07-22 15:11:12 +08:00
|
|
|
|
|
|
|
SmallString<0> ASMString;
|
|
|
|
raw_svector_ostream ASMStream(ASMString);
|
|
|
|
llvm::legacy::PassManager PM;
|
|
|
|
|
|
|
|
PM.add(createTargetTransformInfoWrapperPass(TargetM->getTargetIRAnalysis()));
|
|
|
|
|
|
|
|
if (TargetM->addPassesToEmitFile(
|
|
|
|
PM, ASMStream, TargetMachine::CGFT_AssemblyFile, true /* verify */)) {
|
|
|
|
errs() << "The target does not support generation of this file type!\n";
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
PM.run(*GPUModule);
|
|
|
|
|
|
|
|
return ASMStream.str();
|
|
|
|
}
|
|
|
|
|
2016-07-26 00:31:21 +08:00
|
|
|
std::string GPUNodeBuilder::finalizeKernelFunction() {
|
2016-09-12 14:06:31 +08:00
|
|
|
if (verifyModule(*GPUModule)) {
|
|
|
|
BuildSuccessful = false;
|
|
|
|
return "";
|
|
|
|
}
|
2016-07-19 15:32:38 +08:00
|
|
|
|
|
|
|
if (DumpKernelIR)
|
|
|
|
outs() << *GPUModule << "\n";
|
|
|
|
|
2016-07-24 14:43:21 +08:00
|
|
|
// Optimize module.
|
|
|
|
llvm::legacy::PassManager OptPasses;
|
|
|
|
PassManagerBuilder PassBuilder;
|
|
|
|
PassBuilder.OptLevel = 3;
|
|
|
|
PassBuilder.SizeLevel = 0;
|
|
|
|
PassBuilder.populateModulePassManager(OptPasses);
|
|
|
|
OptPasses.run(*GPUModule);
|
|
|
|
|
2016-07-22 15:11:12 +08:00
|
|
|
std::string Assembly = createKernelASM();
|
|
|
|
|
|
|
|
if (DumpKernelASM)
|
|
|
|
outs() << Assembly << "\n";
|
|
|
|
|
2016-07-19 15:32:38 +08:00
|
|
|
GPUModule.release();
|
2016-07-19 15:32:44 +08:00
|
|
|
KernelIDs.clear();
|
2016-07-26 00:31:21 +08:00
|
|
|
|
|
|
|
return Assembly;
|
2016-07-19 15:32:38 +08:00
|
|
|
}
|
|
|
|
|
2016-07-13 23:54:58 +08:00
|
|
|
namespace {
|
|
|
|
class PPCGCodeGeneration : public ScopPass {
|
|
|
|
public:
|
|
|
|
static char ID;
|
|
|
|
|
[Polly] Added OpenCL Runtime to GPURuntime Library for GPGPU CodeGen
Summary:
When compiling for GPU, one can now choose to compile for OpenCL or CUDA,
with the corresponding polly-gpu-runtime flag (libopencl / libcudart). The
GPURuntime library (GPUJIT) has been extended with the OpenCL Runtime library
for that purpose, correctly choosing the corresponding library calls to the
option chosen when compiling (via different initialization calls).
Additionally, a specific GPU Target architecture can now be chosen with -polly-gpu-arch (only nvptx64 implemented thus far).
Reviewers: grosser, bollu, Meinersbur, etherzhhb, singam-sanjay
Reviewed By: grosser, Meinersbur
Subscribers: singam-sanjay, llvm-commits, pollydev, nemanjai, mgorny, yaxunl, Anastasia
Tags: #polly
Differential Revision: https://reviews.llvm.org/D32431
llvm-svn: 302379
2017-05-08 05:03:46 +08:00
|
|
|
GPURuntime Runtime = GPURuntime::CUDA;
|
|
|
|
|
|
|
|
GPUArch Architecture = GPUArch::NVPTX64;
|
|
|
|
|
2016-07-14 18:22:19 +08:00
|
|
|
/// The scop that is currently processed.
|
|
|
|
Scop *S;
|
|
|
|
|
2016-07-18 19:56:39 +08:00
|
|
|
LoopInfo *LI;
|
|
|
|
DominatorTree *DT;
|
|
|
|
ScalarEvolution *SE;
|
|
|
|
const DataLayout *DL;
|
|
|
|
RegionInfo *RI;
|
|
|
|
|
2016-07-13 23:54:58 +08:00
|
|
|
PPCGCodeGeneration() : ScopPass(ID) {}
|
|
|
|
|
2016-07-14 18:22:19 +08:00
|
|
|
/// Construct compilation options for PPCG.
|
|
|
|
///
|
|
|
|
/// @returns The compilation options.
|
|
|
|
ppcg_options *createPPCGOptions() {
|
|
|
|
auto DebugOptions =
|
|
|
|
(ppcg_debug_options *)malloc(sizeof(ppcg_debug_options));
|
|
|
|
auto Options = (ppcg_options *)malloc(sizeof(ppcg_options));
|
|
|
|
|
|
|
|
DebugOptions->dump_schedule_constraints = false;
|
|
|
|
DebugOptions->dump_schedule = false;
|
|
|
|
DebugOptions->dump_final_schedule = false;
|
|
|
|
DebugOptions->dump_sizes = false;
|
2016-08-04 20:44:03 +08:00
|
|
|
DebugOptions->verbose = false;
|
2016-07-14 18:22:19 +08:00
|
|
|
|
|
|
|
Options->debug = DebugOptions;
|
|
|
|
|
|
|
|
Options->reschedule = true;
|
|
|
|
Options->scale_tile_loops = false;
|
|
|
|
Options->wrap = false;
|
|
|
|
|
|
|
|
Options->non_negative_parameters = false;
|
|
|
|
Options->ctx = nullptr;
|
|
|
|
Options->sizes = nullptr;
|
|
|
|
|
2016-07-14 22:14:02 +08:00
|
|
|
Options->tile_size = 32;
|
|
|
|
|
2016-08-04 20:39:03 +08:00
|
|
|
Options->use_private_memory = PrivateMemory;
|
2016-08-04 20:18:14 +08:00
|
|
|
Options->use_shared_memory = SharedMemory;
|
|
|
|
Options->max_shared_memory = 48 * 1024;
|
2016-07-14 18:22:19 +08:00
|
|
|
|
|
|
|
Options->target = PPCG_TARGET_CUDA;
|
|
|
|
Options->openmp = false;
|
|
|
|
Options->linearize_device_arrays = true;
|
|
|
|
Options->live_range_reordering = false;
|
|
|
|
|
|
|
|
Options->opencl_compiler_options = nullptr;
|
|
|
|
Options->opencl_use_gpu = false;
|
|
|
|
Options->opencl_n_include_file = 0;
|
|
|
|
Options->opencl_include_files = nullptr;
|
|
|
|
Options->opencl_print_kernel_types = false;
|
|
|
|
Options->opencl_embed_kernel_code = false;
|
|
|
|
|
|
|
|
Options->save_schedule_file = nullptr;
|
|
|
|
Options->load_schedule_file = nullptr;
|
|
|
|
|
|
|
|
return Options;
|
|
|
|
}
|
|
|
|
|
2016-07-14 18:22:25 +08:00
|
|
|
/// Get a tagged access relation containing all accesses of type @p AccessTy.
|
|
|
|
///
|
|
|
|
/// Instead of a normal access of the form:
|
|
|
|
///
|
|
|
|
/// Stmt[i,j,k] -> Array[f_0(i,j,k), f_1(i,j,k)]
|
|
|
|
///
|
|
|
|
/// a tagged access has the form
|
|
|
|
///
|
|
|
|
/// [Stmt[i,j,k] -> id[]] -> Array[f_0(i,j,k), f_1(i,j,k)]
|
|
|
|
///
|
|
|
|
/// where 'id' is an additional space that references the memory access that
|
|
|
|
/// triggered the access.
|
|
|
|
///
|
|
|
|
/// @param AccessTy The type of the memory accesses to collect.
|
|
|
|
///
|
|
|
|
/// @return The relation describing all tagged memory accesses.
|
|
|
|
isl_union_map *getTaggedAccesses(enum MemoryAccess::AccessType AccessTy) {
|
|
|
|
isl_union_map *Accesses = isl_union_map_empty(S->getParamSpace());
|
|
|
|
|
|
|
|
for (auto &Stmt : *S)
|
|
|
|
for (auto &Acc : Stmt)
|
|
|
|
if (Acc->getType() == AccessTy) {
|
|
|
|
isl_map *Relation = Acc->getAccessRelation();
|
|
|
|
Relation = isl_map_intersect_domain(Relation, Stmt.getDomain());
|
|
|
|
|
|
|
|
isl_space *Space = isl_map_get_space(Relation);
|
|
|
|
Space = isl_space_range(Space);
|
|
|
|
Space = isl_space_from_range(Space);
|
2016-07-15 20:44:27 +08:00
|
|
|
Space = isl_space_set_tuple_id(Space, isl_dim_in, Acc->getId());
|
2016-07-14 18:22:25 +08:00
|
|
|
isl_map *Universe = isl_map_universe(Space);
|
|
|
|
Relation = isl_map_domain_product(Relation, Universe);
|
|
|
|
Accesses = isl_union_map_add_map(Accesses, Relation);
|
|
|
|
}
|
|
|
|
|
|
|
|
return Accesses;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get the set of all read accesses, tagged with the access id.
|
|
|
|
///
|
|
|
|
/// @see getTaggedAccesses
|
|
|
|
isl_union_map *getTaggedReads() {
|
|
|
|
return getTaggedAccesses(MemoryAccess::READ);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get the set of all may (and must) accesses, tagged with the access id.
|
|
|
|
///
|
|
|
|
/// @see getTaggedAccesses
|
|
|
|
isl_union_map *getTaggedMayWrites() {
|
|
|
|
return isl_union_map_union(getTaggedAccesses(MemoryAccess::MAY_WRITE),
|
|
|
|
getTaggedAccesses(MemoryAccess::MUST_WRITE));
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get the set of all must accesses, tagged with the access id.
|
|
|
|
///
|
|
|
|
/// @see getTaggedAccesses
|
|
|
|
isl_union_map *getTaggedMustWrites() {
|
|
|
|
return getTaggedAccesses(MemoryAccess::MUST_WRITE);
|
|
|
|
}
|
|
|
|
|
2016-07-14 18:51:52 +08:00
|
|
|
/// Collect parameter and array names as isl_ids.
|
|
|
|
///
|
|
|
|
/// To reason about the different parameters and arrays used, ppcg requires
|
|
|
|
/// a list of all isl_ids in use. As PPCG traditionally performs
|
|
|
|
/// source-to-source compilation each of these isl_ids is mapped to the
|
|
|
|
/// expression that represents it. As we do not have a corresponding
|
|
|
|
/// expression in Polly, we just map each id to a 'zero' expression to match
|
|
|
|
/// the data format that ppcg expects.
|
|
|
|
///
|
|
|
|
/// @returns Retun a map from collected ids to 'zero' ast expressions.
|
|
|
|
__isl_give isl_id_to_ast_expr *getNames() {
|
|
|
|
auto *Names = isl_id_to_ast_expr_alloc(
|
2016-07-14 18:53:00 +08:00
|
|
|
S->getIslCtx(),
|
|
|
|
S->getNumParams() + std::distance(S->array_begin(), S->array_end()));
|
2016-07-14 18:51:52 +08:00
|
|
|
auto *Zero = isl_ast_expr_from_val(isl_val_zero(S->getIslCtx()));
|
|
|
|
auto *Space = S->getParamSpace();
|
|
|
|
|
|
|
|
for (int I = 0, E = S->getNumParams(); I < E; ++I) {
|
|
|
|
isl_id *Id = isl_space_get_dim_id(Space, isl_dim_param, I);
|
|
|
|
Names = isl_id_to_ast_expr_set(Names, Id, isl_ast_expr_copy(Zero));
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto &Array : S->arrays()) {
|
2016-07-30 17:25:51 +08:00
|
|
|
auto Id = Array->getBasePtrId();
|
2016-07-14 18:51:52 +08:00
|
|
|
Names = isl_id_to_ast_expr_set(Names, Id, isl_ast_expr_copy(Zero));
|
|
|
|
}
|
|
|
|
|
|
|
|
isl_space_free(Space);
|
|
|
|
isl_ast_expr_free(Zero);
|
|
|
|
|
|
|
|
return Names;
|
|
|
|
}
|
|
|
|
|
2016-07-14 18:22:19 +08:00
|
|
|
/// Create a new PPCG scop from the current scop.
|
|
|
|
///
|
2016-07-14 18:22:25 +08:00
|
|
|
/// The PPCG scop is initialized with data from the current polly::Scop. From
|
|
|
|
/// this initial data, the data-dependences in the PPCG scop are initialized.
|
|
|
|
/// We do not use Polly's dependence analysis for now, to ensure we match
|
|
|
|
/// the PPCG default behaviour more closely.
|
2016-07-14 18:22:19 +08:00
|
|
|
///
|
|
|
|
/// @returns A new ppcg scop.
|
|
|
|
ppcg_scop *createPPCGScop() {
|
|
|
|
auto PPCGScop = (ppcg_scop *)malloc(sizeof(ppcg_scop));
|
|
|
|
|
|
|
|
PPCGScop->options = createPPCGOptions();
|
|
|
|
|
|
|
|
PPCGScop->start = 0;
|
|
|
|
PPCGScop->end = 0;
|
|
|
|
|
2016-07-14 18:22:25 +08:00
|
|
|
PPCGScop->context = S->getContext();
|
|
|
|
PPCGScop->domain = S->getDomains();
|
2016-07-14 18:22:19 +08:00
|
|
|
PPCGScop->call = nullptr;
|
2016-07-14 18:22:25 +08:00
|
|
|
PPCGScop->tagged_reads = getTaggedReads();
|
|
|
|
PPCGScop->reads = S->getReads();
|
2016-07-14 18:22:19 +08:00
|
|
|
PPCGScop->live_in = nullptr;
|
2016-07-14 18:22:25 +08:00
|
|
|
PPCGScop->tagged_may_writes = getTaggedMayWrites();
|
|
|
|
PPCGScop->may_writes = S->getWrites();
|
|
|
|
PPCGScop->tagged_must_writes = getTaggedMustWrites();
|
|
|
|
PPCGScop->must_writes = S->getMustWrites();
|
2016-07-14 18:22:19 +08:00
|
|
|
PPCGScop->live_out = nullptr;
|
2016-07-14 18:22:25 +08:00
|
|
|
PPCGScop->tagged_must_kills = isl_union_map_empty(S->getParamSpace());
|
2016-07-14 18:22:19 +08:00
|
|
|
PPCGScop->tagger = nullptr;
|
|
|
|
|
|
|
|
PPCGScop->independence = nullptr;
|
|
|
|
PPCGScop->dep_flow = nullptr;
|
|
|
|
PPCGScop->tagged_dep_flow = nullptr;
|
|
|
|
PPCGScop->dep_false = nullptr;
|
|
|
|
PPCGScop->dep_forced = nullptr;
|
|
|
|
PPCGScop->dep_order = nullptr;
|
|
|
|
PPCGScop->tagged_dep_order = nullptr;
|
|
|
|
|
2016-07-14 18:22:25 +08:00
|
|
|
PPCGScop->schedule = S->getScheduleTree();
|
2016-07-14 18:51:52 +08:00
|
|
|
PPCGScop->names = getNames();
|
2016-07-14 18:22:19 +08:00
|
|
|
|
|
|
|
PPCGScop->pet = nullptr;
|
|
|
|
|
2016-07-14 18:22:25 +08:00
|
|
|
compute_tagger(PPCGScop);
|
|
|
|
compute_dependences(PPCGScop);
|
|
|
|
|
2016-07-14 18:22:19 +08:00
|
|
|
return PPCGScop;
|
|
|
|
}
|
|
|
|
|
2016-07-15 15:05:54 +08:00
|
|
|
/// Collect the array acesses in a statement.
|
|
|
|
///
|
|
|
|
/// @param Stmt The statement for which to collect the accesses.
|
|
|
|
///
|
|
|
|
/// @returns A list of array accesses.
|
|
|
|
gpu_stmt_access *getStmtAccesses(ScopStmt &Stmt) {
|
|
|
|
gpu_stmt_access *Accesses = nullptr;
|
|
|
|
|
|
|
|
for (MemoryAccess *Acc : Stmt) {
|
|
|
|
auto Access = isl_alloc_type(S->getIslCtx(), struct gpu_stmt_access);
|
|
|
|
Access->read = Acc->isRead();
|
|
|
|
Access->write = Acc->isWrite();
|
|
|
|
Access->access = Acc->getAccessRelation();
|
|
|
|
isl_space *Space = isl_map_get_space(Access->access);
|
|
|
|
Space = isl_space_range(Space);
|
|
|
|
Space = isl_space_from_range(Space);
|
2016-07-15 20:44:27 +08:00
|
|
|
Space = isl_space_set_tuple_id(Space, isl_dim_in, Acc->getId());
|
2016-07-15 15:05:54 +08:00
|
|
|
isl_map *Universe = isl_map_universe(Space);
|
|
|
|
Access->tagged_access =
|
|
|
|
isl_map_domain_product(Acc->getAccessRelation(), Universe);
|
2016-08-04 20:18:14 +08:00
|
|
|
Access->exact_write = !Acc->isMayWrite();
|
2016-07-15 15:05:54 +08:00
|
|
|
Access->ref_id = Acc->getId();
|
|
|
|
Access->next = Accesses;
|
2016-08-04 20:18:14 +08:00
|
|
|
Access->n_index = Acc->getScopArrayInfo()->getNumberOfDimensions();
|
2016-07-15 15:05:54 +08:00
|
|
|
Accesses = Access;
|
|
|
|
}
|
|
|
|
|
|
|
|
return Accesses;
|
|
|
|
}
|
|
|
|
|
2016-07-14 23:51:37 +08:00
|
|
|
/// Collect the list of GPU statements.
|
|
|
|
///
|
|
|
|
/// Each statement has an id, a pointer to the underlying data structure,
|
|
|
|
/// as well as a list with all memory accesses.
|
|
|
|
///
|
|
|
|
/// TODO: Initialize the list of memory accesses.
|
|
|
|
///
|
|
|
|
/// @returns A linked-list of statements.
|
|
|
|
gpu_stmt *getStatements() {
|
|
|
|
gpu_stmt *Stmts = isl_calloc_array(S->getIslCtx(), struct gpu_stmt,
|
|
|
|
std::distance(S->begin(), S->end()));
|
|
|
|
|
|
|
|
int i = 0;
|
|
|
|
for (auto &Stmt : *S) {
|
|
|
|
gpu_stmt *GPUStmt = &Stmts[i];
|
|
|
|
|
|
|
|
GPUStmt->id = Stmt.getDomainId();
|
|
|
|
|
|
|
|
// We use the pet stmt pointer to keep track of the Polly statements.
|
|
|
|
GPUStmt->stmt = (pet_stmt *)&Stmt;
|
2016-07-15 15:05:54 +08:00
|
|
|
GPUStmt->accesses = getStmtAccesses(Stmt);
|
2016-07-14 23:51:37 +08:00
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return Stmts;
|
|
|
|
}
|
|
|
|
|
2016-07-15 15:05:54 +08:00
|
|
|
/// Derive the extent of an array.
|
|
|
|
///
|
2016-08-10 18:58:19 +08:00
|
|
|
/// The extent of an array is the set of elements that are within the
|
|
|
|
/// accessed array. For the inner dimensions, the extent constraints are
|
|
|
|
/// 0 and the size of the corresponding array dimension. For the first
|
|
|
|
/// (outermost) dimension, the extent constraints are the minimal and maximal
|
|
|
|
/// subscript value for the first dimension.
|
2016-07-15 15:05:54 +08:00
|
|
|
///
|
|
|
|
/// @param Array The array to derive the extent for.
|
|
|
|
///
|
|
|
|
/// @returns An isl_set describing the extent of the array.
|
|
|
|
__isl_give isl_set *getExtent(ScopArrayInfo *Array) {
|
2016-08-10 18:58:19 +08:00
|
|
|
unsigned NumDims = Array->getNumberOfDimensions();
|
2016-07-15 15:05:54 +08:00
|
|
|
isl_union_map *Accesses = S->getAccesses();
|
|
|
|
Accesses = isl_union_map_intersect_domain(Accesses, S->getDomains());
|
2016-08-10 18:58:19 +08:00
|
|
|
Accesses = isl_union_map_detect_equalities(Accesses);
|
2016-07-15 15:05:54 +08:00
|
|
|
isl_union_set *AccessUSet = isl_union_map_range(Accesses);
|
2016-08-10 18:58:19 +08:00
|
|
|
AccessUSet = isl_union_set_coalesce(AccessUSet);
|
|
|
|
AccessUSet = isl_union_set_detect_equalities(AccessUSet);
|
|
|
|
AccessUSet = isl_union_set_coalesce(AccessUSet);
|
|
|
|
|
|
|
|
if (isl_union_set_is_empty(AccessUSet)) {
|
|
|
|
isl_union_set_free(AccessUSet);
|
|
|
|
return isl_set_empty(Array->getSpace());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Array->getNumberOfDimensions() == 0) {
|
|
|
|
isl_union_set_free(AccessUSet);
|
|
|
|
return isl_set_universe(Array->getSpace());
|
|
|
|
}
|
|
|
|
|
2016-07-15 15:05:54 +08:00
|
|
|
isl_set *AccessSet =
|
|
|
|
isl_union_set_extract_set(AccessUSet, Array->getSpace());
|
2016-08-10 18:58:19 +08:00
|
|
|
|
2016-07-15 15:05:54 +08:00
|
|
|
isl_union_set_free(AccessUSet);
|
2016-08-10 18:58:19 +08:00
|
|
|
isl_local_space *LS = isl_local_space_from_space(Array->getSpace());
|
|
|
|
|
|
|
|
isl_pw_aff *Val =
|
|
|
|
isl_pw_aff_from_aff(isl_aff_var_on_domain(LS, isl_dim_set, 0));
|
|
|
|
|
|
|
|
isl_pw_aff *OuterMin = isl_set_dim_min(isl_set_copy(AccessSet), 0);
|
|
|
|
isl_pw_aff *OuterMax = isl_set_dim_max(AccessSet, 0);
|
|
|
|
OuterMin = isl_pw_aff_add_dims(OuterMin, isl_dim_in,
|
|
|
|
isl_pw_aff_dim(Val, isl_dim_in));
|
|
|
|
OuterMax = isl_pw_aff_add_dims(OuterMax, isl_dim_in,
|
|
|
|
isl_pw_aff_dim(Val, isl_dim_in));
|
|
|
|
OuterMin =
|
|
|
|
isl_pw_aff_set_tuple_id(OuterMin, isl_dim_in, Array->getBasePtrId());
|
|
|
|
OuterMax =
|
|
|
|
isl_pw_aff_set_tuple_id(OuterMax, isl_dim_in, Array->getBasePtrId());
|
|
|
|
|
|
|
|
isl_set *Extent = isl_set_universe(Array->getSpace());
|
|
|
|
|
|
|
|
Extent = isl_set_intersect(
|
|
|
|
Extent, isl_pw_aff_le_set(OuterMin, isl_pw_aff_copy(Val)));
|
|
|
|
Extent = isl_set_intersect(Extent, isl_pw_aff_ge_set(OuterMax, Val));
|
|
|
|
|
|
|
|
for (unsigned i = 1; i < NumDims; ++i)
|
|
|
|
Extent = isl_set_lower_bound_si(Extent, isl_dim_set, i, 0);
|
|
|
|
|
|
|
|
for (unsigned i = 1; i < NumDims; ++i) {
|
|
|
|
isl_pw_aff *PwAff =
|
|
|
|
const_cast<isl_pw_aff *>(Array->getDimensionSizePw(i));
|
|
|
|
isl_pw_aff *Val = isl_pw_aff_from_aff(isl_aff_var_on_domain(
|
|
|
|
isl_local_space_from_space(Array->getSpace()), isl_dim_set, i));
|
|
|
|
PwAff = isl_pw_aff_add_dims(PwAff, isl_dim_in,
|
|
|
|
isl_pw_aff_dim(Val, isl_dim_in));
|
|
|
|
PwAff = isl_pw_aff_set_tuple_id(PwAff, isl_dim_in,
|
|
|
|
isl_pw_aff_get_tuple_id(Val, isl_dim_in));
|
|
|
|
auto *Set = isl_pw_aff_gt_set(PwAff, Val);
|
|
|
|
Extent = isl_set_intersect(Set, Extent);
|
|
|
|
}
|
2016-07-15 15:05:54 +08:00
|
|
|
|
2016-08-10 18:58:19 +08:00
|
|
|
return Extent;
|
2016-07-15 15:05:54 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Derive the bounds of an array.
|
|
|
|
///
|
|
|
|
/// For the first dimension we derive the bound of the array from the extent
|
|
|
|
/// of this dimension. For inner dimensions we obtain their size directly from
|
|
|
|
/// ScopArrayInfo.
|
|
|
|
///
|
|
|
|
/// @param PPCGArray The array to compute bounds for.
|
|
|
|
/// @param Array The polly array from which to take the information.
|
|
|
|
void setArrayBounds(gpu_array_info &PPCGArray, ScopArrayInfo *Array) {
|
|
|
|
if (PPCGArray.n_index > 0) {
|
2016-09-11 21:30:12 +08:00
|
|
|
if (isl_set_is_empty(PPCGArray.extent)) {
|
|
|
|
isl_set *Dom = isl_set_copy(PPCGArray.extent);
|
|
|
|
isl_local_space *LS = isl_local_space_from_space(
|
|
|
|
isl_space_params(isl_set_get_space(Dom)));
|
|
|
|
isl_set_free(Dom);
|
|
|
|
isl_aff *Zero = isl_aff_zero_on_domain(LS);
|
|
|
|
PPCGArray.bound[0] = isl_pw_aff_from_aff(Zero);
|
|
|
|
} else {
|
|
|
|
isl_set *Dom = isl_set_copy(PPCGArray.extent);
|
|
|
|
Dom = isl_set_project_out(Dom, isl_dim_set, 1, PPCGArray.n_index - 1);
|
|
|
|
isl_pw_aff *Bound = isl_set_dim_max(isl_set_copy(Dom), 0);
|
|
|
|
isl_set_free(Dom);
|
|
|
|
Dom = isl_pw_aff_domain(isl_pw_aff_copy(Bound));
|
|
|
|
isl_local_space *LS =
|
|
|
|
isl_local_space_from_space(isl_set_get_space(Dom));
|
|
|
|
isl_aff *One = isl_aff_zero_on_domain(LS);
|
|
|
|
One = isl_aff_add_constant_si(One, 1);
|
|
|
|
Bound = isl_pw_aff_add(Bound, isl_pw_aff_alloc(Dom, One));
|
|
|
|
Bound = isl_pw_aff_gist(Bound, S->getContext());
|
|
|
|
PPCGArray.bound[0] = Bound;
|
|
|
|
}
|
2016-07-15 15:05:54 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
for (unsigned i = 1; i < PPCGArray.n_index; ++i) {
|
|
|
|
isl_pw_aff *Bound = Array->getDimensionSizePw(i);
|
|
|
|
auto LS = isl_pw_aff_get_domain_space(Bound);
|
|
|
|
auto Aff = isl_multi_aff_zero(LS);
|
|
|
|
Bound = isl_pw_aff_pullback_multi_aff(Bound, Aff);
|
|
|
|
PPCGArray.bound[i] = Bound;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Create the arrays for @p PPCGProg.
|
|
|
|
///
|
|
|
|
/// @param PPCGProg The program to compute the arrays for.
|
|
|
|
void createArrays(gpu_prog *PPCGProg) {
|
|
|
|
int i = 0;
|
2016-07-30 17:25:51 +08:00
|
|
|
for (auto &Array : S->arrays()) {
|
2016-07-15 15:05:54 +08:00
|
|
|
std::string TypeName;
|
|
|
|
raw_string_ostream OS(TypeName);
|
|
|
|
|
|
|
|
OS << *Array->getElementType();
|
|
|
|
TypeName = OS.str();
|
|
|
|
|
|
|
|
gpu_array_info &PPCGArray = PPCGProg->array[i];
|
|
|
|
|
|
|
|
PPCGArray.space = Array->getSpace();
|
|
|
|
PPCGArray.type = strdup(TypeName.c_str());
|
|
|
|
PPCGArray.size = Array->getElementType()->getPrimitiveSizeInBits() / 8;
|
|
|
|
PPCGArray.name = strdup(Array->getName().c_str());
|
|
|
|
PPCGArray.extent = nullptr;
|
|
|
|
PPCGArray.n_index = Array->getNumberOfDimensions();
|
|
|
|
PPCGArray.bound =
|
|
|
|
isl_alloc_array(S->getIslCtx(), isl_pw_aff *, PPCGArray.n_index);
|
|
|
|
PPCGArray.extent = getExtent(Array);
|
|
|
|
PPCGArray.n_ref = 0;
|
|
|
|
PPCGArray.refs = nullptr;
|
|
|
|
PPCGArray.accessed = true;
|
2016-09-18 03:22:18 +08:00
|
|
|
PPCGArray.read_only_scalar =
|
|
|
|
Array->isReadOnly() && Array->getNumberOfDimensions() == 0;
|
2016-07-15 15:05:54 +08:00
|
|
|
PPCGArray.has_compound_element = false;
|
|
|
|
PPCGArray.local = false;
|
|
|
|
PPCGArray.declare_local = false;
|
|
|
|
PPCGArray.global = false;
|
|
|
|
PPCGArray.linearize = false;
|
|
|
|
PPCGArray.dep_order = nullptr;
|
2016-07-25 20:47:39 +08:00
|
|
|
PPCGArray.user = Array;
|
2016-07-15 15:05:54 +08:00
|
|
|
|
|
|
|
setArrayBounds(PPCGArray, Array);
|
2016-07-15 18:51:14 +08:00
|
|
|
i++;
|
2016-07-18 23:44:32 +08:00
|
|
|
|
|
|
|
collect_references(PPCGProg, &PPCGArray);
|
2016-07-15 15:05:54 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Create an identity map between the arrays in the scop.
|
|
|
|
///
|
|
|
|
/// @returns An identity map between the arrays in the scop.
|
|
|
|
isl_union_map *getArrayIdentity() {
|
|
|
|
isl_union_map *Maps = isl_union_map_empty(S->getParamSpace());
|
|
|
|
|
2016-07-30 17:25:51 +08:00
|
|
|
for (auto &Array : S->arrays()) {
|
2016-07-15 15:05:54 +08:00
|
|
|
isl_space *Space = Array->getSpace();
|
|
|
|
Space = isl_space_map_from_set(Space);
|
|
|
|
isl_map *Identity = isl_map_identity(Space);
|
|
|
|
Maps = isl_union_map_add_map(Maps, Identity);
|
|
|
|
}
|
|
|
|
|
|
|
|
return Maps;
|
|
|
|
}
|
|
|
|
|
2016-07-14 18:22:19 +08:00
|
|
|
/// Create a default-initialized PPCG GPU program.
|
|
|
|
///
|
|
|
|
/// @returns A new gpu grogram description.
|
|
|
|
gpu_prog *createPPCGProg(ppcg_scop *PPCGScop) {
|
|
|
|
|
|
|
|
if (!PPCGScop)
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
auto PPCGProg = isl_calloc_type(S->getIslCtx(), struct gpu_prog);
|
|
|
|
|
|
|
|
PPCGProg->ctx = S->getIslCtx();
|
|
|
|
PPCGProg->scop = PPCGScop;
|
2016-07-14 18:51:52 +08:00
|
|
|
PPCGProg->context = isl_set_copy(PPCGScop->context);
|
2016-07-15 15:05:54 +08:00
|
|
|
PPCGProg->read = isl_union_map_copy(PPCGScop->reads);
|
|
|
|
PPCGProg->may_write = isl_union_map_copy(PPCGScop->may_writes);
|
|
|
|
PPCGProg->must_write = isl_union_map_copy(PPCGScop->must_writes);
|
|
|
|
PPCGProg->tagged_must_kill =
|
|
|
|
isl_union_map_copy(PPCGScop->tagged_must_kills);
|
|
|
|
PPCGProg->to_inner = getArrayIdentity();
|
|
|
|
PPCGProg->to_outer = getArrayIdentity();
|
2016-07-14 18:22:19 +08:00
|
|
|
PPCGProg->any_to_outer = nullptr;
|
|
|
|
PPCGProg->array_order = nullptr;
|
2016-07-14 23:51:37 +08:00
|
|
|
PPCGProg->n_stmts = std::distance(S->begin(), S->end());
|
|
|
|
PPCGProg->stmts = getStatements();
|
2016-07-15 15:05:54 +08:00
|
|
|
PPCGProg->n_array = std::distance(S->array_begin(), S->array_end());
|
|
|
|
PPCGProg->array = isl_calloc_array(S->getIslCtx(), struct gpu_array_info,
|
|
|
|
PPCGProg->n_array);
|
|
|
|
|
|
|
|
createArrays(PPCGProg);
|
2016-07-14 18:22:19 +08:00
|
|
|
|
2016-08-10 18:58:19 +08:00
|
|
|
PPCGProg->may_persist = compute_may_persist(PPCGProg);
|
|
|
|
|
2016-07-14 18:22:19 +08:00
|
|
|
return PPCGProg;
|
|
|
|
}
|
|
|
|
|
2016-07-14 23:51:37 +08:00
|
|
|
struct PrintGPUUserData {
|
|
|
|
struct cuda_info *CudaInfo;
|
|
|
|
struct gpu_prog *PPCGProg;
|
|
|
|
std::vector<ppcg_kernel *> Kernels;
|
|
|
|
};
|
|
|
|
|
|
|
|
/// Print a user statement node in the host code.
|
|
|
|
///
|
|
|
|
/// We use ppcg's printing facilities to print the actual statement and
|
|
|
|
/// additionally build up a list of all kernels that are encountered in the
|
|
|
|
/// host ast.
|
|
|
|
///
|
|
|
|
/// @param P The printer to print to
|
|
|
|
/// @param Options The printing options to use
|
|
|
|
/// @param Node The node to print
|
|
|
|
/// @param User A user pointer to carry additional data. This pointer is
|
|
|
|
/// expected to be of type PrintGPUUserData.
|
|
|
|
///
|
|
|
|
/// @returns A printer to which the output has been printed.
|
|
|
|
static __isl_give isl_printer *
|
|
|
|
printHostUser(__isl_take isl_printer *P,
|
|
|
|
__isl_take isl_ast_print_options *Options,
|
|
|
|
__isl_take isl_ast_node *Node, void *User) {
|
|
|
|
auto Data = (struct PrintGPUUserData *)User;
|
|
|
|
auto Id = isl_ast_node_get_annotation(Node);
|
|
|
|
|
|
|
|
if (Id) {
|
2016-07-16 01:12:41 +08:00
|
|
|
bool IsUser = !strcmp(isl_id_get_name(Id), "user");
|
|
|
|
|
|
|
|
// If this is a user statement, format it ourselves as ppcg would
|
|
|
|
// otherwise try to call pet functionality that is not available in
|
|
|
|
// Polly.
|
|
|
|
if (IsUser) {
|
|
|
|
P = isl_printer_start_line(P);
|
|
|
|
P = isl_printer_print_ast_node(P, Node);
|
|
|
|
P = isl_printer_end_line(P);
|
|
|
|
isl_id_free(Id);
|
|
|
|
isl_ast_print_options_free(Options);
|
|
|
|
return P;
|
|
|
|
}
|
|
|
|
|
2016-07-14 23:51:37 +08:00
|
|
|
auto Kernel = (struct ppcg_kernel *)isl_id_get_user(Id);
|
|
|
|
isl_id_free(Id);
|
|
|
|
Data->Kernels.push_back(Kernel);
|
|
|
|
}
|
|
|
|
|
|
|
|
return print_host_user(P, Options, Node, User);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Print C code corresponding to the control flow in @p Kernel.
|
|
|
|
///
|
|
|
|
/// @param Kernel The kernel to print
|
|
|
|
void printKernel(ppcg_kernel *Kernel) {
|
|
|
|
auto *P = isl_printer_to_str(S->getIslCtx());
|
|
|
|
P = isl_printer_set_output_format(P, ISL_FORMAT_C);
|
|
|
|
auto *Options = isl_ast_print_options_alloc(S->getIslCtx());
|
|
|
|
P = isl_ast_node_print(Kernel->tree, P, Options);
|
|
|
|
char *String = isl_printer_get_str(P);
|
|
|
|
printf("%s\n", String);
|
|
|
|
free(String);
|
|
|
|
isl_printer_free(P);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Print C code corresponding to the GPU code described by @p Tree.
|
|
|
|
///
|
|
|
|
/// @param Tree An AST describing GPU code
|
|
|
|
/// @param PPCGProg The PPCG program from which @Tree has been constructed.
|
|
|
|
void printGPUTree(isl_ast_node *Tree, gpu_prog *PPCGProg) {
|
|
|
|
auto *P = isl_printer_to_str(S->getIslCtx());
|
|
|
|
P = isl_printer_set_output_format(P, ISL_FORMAT_C);
|
|
|
|
|
|
|
|
PrintGPUUserData Data;
|
|
|
|
Data.PPCGProg = PPCGProg;
|
|
|
|
|
|
|
|
auto *Options = isl_ast_print_options_alloc(S->getIslCtx());
|
|
|
|
Options =
|
|
|
|
isl_ast_print_options_set_print_user(Options, printHostUser, &Data);
|
|
|
|
P = isl_ast_node_print(Tree, P, Options);
|
|
|
|
char *String = isl_printer_get_str(P);
|
|
|
|
printf("# host\n");
|
|
|
|
printf("%s\n", String);
|
|
|
|
free(String);
|
|
|
|
isl_printer_free(P);
|
|
|
|
|
|
|
|
for (auto Kernel : Data.Kernels) {
|
|
|
|
printf("# kernel%d\n", Kernel->id);
|
|
|
|
printKernel(Kernel);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-14 18:22:25 +08:00
|
|
|
// Generate a GPU program using PPCG.
|
|
|
|
//
|
|
|
|
// GPU mapping consists of multiple steps:
|
|
|
|
//
|
|
|
|
// 1) Compute new schedule for the program.
|
|
|
|
// 2) Map schedule to GPU (TODO)
|
|
|
|
// 3) Generate code for new schedule (TODO)
|
|
|
|
//
|
|
|
|
// We do not use here the Polly ScheduleOptimizer, as the schedule optimizer
|
|
|
|
// is mostly CPU specific. Instead, we use PPCG's GPU code generation
|
|
|
|
// strategy directly from this pass.
|
|
|
|
gpu_gen *generateGPU(ppcg_scop *PPCGScop, gpu_prog *PPCGProg) {
|
|
|
|
|
|
|
|
auto PPCGGen = isl_calloc_type(S->getIslCtx(), struct gpu_gen);
|
|
|
|
|
|
|
|
PPCGGen->ctx = S->getIslCtx();
|
|
|
|
PPCGGen->options = PPCGScop->options;
|
|
|
|
PPCGGen->print = nullptr;
|
|
|
|
PPCGGen->print_user = nullptr;
|
2016-07-14 23:51:32 +08:00
|
|
|
PPCGGen->build_ast_expr = &pollyBuildAstExprForStmt;
|
2016-07-14 18:22:25 +08:00
|
|
|
PPCGGen->prog = PPCGProg;
|
|
|
|
PPCGGen->tree = nullptr;
|
|
|
|
PPCGGen->types.n = 0;
|
|
|
|
PPCGGen->types.name = nullptr;
|
|
|
|
PPCGGen->sizes = nullptr;
|
|
|
|
PPCGGen->used_sizes = nullptr;
|
|
|
|
PPCGGen->kernel_id = 0;
|
|
|
|
|
|
|
|
// Set scheduling strategy to same strategy PPCG is using.
|
|
|
|
isl_options_set_schedule_outer_coincidence(PPCGGen->ctx, true);
|
|
|
|
isl_options_set_schedule_maximize_band_depth(PPCGGen->ctx, true);
|
2016-07-16 00:15:47 +08:00
|
|
|
isl_options_set_schedule_whole_component(PPCGGen->ctx, false);
|
2016-07-14 18:22:25 +08:00
|
|
|
|
|
|
|
isl_schedule *Schedule = get_schedule(PPCGGen);
|
|
|
|
|
2016-07-14 18:51:52 +08:00
|
|
|
int has_permutable = has_any_permutable_node(Schedule);
|
|
|
|
|
2016-07-14 23:51:37 +08:00
|
|
|
if (!has_permutable || has_permutable < 0) {
|
2016-07-14 18:51:52 +08:00
|
|
|
Schedule = isl_schedule_free(Schedule);
|
2016-07-14 23:51:37 +08:00
|
|
|
} else {
|
2016-07-14 18:51:52 +08:00
|
|
|
Schedule = map_to_device(PPCGGen, Schedule);
|
2016-07-14 23:51:37 +08:00
|
|
|
PPCGGen->tree = generate_code(PPCGGen, isl_schedule_copy(Schedule));
|
|
|
|
}
|
2016-07-14 18:51:52 +08:00
|
|
|
|
2016-07-14 18:22:25 +08:00
|
|
|
if (DumpSchedule) {
|
|
|
|
isl_printer *P = isl_printer_to_str(S->getIslCtx());
|
|
|
|
P = isl_printer_set_yaml_style(P, ISL_YAML_STYLE_BLOCK);
|
|
|
|
P = isl_printer_print_str(P, "Schedule\n");
|
|
|
|
P = isl_printer_print_str(P, "========\n");
|
|
|
|
if (Schedule)
|
|
|
|
P = isl_printer_print_schedule(P, Schedule);
|
|
|
|
else
|
|
|
|
P = isl_printer_print_str(P, "No schedule found\n");
|
|
|
|
|
|
|
|
printf("%s\n", isl_printer_get_str(P));
|
|
|
|
isl_printer_free(P);
|
|
|
|
}
|
|
|
|
|
2016-07-14 23:51:37 +08:00
|
|
|
if (DumpCode) {
|
|
|
|
printf("Code\n");
|
|
|
|
printf("====\n");
|
|
|
|
if (PPCGGen->tree)
|
|
|
|
printGPUTree(PPCGGen->tree, PPCGProg);
|
|
|
|
else
|
|
|
|
printf("No code generated\n");
|
|
|
|
}
|
|
|
|
|
2016-07-14 18:22:25 +08:00
|
|
|
isl_schedule_free(Schedule);
|
|
|
|
|
|
|
|
return PPCGGen;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Free gpu_gen structure.
|
|
|
|
///
|
|
|
|
/// @param PPCGGen The ppcg_gen object to free.
|
|
|
|
void freePPCGGen(gpu_gen *PPCGGen) {
|
|
|
|
isl_ast_node_free(PPCGGen->tree);
|
|
|
|
isl_union_map_free(PPCGGen->sizes);
|
|
|
|
isl_union_map_free(PPCGGen->used_sizes);
|
|
|
|
free(PPCGGen);
|
|
|
|
}
|
|
|
|
|
2016-07-15 18:32:22 +08:00
|
|
|
/// Free the options in the ppcg scop structure.
|
|
|
|
///
|
|
|
|
/// ppcg is not freeing these options for us. To avoid leaks we do this
|
|
|
|
/// ourselves.
|
|
|
|
///
|
|
|
|
/// @param PPCGScop The scop referencing the options to free.
|
|
|
|
void freeOptions(ppcg_scop *PPCGScop) {
|
|
|
|
free(PPCGScop->options->debug);
|
|
|
|
PPCGScop->options->debug = nullptr;
|
|
|
|
free(PPCGScop->options);
|
|
|
|
PPCGScop->options = nullptr;
|
|
|
|
}
|
|
|
|
|
2016-09-18 14:50:35 +08:00
|
|
|
/// Approximate the number of points in the set.
|
|
|
|
///
|
|
|
|
/// This function returns an ast expression that overapproximates the number
|
|
|
|
/// of points in an isl set through the rectangular hull surrounding this set.
|
|
|
|
///
|
|
|
|
/// @param Set The set to count.
|
|
|
|
/// @param Build The isl ast build object to use for creating the ast
|
|
|
|
/// expression.
|
|
|
|
///
|
|
|
|
/// @returns An approximation of the number of points in the set.
|
|
|
|
__isl_give isl_ast_expr *approxPointsInSet(__isl_take isl_set *Set,
|
|
|
|
__isl_keep isl_ast_build *Build) {
|
|
|
|
|
|
|
|
isl_val *One = isl_val_int_from_si(isl_set_get_ctx(Set), 1);
|
|
|
|
auto *Expr = isl_ast_expr_from_val(isl_val_copy(One));
|
|
|
|
|
|
|
|
isl_space *Space = isl_set_get_space(Set);
|
|
|
|
Space = isl_space_params(Space);
|
|
|
|
auto *Univ = isl_set_universe(Space);
|
|
|
|
isl_pw_aff *OneAff = isl_pw_aff_val_on_domain(Univ, One);
|
|
|
|
|
|
|
|
for (long i = 0; i < isl_set_dim(Set, isl_dim_set); i++) {
|
|
|
|
isl_pw_aff *Max = isl_set_dim_max(isl_set_copy(Set), i);
|
|
|
|
isl_pw_aff *Min = isl_set_dim_min(isl_set_copy(Set), i);
|
|
|
|
isl_pw_aff *DimSize = isl_pw_aff_sub(Max, Min);
|
|
|
|
DimSize = isl_pw_aff_add(DimSize, isl_pw_aff_copy(OneAff));
|
|
|
|
auto DimSizeExpr = isl_ast_build_expr_from_pw_aff(Build, DimSize);
|
|
|
|
Expr = isl_ast_expr_mul(Expr, DimSizeExpr);
|
|
|
|
}
|
|
|
|
|
|
|
|
isl_set_free(Set);
|
|
|
|
isl_pw_aff_free(OneAff);
|
|
|
|
|
|
|
|
return Expr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Approximate a number of dynamic instructions executed by a given
|
|
|
|
/// statement.
|
|
|
|
///
|
|
|
|
/// @param Stmt The statement for which to compute the number of dynamic
|
|
|
|
/// instructions.
|
|
|
|
/// @param Build The isl ast build object to use for creating the ast
|
|
|
|
/// expression.
|
|
|
|
/// @returns An approximation of the number of dynamic instructions executed
|
|
|
|
/// by @p Stmt.
|
|
|
|
__isl_give isl_ast_expr *approxDynamicInst(ScopStmt &Stmt,
|
|
|
|
__isl_keep isl_ast_build *Build) {
|
|
|
|
auto Iterations = approxPointsInSet(Stmt.getDomain(), Build);
|
|
|
|
|
|
|
|
long InstCount = 0;
|
|
|
|
|
|
|
|
if (Stmt.isBlockStmt()) {
|
|
|
|
auto *BB = Stmt.getBasicBlock();
|
|
|
|
InstCount = std::distance(BB->begin(), BB->end());
|
|
|
|
} else {
|
|
|
|
auto *R = Stmt.getRegion();
|
|
|
|
|
|
|
|
for (auto *BB : R->blocks()) {
|
|
|
|
InstCount += std::distance(BB->begin(), BB->end());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
isl_val *InstVal = isl_val_int_from_si(S->getIslCtx(), InstCount);
|
|
|
|
auto *InstExpr = isl_ast_expr_from_val(InstVal);
|
|
|
|
return isl_ast_expr_mul(InstExpr, Iterations);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Approximate dynamic instructions executed in scop.
|
|
|
|
///
|
|
|
|
/// @param S The scop for which to approximate dynamic instructions.
|
|
|
|
/// @param Build The isl ast build object to use for creating the ast
|
|
|
|
/// expression.
|
|
|
|
/// @returns An approximation of the number of dynamic instructions executed
|
|
|
|
/// in @p S.
|
|
|
|
__isl_give isl_ast_expr *
|
|
|
|
getNumberOfIterations(Scop &S, __isl_keep isl_ast_build *Build) {
|
|
|
|
isl_ast_expr *Instructions;
|
|
|
|
|
|
|
|
isl_val *Zero = isl_val_int_from_si(S.getIslCtx(), 0);
|
|
|
|
Instructions = isl_ast_expr_from_val(Zero);
|
|
|
|
|
|
|
|
for (ScopStmt &Stmt : S) {
|
|
|
|
isl_ast_expr *StmtInstructions = approxDynamicInst(Stmt, Build);
|
|
|
|
Instructions = isl_ast_expr_add(Instructions, StmtInstructions);
|
|
|
|
}
|
|
|
|
return Instructions;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Create a check that ensures sufficient compute in scop.
|
|
|
|
///
|
|
|
|
/// @param S The scop for which to ensure sufficient compute.
|
|
|
|
/// @param Build The isl ast build object to use for creating the ast
|
|
|
|
/// expression.
|
|
|
|
/// @returns An expression that evaluates to TRUE in case of sufficient
|
|
|
|
/// compute and to FALSE, otherwise.
|
|
|
|
__isl_give isl_ast_expr *
|
|
|
|
createSufficientComputeCheck(Scop &S, __isl_keep isl_ast_build *Build) {
|
|
|
|
auto Iterations = getNumberOfIterations(S, Build);
|
|
|
|
auto *MinComputeVal = isl_val_int_from_si(S.getIslCtx(), MinCompute);
|
|
|
|
auto *MinComputeExpr = isl_ast_expr_from_val(MinComputeVal);
|
|
|
|
return isl_ast_expr_ge(Iterations, MinComputeExpr);
|
|
|
|
}
|
|
|
|
|
2016-07-18 19:56:39 +08:00
|
|
|
/// Generate code for a given GPU AST described by @p Root.
|
|
|
|
///
|
2016-07-19 15:32:38 +08:00
|
|
|
/// @param Root An isl_ast_node pointing to the root of the GPU AST.
|
|
|
|
/// @param Prog The GPU Program to generate code for.
|
|
|
|
void generateCode(__isl_take isl_ast_node *Root, gpu_prog *Prog) {
|
2016-07-18 19:56:39 +08:00
|
|
|
ScopAnnotator Annotator;
|
|
|
|
Annotator.buildAliasScopes(*S);
|
|
|
|
|
|
|
|
Region *R = &S->getRegion();
|
|
|
|
|
|
|
|
simplifyRegion(R, DT, LI, RI);
|
|
|
|
|
|
|
|
BasicBlock *EnteringBB = R->getEnteringBlock();
|
|
|
|
|
|
|
|
PollyIRBuilder Builder = createPollyIRBuilder(EnteringBB, Annotator);
|
|
|
|
|
|
|
|
// Only build the run-time condition and parameters _after_ having
|
|
|
|
// introduced the conditional branch. This is important as the conditional
|
|
|
|
// branch will guard the original scop from new induction variables that
|
|
|
|
// the SCEVExpander may introduce while code generating the parameters and
|
|
|
|
// which may introduce scalar dependences that prevent us from correctly
|
|
|
|
// code generating this scop.
|
|
|
|
BasicBlock *StartBlock =
|
2017-04-04 18:01:53 +08:00
|
|
|
executeScopConditionally(*S, Builder.getTrue(), *DT, *RI, *LI);
|
2016-07-18 19:56:39 +08:00
|
|
|
|
2017-04-04 18:01:53 +08:00
|
|
|
GPUNodeBuilder NodeBuilder(Builder, Annotator, *DL, *LI, *SE, *DT, *S,
|
[Polly] Added OpenCL Runtime to GPURuntime Library for GPGPU CodeGen
Summary:
When compiling for GPU, one can now choose to compile for OpenCL or CUDA,
with the corresponding polly-gpu-runtime flag (libopencl / libcudart). The
GPURuntime library (GPUJIT) has been extended with the OpenCL Runtime library
for that purpose, correctly choosing the corresponding library calls to the
option chosen when compiling (via different initialization calls).
Additionally, a specific GPU Target architecture can now be chosen with -polly-gpu-arch (only nvptx64 implemented thus far).
Reviewers: grosser, bollu, Meinersbur, etherzhhb, singam-sanjay
Reviewed By: grosser, Meinersbur
Subscribers: singam-sanjay, llvm-commits, pollydev, nemanjai, mgorny, yaxunl, Anastasia
Tags: #polly
Differential Revision: https://reviews.llvm.org/D32431
llvm-svn: 302379
2017-05-08 05:03:46 +08:00
|
|
|
StartBlock, Prog, Runtime, Architecture);
|
2016-11-03 06:32:23 +08:00
|
|
|
|
2016-07-18 19:56:39 +08:00
|
|
|
// TODO: Handle LICM
|
|
|
|
auto SplitBlock = StartBlock->getSinglePredecessor();
|
|
|
|
Builder.SetInsertPoint(SplitBlock->getTerminator());
|
|
|
|
NodeBuilder.addParameters(S->getContext());
|
2016-08-09 01:35:55 +08:00
|
|
|
|
|
|
|
isl_ast_build *Build = isl_ast_build_alloc(S->getIslCtx());
|
|
|
|
isl_ast_expr *Condition = IslAst::buildRunCondition(S, Build);
|
2016-09-18 14:50:35 +08:00
|
|
|
isl_ast_expr *SufficientCompute = createSufficientComputeCheck(*S, Build);
|
|
|
|
Condition = isl_ast_expr_and(Condition, SufficientCompute);
|
2016-08-09 01:35:55 +08:00
|
|
|
isl_ast_build_free(Build);
|
|
|
|
|
|
|
|
Value *RTC = NodeBuilder.createRTC(Condition);
|
|
|
|
Builder.GetInsertBlock()->getTerminator()->setOperand(0, RTC);
|
|
|
|
|
2016-07-18 19:56:39 +08:00
|
|
|
Builder.SetInsertPoint(&*StartBlock->begin());
|
2016-07-25 17:16:01 +08:00
|
|
|
|
|
|
|
NodeBuilder.initializeAfterRTH();
|
2016-07-18 19:56:39 +08:00
|
|
|
NodeBuilder.create(Root);
|
2016-07-25 17:15:57 +08:00
|
|
|
NodeBuilder.finalize();
|
2016-09-12 14:06:31 +08:00
|
|
|
|
2016-09-18 16:31:09 +08:00
|
|
|
/// In case a sequential kernel has more surrounding loops as any parallel
|
|
|
|
/// kernel, the SCoP is probably mostly sequential. Hence, there is no
|
2017-03-12 16:19:01 +08:00
|
|
|
/// point in running it on a GPU.
|
2016-09-18 16:31:09 +08:00
|
|
|
if (NodeBuilder.DeepestSequential > NodeBuilder.DeepestParallel)
|
|
|
|
SplitBlock->getTerminator()->setOperand(0, Builder.getFalse());
|
|
|
|
|
2016-09-12 14:06:31 +08:00
|
|
|
if (!NodeBuilder.BuildSuccessful)
|
|
|
|
SplitBlock->getTerminator()->setOperand(0, Builder.getFalse());
|
2016-07-18 19:56:39 +08:00
|
|
|
}
|
|
|
|
|
2016-07-14 18:22:19 +08:00
|
|
|
bool runOnScop(Scop &CurrentScop) override {
|
|
|
|
S = &CurrentScop;
|
2016-07-18 19:56:39 +08:00
|
|
|
LI = &getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
|
|
|
|
DT = &getAnalysis<DominatorTreeWrapperPass>().getDomTree();
|
|
|
|
SE = &getAnalysis<ScalarEvolutionWrapperPass>().getSE();
|
2017-04-11 12:59:13 +08:00
|
|
|
DL = &S->getRegion().getEntry()->getModule()->getDataLayout();
|
2016-07-18 19:56:39 +08:00
|
|
|
RI = &getAnalysis<RegionInfoPass>().getRegionInfo();
|
2016-07-14 18:22:19 +08:00
|
|
|
|
2016-07-19 23:56:25 +08:00
|
|
|
// We currently do not support scops with invariant loads.
|
|
|
|
if (S->hasInvariantAccesses())
|
|
|
|
return false;
|
|
|
|
|
2016-07-14 18:22:19 +08:00
|
|
|
auto PPCGScop = createPPCGScop();
|
|
|
|
auto PPCGProg = createPPCGProg(PPCGScop);
|
2016-07-14 18:22:25 +08:00
|
|
|
auto PPCGGen = generateGPU(PPCGScop, PPCGProg);
|
2016-07-18 19:56:39 +08:00
|
|
|
|
|
|
|
if (PPCGGen->tree)
|
2016-07-19 15:32:38 +08:00
|
|
|
generateCode(isl_ast_node_copy(PPCGGen->tree), PPCGProg);
|
2016-07-18 19:56:39 +08:00
|
|
|
|
2016-07-15 18:32:22 +08:00
|
|
|
freeOptions(PPCGScop);
|
2016-07-14 18:22:25 +08:00
|
|
|
freePPCGGen(PPCGGen);
|
2016-07-14 18:22:19 +08:00
|
|
|
gpu_prog_free(PPCGProg);
|
|
|
|
ppcg_scop_free(PPCGScop);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2016-07-13 23:54:58 +08:00
|
|
|
|
|
|
|
void printScop(raw_ostream &, Scop &) const override {}
|
|
|
|
|
|
|
|
void getAnalysisUsage(AnalysisUsage &AU) const override {
|
|
|
|
AU.addRequired<DominatorTreeWrapperPass>();
|
|
|
|
AU.addRequired<RegionInfoPass>();
|
|
|
|
AU.addRequired<ScalarEvolutionWrapperPass>();
|
|
|
|
AU.addRequired<ScopDetection>();
|
|
|
|
AU.addRequired<ScopInfoRegionPass>();
|
|
|
|
AU.addRequired<LoopInfoWrapperPass>();
|
|
|
|
|
|
|
|
AU.addPreserved<AAResultsWrapperPass>();
|
|
|
|
AU.addPreserved<BasicAAWrapperPass>();
|
|
|
|
AU.addPreserved<LoopInfoWrapperPass>();
|
|
|
|
AU.addPreserved<DominatorTreeWrapperPass>();
|
|
|
|
AU.addPreserved<GlobalsAAWrapperPass>();
|
|
|
|
AU.addPreserved<ScopDetection>();
|
|
|
|
AU.addPreserved<ScalarEvolutionWrapperPass>();
|
|
|
|
AU.addPreserved<SCEVAAWrapperPass>();
|
|
|
|
|
|
|
|
// FIXME: We do not yet add regions for the newly generated code to the
|
|
|
|
// region tree.
|
|
|
|
AU.addPreserved<RegionInfoPass>();
|
|
|
|
AU.addPreserved<ScopInfoRegionPass>();
|
|
|
|
}
|
|
|
|
};
|
2017-03-01 23:54:27 +08:00
|
|
|
} // namespace
|
2016-07-13 23:54:58 +08:00
|
|
|
|
|
|
|
char PPCGCodeGeneration::ID = 1;
|
|
|
|
|
[Polly] Added OpenCL Runtime to GPURuntime Library for GPGPU CodeGen
Summary:
When compiling for GPU, one can now choose to compile for OpenCL or CUDA,
with the corresponding polly-gpu-runtime flag (libopencl / libcudart). The
GPURuntime library (GPUJIT) has been extended with the OpenCL Runtime library
for that purpose, correctly choosing the corresponding library calls to the
option chosen when compiling (via different initialization calls).
Additionally, a specific GPU Target architecture can now be chosen with -polly-gpu-arch (only nvptx64 implemented thus far).
Reviewers: grosser, bollu, Meinersbur, etherzhhb, singam-sanjay
Reviewed By: grosser, Meinersbur
Subscribers: singam-sanjay, llvm-commits, pollydev, nemanjai, mgorny, yaxunl, Anastasia
Tags: #polly
Differential Revision: https://reviews.llvm.org/D32431
llvm-svn: 302379
2017-05-08 05:03:46 +08:00
|
|
|
Pass *polly::createPPCGCodeGenerationPass(GPUArch Arch, GPURuntime Runtime) {
|
|
|
|
PPCGCodeGeneration *generator = new PPCGCodeGeneration();
|
|
|
|
generator->Runtime = Runtime;
|
|
|
|
generator->Architecture = Arch;
|
|
|
|
return generator;
|
|
|
|
}
|
2016-07-13 23:54:58 +08:00
|
|
|
|
|
|
|
INITIALIZE_PASS_BEGIN(PPCGCodeGeneration, "polly-codegen-ppcg",
|
|
|
|
"Polly - Apply PPCG translation to SCOP", false, false)
|
|
|
|
INITIALIZE_PASS_DEPENDENCY(DependenceInfo);
|
|
|
|
INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass);
|
|
|
|
INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass);
|
|
|
|
INITIALIZE_PASS_DEPENDENCY(RegionInfoPass);
|
|
|
|
INITIALIZE_PASS_DEPENDENCY(ScalarEvolutionWrapperPass);
|
|
|
|
INITIALIZE_PASS_DEPENDENCY(ScopDetection);
|
|
|
|
INITIALIZE_PASS_END(PPCGCodeGeneration, "polly-codegen-ppcg",
|
|
|
|
"Polly - Apply PPCG translation to SCOP", false, false)
|