///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// DxilUtil.h                                                                //
// Copyright (C) Microsoft Corporation. All rights reserved.                 //
// This file is distributed under the University of Illinois Open Source     //
// License. See LICENSE.TXT for details.                                     //
//                                                                           //
// DXIL helper functions.                                                    //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

#pragma once
#include <memory>
#include <string>
#include <unordered_set>

#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/GetElementPtrTypeIterator.h"
#include "llvm/IR/IRBuilder.h"

#include "dxc/DXIL/DxilConstants.h"
#include "dxc/DXIL/DxilResourceProperties.h"

namespace llvm {
class Type;
class GlobalVariable;
class Function;
class Module;
class MemoryBuffer;
class LLVMContext;
class DiagnosticInfo;
class Value;
class Instruction;
class CallInst;
class BasicBlock;
class raw_ostream;
class ModulePass;
class PassRegistry;
class DebugInfoFinder;
class DebugLoc;
class DIGlobalVariable;
class ConstantInt;
class SwitchInst;
class GEPOperator;

ModulePass *createDxilLoadMetadataPass();
void initializeDxilLoadMetadataPass(llvm::PassRegistry &);
} // namespace llvm

namespace hlsl {

class DxilFieldAnnotation;
class DxilModule;
class DxilTypeSystem;
class OP;

namespace dxilutil {
extern const char ManglingPrefix[];
extern const char EntryPrefix[];
extern const char *kResourceMapErrorMsg;

unsigned GetLegacyCBufferFieldElementSize(DxilFieldAnnotation &fieldAnnotation,
                                          llvm::Type *Ty,
                                          DxilTypeSystem &typeSys);
llvm::Type *GetArrayEltTy(llvm::Type *Ty);
bool HasDynamicIndexing(llvm::Value *V);

// Cleans up unnecessary chains of GEPs and bitcasts left over from certain
// optimizations. This function is NOT safe to call while iterating
// instructions either forward or backward. If V happens to be a GEP or
// bitcast, the function may delete V, instructions preceding V it, and
// instructions following V.
bool MergeGepUse(llvm::Value *V);

// Find alloca insertion point, given instruction
llvm::Instruction *
FindAllocaInsertionPt(llvm::Instruction *I); // Considers entire parent function
llvm::Instruction *
FindAllocaInsertionPt(llvm::BasicBlock *BB); // Only considers provided block
llvm::Instruction *FindAllocaInsertionPt(llvm::Function *F);
llvm::Instruction *SkipAllocas(llvm::Instruction *I);
// Get first non-alloca insertion point, to avoid inserting non-allocas before
// alloca
llvm::Instruction *FirstNonAllocaInsertionPt(
    llvm::Instruction *I); // Considers entire parent function
llvm::Instruction *FirstNonAllocaInsertionPt(
    llvm::BasicBlock *BB); // Only considers provided block
llvm::Instruction *FirstNonAllocaInsertionPt(llvm::Function *F);

bool IsStaticGlobal(llvm::GlobalVariable *GV);
bool IsSharedMemoryGlobal(llvm::GlobalVariable *GV);
bool RemoveUnusedFunctions(llvm::Module &M, llvm::Function *EntryFunc,
                           llvm::Function *PatchConstantFunc, bool IsLib);

llvm::DIGlobalVariable *
FindGlobalVariableDebugInfo(llvm::GlobalVariable *GV,
                            llvm::DebugInfoFinder &DbgInfoFinder);

void EmitErrorOnInstruction(llvm::Instruction *I, llvm::Twine Msg);
void EmitWarningOnInstruction(llvm::Instruction *I, llvm::Twine Msg);
void EmitErrorOnFunction(llvm::LLVMContext &Ctx, llvm::Function *F,
                         llvm::Twine Msg);
void EmitWarningOnFunction(llvm::LLVMContext &Ctx, llvm::Function *F,
                           llvm::Twine Msg);
void EmitErrorOnGlobalVariable(llvm::LLVMContext &Ctx, llvm::GlobalVariable *GV,
                               llvm::Twine Msg);
void EmitWarningOnGlobalVariable(llvm::LLVMContext &Ctx,
                                 llvm::GlobalVariable *GV, llvm::Twine Msg);
void EmitErrorOnContext(llvm::LLVMContext &Ctx, llvm::Twine Msg);
void EmitWarningOnContext(llvm::LLVMContext &Ctx, llvm::Twine Msg);
void EmitNoteOnContext(llvm::LLVMContext &Ctx, llvm::Twine Msg);

void EmitResMappingError(llvm::Instruction *Res);
// Simple demangle just support case "\01?name@" pattern.
llvm::StringRef DemangleFunctionName(llvm::StringRef name);
// ReplaceFunctionName replaces the undecorated portion of originalName with
// undecorated newName
std::string ReplaceFunctionName(llvm::StringRef originalName,
                                llvm::StringRef newName);
void PrintEscapedString(llvm::StringRef Name, llvm::raw_ostream &Out);
void PrintUnescapedString(llvm::StringRef Name, llvm::raw_ostream &Out);
// Change select/phi on operands into select/phi on operation.
// phi0 = phi a0, b0, c0
// phi1 = phi a1, b1, c1
// Inst = Add(phi0, phi1);
// into
// A = Add(a0, a1);
// B = Add(b0, b1);
// C = Add(c0, c1);
// NewInst = phi A, B, C
// Only support 1 operand now, other oerands should be Constant.
llvm::Value *SelectOnOperation(llvm::Instruction *Inst, unsigned operandIdx);
// Collect all select operand used by Inst.
void CollectSelect(llvm::Instruction *Inst,
                   std::unordered_set<llvm::Instruction *> &selectSet);
// If all operands are the same for a select inst, replace it with the operand.
// Returns replacement value if successful
llvm::Value *MergeSelectOnSameValue(llvm::Instruction *SelInst,
                                    unsigned startOpIdx, unsigned numOperands);
bool SimplifyTrivialPHIs(llvm::BasicBlock *BB);
llvm::BasicBlock *GetSwitchSuccessorForCond(llvm::SwitchInst *Switch,
                                            llvm::ConstantInt *Cond);
void MigrateDebugValue(llvm::Value *Old, llvm::Value *New);
void TryScatterDebugValueToVectorElements(llvm::Value *Val);
std::unique_ptr<llvm::Module> LoadModuleFromBitcode(llvm::StringRef BC,
                                                    llvm::LLVMContext &Ctx,
                                                    std::string &DiagStr);
std::unique_ptr<llvm::Module> LoadModuleFromBitcode(llvm::MemoryBuffer *MB,
                                                    llvm::LLVMContext &Ctx,
                                                    std::string &DiagStr);
std::unique_ptr<llvm::Module>
LoadModuleFromBitcodeLazy(std::unique_ptr<llvm::MemoryBuffer> &&MB,
                          llvm::LLVMContext &Ctx, std::string &DiagStr);
void PrintDiagnosticHandler(const llvm::DiagnosticInfo &DI, void *Context);
bool IsIntegerOrFloatingPointType(llvm::Type *Ty);
// Returns true if type contains HLSL Object type (resource)
bool ContainsHLSLObjectType(llvm::Type *Ty);
std::pair<bool, DxilResourceProperties>
GetHLSLResourceProperties(llvm::Type *Ty);
bool IsHLSLResourceType(llvm::Type *Ty);
bool IsHLSLObjectType(llvm::Type *Ty);
bool IsHLSLRayQueryType(llvm::Type *Ty);
llvm::Type *GetHLSLHitObjectType(llvm::Module *M);
bool IsHLSLHitObjectType(llvm::Type *Ty);
llvm::Type *GetHLSLMatrixRefType(llvm::Module *M);
bool IsHLSLMatrixRefType(llvm::Type *Ty);
bool IsHLSLResourceDescType(llvm::Type *Ty);
bool IsResourceSingleComponent(llvm::Type *Ty);
uint8_t GetResourceComponentCount(llvm::Type *Ty);
bool IsSplat(llvm::ConstantDataVector *cdv);
bool IsHLSLNodeIOType(llvm::Type *Ty);
bool IsHLSLNodeOutputType(llvm::Type *Ty);
bool IsHLSLNodeOutputArrayType(llvm::Type *Ty);
bool IsHLSLEmptyNodeOutputType(llvm::Type *Ty);
bool IsHLSLEmptyNodeOutputArrayType(llvm::Type *Ty);
bool IsHLSLNodeInputRecordType(llvm::Type *Ty);
bool IsHLSLRWNodeInputRecordType(llvm::Type *Ty);
bool IsHLSLNodeOutputRecordType(llvm::Type *Ty);
bool IsHLSLGSNodeOutputRecordType(llvm::Type *Ty);
bool IsHLSLNodeRecordType(llvm::Type *Ty);
bool IsHLSLNodeInputOutputType(llvm::Type *Ty);

llvm::Type *
StripArrayTypes(llvm::Type *Ty,
                llvm::SmallVectorImpl<unsigned> *OuterToInnerLengths = nullptr);
llvm::Type *WrapInArrayTypes(llvm::Type *Ty,
                             llvm::ArrayRef<unsigned> OuterToInnerLengths);
llvm::Value *MirrorGEP(llvm::GEPOperator *GEP, llvm::Value *NewBasePtr);

llvm::CallInst *TranslateCallRawBufferLoadToBufferLoad(
    llvm::CallInst *CI, llvm::Function *newFunction, hlsl::OP *op);
void ReplaceRawBufferLoadWithBufferLoad(llvm::Function *F, hlsl::OP *op);

llvm::CallInst *TranslateCallRawBufferStoreToBufferStore(
    llvm::CallInst *CI, llvm::Function *newFunction, hlsl::OP *op);
void ReplaceRawBufferStoreWithBufferStore(llvm::Function *F, hlsl::OP *op);

void ReplaceRawBufferLoad64Bit(llvm::Function *F, llvm::Type *EltTy,
                               hlsl::OP *hlslOP);
void ReplaceRawBufferStore64Bit(llvm::Function *F, llvm::Type *ETy,
                                hlsl::OP *hlslOP);

bool IsConvergentMarker(llvm::Value *V);
llvm::Value *GetConvergentSource(llvm::Value *V);

/// If value is a bitcast to base class pattern, equivalent
/// to a getelementptr X, 0, 0, 0...  turn it into the appropriate gep.
/// This can enhance SROA and other transforms that want type-safe pointers,
/// and enables merging with other getelementptr's.
llvm::Value *TryReplaceBaseCastWithGep(llvm::Value *V);
bool FunctionHasNoSideEffects(llvm::Instruction *I);
llvm::Value::user_iterator mdv_users_end(llvm::Value *V);
llvm::Value::user_iterator mdv_users_begin(llvm::Value *V);
inline bool mdv_user_empty(llvm::Value *V) {
  return mdv_users_begin(V) == mdv_users_end(V);
}

/// Finds all allocas that only have stores and delete them.
/// These allocas hold on to values that do not contribute to the
/// shader's results.
bool DeleteDeadAllocas(llvm::Function &F);

llvm::Value *GEPIdxToOffset(llvm::GetElementPtrInst *GEP,
                            llvm::IRBuilder<> &Builder, hlsl::OP *OP,
                            const llvm::DataLayout &DL);

// Passes back Dxil version of the given module on true return.
bool LoadDxilVersion(const llvm::Module *M, unsigned &Major, unsigned &Minor);

} // namespace dxilutil

} // namespace hlsl
