blob: b13b17284adfb16377172d314320f296129f205c [file] [edit]
// Copyright 2022 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 "llvm/IR/InstVisitor.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/PassManager.h"
#ifndef _CLSPV_LIB_THREE_ELEMENT_VECTOR_LOWERING_PASS_H
#define _CLSPV_LIB_THREE_ELEMENT_VECTOR_LOWERING_PASS_H
namespace clspv {
struct ThreeElementVectorLoweringPass
: llvm::OptionalPassInfoMixin<ThreeElementVectorLoweringPass>,
llvm::InstVisitor<ThreeElementVectorLoweringPass, llvm::Value *> {
llvm::PreservedAnalyses run(llvm::Module &M, llvm::ModuleAnalysisManager &);
private:
// Implementation details for InstVisitor.
using Visitor =
llvm::InstVisitor<ThreeElementVectorLoweringPass, llvm::Value *>;
using Visitor::visit;
friend Visitor;
/// Higher-level dispatcher. This is not provided by InstVisitor.
/// Returns nullptr if no lowering is required.
llvm::Value *visit(llvm::Value *V);
/// Visit Constant. This is not provided by InstVisitor.
llvm::Value *visitConstant(llvm::Constant &Cst);
/// Visit Unary or Binary Operator. This is not provided by InstVisitor.
llvm::Value *visitNAryOperator(llvm::Instruction &I);
/// InstVisitor impl, general "catch-all" function.
llvm::Value *visitInstruction(llvm::Instruction &I);
// InstVisitor impl, specific cases.
llvm::Value *visitAllocaInst(llvm::AllocaInst &I);
llvm::Value *visitBinaryOperator(llvm::BinaryOperator &I);
llvm::Value *visitCallInst(llvm::CallInst &I);
llvm::Value *visitCastInst(llvm::CastInst &I);
llvm::Value *visitCmpInst(llvm::CmpInst &I);
llvm::Value *visitExtractElementInst(llvm::ExtractElementInst &I);
llvm::Value *visitExtractValueInst(llvm::ExtractValueInst &I);
llvm::Value *visitGetElementPtrInst(llvm::GetElementPtrInst &I);
llvm::Value *visitInsertElementInst(llvm::InsertElementInst &I);
llvm::Value *visitInsertValueInst(llvm::InsertValueInst &I);
llvm::Value *visitLoadInst(llvm::LoadInst &I);
llvm::Value *visitPHINode(llvm::PHINode &I);
llvm::Value *visitSelectInst(llvm::SelectInst &I);
llvm::Value *visitShuffleVectorInst(llvm::ShuffleVectorInst &I);
llvm::Value *visitStoreInst(llvm::StoreInst &I);
llvm::Value *visitUnaryOperator(llvm::UnaryOperator &I);
private:
// Helpers for lowering values.
/// Return true if the given @p U needs to be lowered.
///
/// This only looks at the types involved, not the opcodes or anything else.
bool handlingRequired(llvm::User &U);
/// Return the lowered version of @p U or @p U itself when no lowering is
/// required.
llvm::Value *visitOrSelf(llvm::Value *U) {
auto *V = visit(U);
return V ? V : U;
}
/// Register the replacement of @p U with @p V.
///
/// If @p U and @p V have the same type, replace the relevant usages as well
/// to ensure the rest of the program is using the new instructions.
void registerReplacement(llvm::Value &U, llvm::Value &V);
private:
// Helpers for lowering types.
/// Get a vector of 4 elements equivalent for this type, if it uses a vector
/// of 3 elements. Returns nullptr if no lowering is required.
llvm::Type *getEquivalentType(llvm::Type *Ty);
/// Implementation details of getEquivalentType.
llvm::Type *getEquivalentTypeImpl(llvm::Type *Ty);
/// Return the equivalent type for @p Ty or @p Ty if no lowering is needed.
llvm::Type *getEquivalentTypeOrSelf(llvm::Type *Ty) {
auto *EquivalentTy = getEquivalentType(Ty);
return EquivalentTy ? EquivalentTy : Ty;
}
private:
// High-level implementation details of runOnModule.
/// Look for vec3 patterns inside the function that requires lowering vec3 to
/// vec4.
bool vec3ShouldBeLowered(llvm::Function &F);
/// Returns whether the vec3 should be transformed into vec4
bool vec3ShouldBeLowered(llvm::Module &M);
/// Returns whether a value have an implicit cast or not, works only with
/// opaque pointers
bool haveImplicitCast(llvm::Value *Value);
// Returns true if the type before last indice is a vec3 and last indice is
// not constant or bigger or equal to 3.
bool haveInvalidVec3GEP(llvm::Value *Value);
/// Lower all global variables in the module.
bool runOnGlobals(llvm::Module &M);
/// Lower the given function.
bool runOnFunction(llvm::Function &F);
/// Map the call @p CI to an OpenCL builtin function or an LLVM intrinsic to
/// the same calls but reworking the args and the return value.
llvm::Value *convertBuiltinCall(llvm::CallInst &CI,
llvm::Type *EquivalentReturnTy,
llvm::ArrayRef<llvm::Value *> EquivalentArgs);
/// Replace all instructions that have vector of size 3 to vector of size 4.
/// This will run at the end of the pass and before cleaning dead
/// instructions. It was needed as opaque pointers will depend on inferring
/// the types from other instructions so we should keep instructions change to
/// the end of the pass pipeline.
void replaceAllVec3Instances();
/// Map the call @p CI to an OpenCL builtin function or an LLVM intrinsic to
/// a calls with vec4 without reworking the args and the return value.
llvm::Value *
convertSIMDBuiltinCall(llvm::CallInst &CI, llvm::Type *EquivalentReturnTy,
llvm::ArrayRef<llvm::Value *> EquivalentArgs);
// Map calls of Spirv Operators builtin that cannot be convert using
// convertBuiltinCall or convertSIMDBuiltinCall
llvm::Value *
convertSpirvOpBuiltinCall(llvm::CallInst &CI, llvm::Type *EquivalentReturnTy,
llvm::ArrayRef<llvm::Value *> EquivalentArgs);
/// Create an alternative version of @p F that doesn't have vec3 as parameter
/// or return types.
/// Returns nullptr if no lowering is required.
llvm::Function *convertUserDefinedFunction(llvm::Function &F);
/// Create (and insert) a call to the equivalent user-defined function.
llvm::CallInst *
convertUserDefinedFunctionCall(llvm::CallInst &CI,
llvm::ArrayRef<llvm::Value *> EquivalentArgs);
/// Clears the dead instructions and others that might be rendered dead
/// by their removal.
void cleanDeadInstructions();
/// Remove all long-vector functions that were lowered.
void cleanDeadFunctions();
/// Remove all long-vector globals that were lowered.
void cleanDeadGlobals();
private:
/// A map between 3 elements vector types and their equivalent representation.
llvm::DenseMap<llvm::Type *, llvm::Type *> TypeMap;
/// Opaque pointer type cache.
llvm::DenseMap<llvm::Value *, llvm::Type *> type_cache_;
/// A map between original values and their replacement.
///
/// The content of this mapping is valid only for the function being visited
/// at a given time. The keys in this mapping should be removed from the
/// function once all instructions in the current function have been visited
/// and transformed. Instructions are not removed from the function as they
/// are visited because this would invalidate iterators.
llvm::DenseMap<llvm::Value *, llvm::Value *> ValueMap;
/// A map between functions and their replacement. This includes OpenCL
/// builtin declarations.
///
/// The keys in this mapping should be deleted when finishing processing the
/// module.
llvm::DenseMap<llvm::Function *, llvm::Function *> FunctionMap;
/// A map between global variables and their replacement.
///
/// The map is filled before any functions are visited, yet the original
/// globals are not removed from the module. Their removal is deferred once
/// all functions have been visited.
llvm::DenseMap<llvm::GlobalVariable *, llvm::GlobalVariable *>
GlobalVariableMap;
};
} // namespace clspv
#endif // _CLSPV_LIB_THREE_ELEMENT_VECTOR_LOWERING_PASS_H