blob: 3e48ad922bbbf79ee4ca1d631bd92214e6de3f7c [file] [log] [blame]
// Copyright 2017 The Clspv Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <string>
#include <utility>
#include <vector>
namespace llvm {
// This is against Google C++ style guide.
class FunctionPass;
class ModulePass;
class raw_pwrite_stream;
class raw_ostream;
template <typename T> class ArrayRef;
// This initializes all the clspv passes. When creating a new pass, you should:
//
// 1- Declare the pass using INITIALIZE_PASS.
// 2- Declare the function initialize<pass_name>Pass() in lib/Passes.h.
// 3- Add a call to the initialize<pass_name>Pass() to initializeClspvPasses().
// 4- Your pass must include the file lib/Passes.h.
class PassRegistry;
void initializeClspvPasses(PassRegistry &);
} // namespace llvm
namespace clspv {
/// Process long vectors into an equivalent representation that can be mapped to
/// Vulkan SPIR-V.
llvm::ModulePass *createLongVectorLoweringPass();
/// Declare a structure with all enabled push constants and attach metadata to
/// the module for use by the utilities that abstract push constant usage.
/// @return An LLVM module pass.
llvm::ModulePass *createDeclarePushConstantsPass();
/// Define the work-item builtins that OpenCL has.
/// @return An LLVM module pass.
///
/// These OpenCL work-item builtins are in this pass mapped to LLVM module-scope
/// global variables, such that:
/// - get_global_id() is mapped to a variable named
/// "__spirv_GlobalInvocationId".
/// When support for the global offset is enabled, the global offset is added
/// to "__spirv_GlobalInvocationId".
/// - get_local_size() is mapped to a variable named "__spirv_WorkgroupSize"
/// - get_local_id() is mapped to a variable named "__spirv_LocalInvocationId"
/// - get_num_groups() is mapped to a variable named "__spirv_NumWorkgroups"
/// - get_group_id() is mapped to a variable named "__spirv_WorkgroupId"
///
/// These OpenCL work-item builtins are also defined:
/// - get_work_dim() returns either always 3 or a value passed as a spec
/// constant
/// - get_global_offset() returns either always 0 or a value passed as a push
/// constant
/// - get_global_size() is implemented by multiplying the result from
/// get_local_size() by the result from get_num_groups()
///
/// If we could add a SPIR-V target to Clang in future, we could define these as
/// SPIR-V builtins in Clang that map to SPIR-V intrinsics in LLVM.
llvm::ModulePass *createDefineOpenCLWorkItemBuiltinsPass();
/// Internalize non-kernel OpenCL functions.
/// @return An LLVM module pass.
///
/// Any function that isn't specified as being a kernel will be marked as
/// internal linkage by this pass, so that DCE can remove unreferenced functions
/// if it wants to during optimizations later.
///
/// If we could change how Clang defines non-kernel OpenCL functions such that
/// the correct LLVM linkage was chosen for a final linkage of the module, we
/// might not need this pass. We'd have to be careful to preserve the symbols
/// if we were creating an OpenCL library though.
llvm::ModulePass *createFunctionInternalizerPass();
/// Inline call instructions which have pointer bitcast as arguments.
/// @return An LLVM module pass.
///
/// If function has pointer type arguments and call instruction passes pointer
/// bitcast as argument, we need to prevent it. In order to do it, we inline
/// call instructions which have pointer bitcast as argument.
llvm::ModulePass *createInlineFuncWithPointerBitCastArgPass();
/// Inline call instructions to functions having pointer-to-private args.
/// @return An LLVM module pass.
///
/// Try to avoid OpPtrAccessChain to Function storage class by inlining
/// function calls that lead to getelementptr on private storage, but which
/// the compiler will not normally optimize away.
llvm::ModulePass *createInlineFuncWithPointerToFunctionArgPass();
/// Inline call instructions to functions having a single call site.
/// @return An LLVM module pass.
///
/// Inline functions that have a single call site.
llvm::ModulePass *createInlineFuncWithSingleCallSitePass();
/// Zero-initialize allocas.
/// @return An LLVM module pass.
///
/// Store a zero value to any alloca. This helps us avoid ConstantExpr
/// in the output. The SPIR-V producer does not know how to emit code
/// for ConstantExpr. This is a bit of a hack.
llvm::ModulePass *createZeroInitializeAllocasPass();
/// Rewrite constant expressions as instructions.
/// @return An LLVM module pass.
llvm::ModulePass *createRewriteConstantExpressionsPass();
/// Create a simple OpenCL inliner.
/// @return An LLVM module pass.
///
/// We create a simple inliner pass that'll fold away some of the OpenCL
/// builtins where appropriate.
llvm::ModulePass *createOpenCLInlinerPass();
/// Create a re-order basic blocks pass.
/// @return An LLVM module pass.
///
/// We create a pass that will re-order basic blocks to match the SPIR-V rules
/// for structured control flow.
///
/// We could remove this class if we could change the LLVM structurizer to
/// output basic blocks in the order that is suitable for SPIR-V.
llvm::FunctionPass *createReorderBasicBlocksPass();
/// Create a pass to replace some LLVM intrinsics.
/// @return An LLVM module pass.
///
/// We create an LLVM intrinsic replacement pass to turn some LLVM intrinsics
/// into SPIR-V ones. Specifically memset and memcpy both do pointer bitcasts
/// which we can't support.
llvm::ModulePass *createReplaceLLVMIntrinsicsPass();
/// Create a pass to replace some OpenCL builtins.
/// @return An LLVM module pass.
///
/// We create an OpenCL builtin folder pass that'll remove some builtins where
/// possible.
llvm::ModulePass *createReplaceOpenCLBuiltinPass();
/// Create a pass to emit SPIR-V for the module.
/// @return An LLVM module pass.
llvm::ModulePass *createSPIRVProducerPass(
llvm::raw_pwrite_stream &out,
llvm::ArrayRef<std::pair<unsigned, std::string>> samplerMap,
bool outputCInitList);
/// Undo LLVM's bitcast instructions with pointer type.
/// @return An LLVM module pass.
///
/// LLVM could genrate bitcast instruction with pointer type but SPIR-V for
/// Vulkan can't handle it, so this pass converts the bitcast instruction with
/// pointer type into other instructions' sequence.
llvm::ModulePass *createReplacePointerBitcastPass();
/// Simplify LLVM's bitcast instructions with pointer type.
/// @return An LLVM module pass.
///
/// This pass will do the following transformations:
/// - Transform GEP -> bitcast into bitcast -> GEP.
/// - Combine bitcasts.
/// - Combine GEPs.
///
/// The purpose is to flatten arbitrarily used bitcasts & GEPs on pointers such
/// that there will be at most one bitcast, with at most one GEP, before each
/// load or store operation. This drastically improves the ability to generate
/// valid SPIR-V for Vulkan (which cannot handle pointer bitcasts at all).
llvm::ModulePass *createSimplifyPointerBitcastPass();
/// Splat scalar type arguments to vector type.
/// @return An LLVM module pass.
///
/// min/max/mix/clamp function have function prototype with scalar type on
/// OpenCL. But vulkan does not support it. In order to fit it, the pass
/// promotes scalar arument to vector argument.
llvm::ModulePass *createSplatArgPass();
/// Undo Clang's use of an i8 to form a bool type.
/// @return An LLVM module pass.
///
/// Clang uses i8 to represent a bool as it is stored, but for SPIR-V we want
/// the bool to remain as an i1. This pass will undo what Clang has done.
///
/// If we could add a SPIR-V target to Clang in future, we could do away with
/// this pass and simply instruct Clang that bool's as stored are i1.
llvm::ModulePass *createUndoBoolPass();
/// Undo Clang's use of a pointer parameter when dealing with a struct passed as
/// function parameter.
/// @return An LLVM module pass.
///
/// Clang will return a function like:
/// struct Foo { int x; };
/// void func(struct Foo a);
///
/// By essentially changing the function signature to:
/// struct Foo { int x; };
/// void func(struct Foo *a with byval attribute);
///
/// Since SPIR-V can happily accept struct parameters from functions, and given
/// we want to keep the produced SPIR-V as close to the source language as
/// possible, we use this pass to undo Clang's work.
///
/// If we could add a SPIR-V target to Clang in future, we could do away with
/// this pass and simply instruct Clang to pass parameters with the struct as
/// normal.
llvm::ModulePass *createUndoByvalPass();
/// Undo LLVM's use of get element pointer constant expressions on global
/// variables.
/// @return An LLVM module pass.
///
/// LLVM will generate get element pointer constant expressions when we have a
/// constant index into a global variable. SPIR-V for Vulkan can't represent
/// this, so this pass will turn them back into regular instructions.
llvm::ModulePass *createUndoGetElementPtrConstantExprPass();
/// Undo Clang's use of a pointer parameter when dealing with a struct returned
/// from a function.
/// @return An LLVM module pass.
///
/// Clang will return a function like:
/// struct Foo { int x; };
/// struct Foo func(int a);
///
/// By essentially changing the function signature to:
/// struct Foo { int x; };
/// void func(struct Foo *return, int a);
///
/// Since SPIR-V can happily accept struct returns from functions, and given
/// that we want to keep the produced SPIR-V as close to the source language as
/// possible, we use this pass to undo Clang's work.
///
/// If we could add a SPIR-V target to Clang in future, we could do away with
/// this pass and simply instruct Clang to return the struct as normal.
llvm::ModulePass *createUndoSRetPass();
/// Undo LLVM optimizing calls to __translate_sampler_initializer.
/// @return An LLVM module pass.
///
/// LLVM will optimize two calls to __translate_sampler_initializer into a
/// single call to the function, with a select as the argument. This is really
/// bad for us when we are trying to statically find out the number of samplers
/// we are using, and mapping them to the sampler map. This pass will undo this
/// optimization.
///
/// If we could add a function attribute to LLVM to stop it ever doing any
/// optimizations on a function that would help here.
llvm::ModulePass *createUndoTranslateSamplerFoldPass();
/// Undo LLVM optimizing a mask to a small integer size.
/// @return An LLVM module pass.
///
/// LLVM will optimize masks to smaller integers that get used in switches and
/// comparisons. These integer sizes may be invalid types. LLVM backends can
/// handle this, but since this backend does not conform to the normal process,
/// we undo this optimization here instead.
///
/// If we could add a flag to our LLVM target to indiciate "please do not create
/// malformed types" that would make this pass redundant.
llvm::ModulePass *createUndoTruncateToOddIntegerPass();
/// Cluster module-scope __constant variables.
/// @return An LLVM module pass.
///
/// Replace all live module-scope __constant variables by a single such variable
/// of struct type. Assumes none of the constants contain pointers. Eliminates
/// any dead module-scope __constant variables.
llvm::ModulePass *createClusterModuleScopeConstantVars();
/// Cluster plain-old-data kernel arguments into a single struct argument.
/// @return An LLVM module pass.
///
/// The idea is to save on the number of StorageBuffer (SSBO) arguments, as that
/// is a very limited resource. Vulkan requires a minimum of 4 StorageBuffer
/// arguments (maxPerStageDescriptorStorageBuffers), which is very easy to
/// reach.
llvm::ModulePass *createClusterPodKernelArgumentsPass();
/// Splat select scalar condition with vector data operands.
/// @return An LLVM module pass.
///
/// Converts a select with scalar bool argument but vector operands so that
/// the bool condition is converted into a bool vector with as many elements
/// as the operands.
llvm::ModulePass *createSplatSelectConditionPass();
/// Hide loads from __constant address space.
/// @return An LLVM module pass.
///
/// Wrap the result of a load from __constant address space. This prevents
/// the instcombine pass from generating selects over pointer-to-constant.
/// See https://github.com/google/clspv/issues/71
llvm::ModulePass *createHideConstantLoadsPass();
/// Unhide loads from __constant address space.
/// @return An LLVM module pass.
///
/// Unwrap the result of a load from __constant address space.
llvm::ModulePass *createUnhideConstantLoadsPass();
/// Rewrite insertvalue instructions.
/// @return An LLVM module pass.
///
/// Rewrite a chain of insertvalue instructions that cover all
/// members of a struct, so that it becomes a single new builtin
/// corresponding to an OpCompositeConstruct. Only affects
/// insertvalue instructions with a single index operand.
///
/// Also, if -hack-inserts option is used, then also rewrite
/// chains of insertvalue instructions that only cover some but
/// not all of a struct.
llvm::ModulePass *createRewriteInsertsPass();
/// Allocate descriptors.
/// @return An LLVM module pass.
///
/// Replaces compile time samplers (from the sampler map), and kernel
/// arguments to descriptors. Encoding is via compiler builtins
/// starting with "clspv.resource.*". Uses of those kernel arguments
/// and samplers are replaced by calls the special functions yielding
/// the appropriately typed pointers.
llvm::ModulePass *createAllocateDescriptorsPass(
llvm::ArrayRef<std::pair<unsigned, std::string>> samplerMap);
/// Direct Resource Access
/// @return An LLVM module pass.
///
/// For kernel arguments that map to resource variables (descriptors),
/// try to avoid passing them by pointer down into helper functions.
/// Find and exploit commonality among callees of each helper function.
/// Assumes descriptors have been allocated and mapped to function
/// calls of the form @clspv.resource.var.*.
llvm::ModulePass *createDirectResourceAccessPass();
/// Signed compare fixup
/// @return An LLVM module pass.
///
/// If -hack-scf is used, then rewrite signed integer comparisons
/// into other equivalent code that does not use signed integer
/// comparisons. Works around a driver bug.
llvm::ModulePass *createSignedCompareFixupPass();
/// Share Module Scope Variables
/// @return An LLVM module pass.
///
/// Attempts to de-duplicate module scope Workgroup scope variables between
/// kernels. Module scope variables of the same type can be merged if they are
/// used by an exclusive set of kernels. This pass should run before direct
/// resource access to enable more opportunities for that pass.
llvm::ModulePass *createShareModuleScopeVariablesPass();
/// Exhaustively inline entry points
/// @return An LLVM module pass.
///
/// Exhaustively inlines all kernels in the module.
llvm::ModulePass *createInlineEntryPointsPass();
/// Scalarize composite instructions.
/// @return An LLVM module pass
///
/// Scalarizes instructions that return composite types. Works around driver
/// bugs. See HackPhis().
llvm::ModulePass *createScalarizePass();
/// Transform types for UBOs to conform to Vulkan 14.5.4 rules.
/// @return An LLVM module pass
///
/// Performs type mutation on the module so that types destined for UBOs satisfy
/// requirements in SPIR-V. It is assumed that the types are correct except for
/// LLVM inserted padding added to represent the types as laid out in OpenCL C.
llvm::ModulePass *createUBOTypeTransformPass();
/// Removes unused arguments from non-kernel functions.
/// @return An LLVM module pass
llvm::ModulePass *createRemoveUnusedArgumentsPass();
/// Specializes or inlines functions with UBO arguments to satisfy SPIR-V
/// requirements.
llvm::ModulePass *createMultiVersionUBOFunctionsPass();
/// Specialize image types.
llvm::ModulePass *createSpecializeImageTypesPass();
/// Fixes up the structurized cfg to have valid structured control flow.
/// Currently performs the following changes:
/// * Add a block to split a continue block used a merge block.
llvm::FunctionPass *createFixupStructuredCFGPass();
/// Adds attributes to intrinsic and builtin functions to produce a better
/// optimization outcome.
llvm::ModulePass *createAddFunctionAttributesPass();
/// Undo specific instcombine transformations that are harmful to generating
/// SPIR-V.
llvm::ModulePass *createUndoInstCombinePass();
/// Removes FreezeInsts from the IR.
llvm::ModulePass *createStripFreezePass();
/// Annotates kernels with the metadata indicating how the POD args should be
/// handled.
llvm::ModulePass *createAutoPodArgsPass();
/// Strips some builtin library functions when -cl-native-math is specified to
/// provide faster, lower precision alternatives.
llvm::ModulePass *createNativeMathPass();
} // namespace clspv