///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// HLOperations.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.                                     //
//                                                                           //
// Implentation of High Level DXIL operations.                               //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

#pragma once

#include "llvm/IR/IRBuilder.h"
#include <string>

namespace llvm {
class Argument;
template <typename T> class ArrayRef;
class AttributeSet;
class CallInst;
class Function;
class FunctionType;
class Module;
class StringRef;
class Type;
class Value;
} // namespace llvm

namespace hlsl {

enum class HLOpcodeGroup {
  NotHL,
  HLExtIntrinsic,
  HLIntrinsic,
  HLCast,
  HLInit,
  HLBinOp,
  HLUnOp,
  HLSubscript,
  HLMatLoadStore,
  HLSelect,
  HLCreateHandle,
  // FIXME: Change the way these groups are being proliferated/used for each
  // new generated op.
  // Suggestion: Add an opcode and reuse CreateHandle/AnnotateHandle groups, and
  // add an IndexHandle group.
  HLCreateNodeOutputHandle,
  HLIndexNodeHandle,
  HLCreateNodeInputRecordHandle,
  HLAnnotateHandle,
  HLWaveMatrix_Annotate,
  HLAnnotateNodeHandle,
  HLAnnotateNodeRecordHandle,
  NumOfHLOps
};

enum class HLBinaryOpcode {
  Invalid,
  Mul,
  Div,
  Rem,
  Add,
  Sub,
  Shl,
  Shr,
  LT,
  GT,
  LE,
  GE,
  EQ,
  NE,
  And,
  Xor,
  Or,
  LAnd,
  LOr,
  UDiv,
  URem,
  UShr,
  ULT,
  UGT,
  ULE,
  UGE,
  NumOfBO,
};

enum class HLUnaryOpcode {
  Invalid,
  PostInc,
  PostDec,
  PreInc,
  PreDec,
  Plus,
  Minus,
  Not,
  LNot,
  NumOfUO,
};

enum class HLSubscriptOpcode {
  DefaultSubscript,
  ColMatSubscript,
  RowMatSubscript,
  ColMatElement,
  RowMatElement,
  DoubleSubscript,
  CBufferSubscript,
  VectorSubscript, // Only for bool vector, other vector type will use GEP
                   // directly.
};

enum class HLCastOpcode {
  DefaultCast,
  UnsignedUnsignedCast,
  FromUnsignedCast,
  ToUnsignedCast,
  ColMatrixToVecCast,
  RowMatrixToVecCast,
  ColMatrixToRowMatrix,
  RowMatrixToColMatrix,
  HandleToResCast,
  HandleToNodeOutputCast,
  NodeOutputToHandleCast,
  HandleToNodeRecordCast,
  NodeRecordToHandleCast,
};

enum class HLMatLoadStoreOpcode {
  ColMatLoad,
  ColMatStore,
  RowMatLoad,
  RowMatStore,
};

extern const char *const HLPrefix;

HLOpcodeGroup GetHLOpcodeGroup(llvm::Function *F);
HLOpcodeGroup GetHLOpcodeGroupByName(const llvm::Function *F);
llvm::StringRef GetHLOpcodeGroupNameByAttr(llvm::Function *F);
llvm::StringRef GetHLLowerStrategy(llvm::Function *F);
unsigned GetHLOpcode(const llvm::CallInst *CI);
unsigned GetRowMajorOpcode(HLOpcodeGroup group, unsigned opcode);
void SetHLLowerStrategy(llvm::Function *F, llvm::StringRef S);

void SetHLWaveSensitive(llvm::Function *F);
bool IsHLWaveSensitive(llvm::Function *F);

// For intrinsic opcode.
unsigned GetUnsignedOpcode(unsigned opcode);
// For HLBinaryOpcode.
bool HasUnsignedOpcode(HLBinaryOpcode opcode);
HLBinaryOpcode GetUnsignedOpcode(HLBinaryOpcode opcode);

llvm::StringRef GetHLOpcodeGroupName(HLOpcodeGroup op);

namespace HLOperandIndex {
// Opcode parameter.
const unsigned kOpcodeIdx = 0;

// Used to initialize values that have no valid index in the HL overload.
const unsigned kInvalidIdx = UINT32_MAX;

// Matrix store.
const unsigned kMatStoreDstPtrOpIdx = 1;
const unsigned kMatStoreValOpIdx = 2;

// Matrix load.
const unsigned kMatLoadPtrOpIdx = 1;

// Normal subscipts.
const unsigned kSubscriptObjectOpIdx = 1;
const unsigned kSubscriptIndexOpIdx = 2;

// Double subscripts.
const unsigned kDoubleSubscriptMipLevelOpIdx = 3;

// Matrix subscripts.
const unsigned kMatSubscriptMatOpIdx = 1;
const unsigned kMatSubscriptSubOpIdx = 2;

// Matrix init.
const unsigned kMatArrayInitMatOpIdx = 1;
const unsigned kMatArrayInitFirstArgOpIdx = 2;

// Array Init.
const unsigned kArrayInitPtrOpIdx = 1;
const unsigned kArrayInitFirstArgOpIdx = 2;

// Normal Init.
const unsigned kInitFirstArgOpIdx = 1;

// Unary operators.
const unsigned kUnaryOpSrc0Idx = 1;

// Binary operators.
const unsigned kBinaryOpSrc0Idx = 1;
const unsigned kBinaryOpSrc1Idx = 2;

// Trinary operators.
const unsigned kTrinaryOpSrc0Idx = 1;
const unsigned kTrinaryOpSrc1Idx = 2;
const unsigned kTrinaryOpSrc2Idx = 3;

// Interlocked.
const unsigned kInterlockedDestOpIndex = 1;
const unsigned kInterlockedValueOpIndex = 2;
const unsigned kInterlockedOriginalValueOpIndex = 3;

// Interlocked method
const unsigned kInterlockedMethodValueOpIndex = 3;

// InterlockedCompareExchange.
const unsigned kInterlockedCmpDestOpIndex = 1;
const unsigned kInterlockedCmpCompareValueOpIndex = 2;
const unsigned kInterlockedCmpValueOpIndex = 3;
const unsigned kInterlockedCmpOriginalValueOpIndex = 4;

// Lerp.
const unsigned kLerpOpXIdx = 1;
const unsigned kLerpOpYIdx = 2;
const unsigned kLerpOpSIdx = 3;

// ProcessTessFactorIsoline.
const unsigned kProcessTessFactorRawDetailFactor = 1;
const unsigned kProcessTessFactorRawDensityFactor = 2;
const unsigned kProcessTessFactorRoundedDetailFactor = 3;
const unsigned kProcessTessFactorRoundedDensityFactor = 4;

// ProcessTessFactor.
const unsigned kProcessTessFactorRawEdgeFactor = 1;
const unsigned kProcessTessFactorInsideScale = 2;
const unsigned kProcessTessFactorRoundedEdgeFactor = 3;
const unsigned kProcessTessFactorRoundedInsideFactor = 4;
const unsigned kProcessTessFactorUnRoundedInsideFactor = 5;

// Reflect.
const unsigned kReflectOpIIdx = 1;
const unsigned kReflectOpNIdx = 2;

// Refract
const unsigned kRefractOpIIdx = 1;
const unsigned kRefractOpNIdx = 2;
const unsigned kRefractOpEtaIdx = 3;

// SmoothStep.
const unsigned kSmoothStepOpMinIdx = 1;
const unsigned kSmoothStepOpMaxIdx = 2;
const unsigned kSmoothStepOpXIdx = 3;

// Clamp
const unsigned kClampOpXIdx = 1;
const unsigned kClampOpMinIdx = 2;
const unsigned kClampOpMaxIdx = 3;

// Object functions.
const unsigned kHandleOpIdx = 1;
// Store.
const unsigned kStoreOffsetOpIdx = 2;
const unsigned kStoreValOpIdx = 3;
// Load.
const unsigned kBufLoadAddrOpIdx = 2;
const unsigned kBufLoadStatusOpIdx = 3;
const unsigned kRWTexLoadStatusOpIdx = 3;
const unsigned kTexLoadOffsetOpIdx = 3;
const unsigned kTexLoadStatusOpIdx = 4;
// Load for Texture2DMS
const unsigned kTex2DMSLoadSampleIdxOpIdx = 3;
const unsigned kTex2DMSLoadOffsetOpIdx = 4;
const unsigned kTex2DMSLoadStatusOpIdx = 5;
// mips.Operator.
const unsigned kMipLoadAddrOpIdx = 3;
const unsigned kMipLoadOffsetOpIdx = 4;
const unsigned kMipLoadStatusOpIdx = 5;

// Sample.
const unsigned kSampleSamplerArgIndex = 2;
const unsigned kSampleCoordArgIndex = 3;
const unsigned kSampleOffsetArgIndex = 4;
const unsigned kSampleClampArgIndex = 5;
const unsigned kSampleStatusArgIndex = 6;

// SampleG.
const unsigned kSampleGDDXArgIndex = 4;
const unsigned kSampleGDDYArgIndex = 5;
const unsigned kSampleGOffsetArgIndex = 6;
const unsigned kSampleGClampArgIndex = 7;
const unsigned kSampleGStatusArgIndex = 8;

// SampleCmp.
const unsigned kSampleCmpCmpValArgIndex = 4;
const unsigned kSampleCmpOffsetArgIndex = 5;
const unsigned kSampleCmpClampArgIndex = 6;
const unsigned kSampleCmpStatusArgIndex = 7;

// SampleCmpBias.
const unsigned kSampleCmpBCmpValArgIndex = 4;
const unsigned kSampleCmpBBiasArgIndex = 5;
const unsigned kSampleCmpBOffsetArgIndex = 6;
const unsigned kSampleCmpBClampArgIndex = 7;
const unsigned kSampleCmpBStatusArgIndex = 8;

// SampleCmpGrad.
const unsigned kSampleCmpGCmpValArgIndex = 4;
const unsigned kSampleCmpGDDXArgIndex = 5;
const unsigned kSampleCmpGDDYArgIndex = 6;
const unsigned kSampleCmpGOffsetArgIndex = 7;
const unsigned kSampleCmpGClampArgIndex = 8;
const unsigned kSampleCmpGStatusArgIndex = 9;

// SampleBias.
const unsigned kSampleBBiasArgIndex = 4;
const unsigned kSampleBOffsetArgIndex = 5;
const unsigned kSampleBClampArgIndex = 6;
const unsigned kSampleBStatusArgIndex = 7;

// SampleLevel.
const unsigned kSampleLLevelArgIndex = 4;
const unsigned kSampleLOffsetArgIndex = 5;
const unsigned kSampleLStatusArgIndex = 6;

// SampleCmpLevel
// the rest are the same as SampleCmp
const unsigned kSampleCmpLLevelArgIndex = 5;
const unsigned kSampleCmpLOffsetArgIndex = 6;

// SampleCmpLevelZero.
const unsigned kSampleCmpLZCmpValArgIndex = 4;
const unsigned kSampleCmpLZOffsetArgIndex = 5;
const unsigned kSampleCmpLZStatusArgIndex = 6;

// Gather.
const unsigned kGatherSamplerArgIndex = 2;
const unsigned kGatherCoordArgIndex = 3;
const unsigned kGatherOffsetArgIndex = 4;
const unsigned kGatherStatusArgIndex = 5;
const unsigned kGatherSampleOffsetArgIndex = 5;
const unsigned kGatherStatusWithSampleOffsetArgIndex = 8;
const unsigned kGatherCubeStatusArgIndex = 4;

// GatherCmp.
const unsigned kGatherCmpCmpValArgIndex = 4;
const unsigned kGatherCmpOffsetArgIndex = 5;
const unsigned kGatherCmpStatusArgIndex = 6;
const unsigned kGatherCmpSampleOffsetArgIndex = 6;
const unsigned kGatherCmpStatusWithSampleOffsetArgIndex = 9;
const unsigned kGatherCmpCubeStatusArgIndex = 5;

// WriteSamplerFeedback.
const unsigned kWriteSamplerFeedbackSampledArgIndex = 2;
const unsigned kWriteSamplerFeedbackSamplerArgIndex = 3;
const unsigned kWriteSamplerFeedbackCoordArgIndex = 4;
const unsigned kWriteSamplerFeedbackBias_BiasArgIndex = 5;
const unsigned kWriteSamplerFeedbackLevel_LodArgIndex = 5;
const unsigned kWriteSamplerFeedbackGrad_DdxArgIndex = 5;
const unsigned kWriteSamplerFeedbackGrad_DdyArgIndex = 6;
const unsigned kWriteSamplerFeedback_ClampArgIndex = 5;
const unsigned kWriteSamplerFeedbackBias_ClampArgIndex = 6;
const unsigned kWriteSamplerFeedbackGrad_ClampArgIndex = 7;

// StreamAppend.
const unsigned kStreamAppendStreamOpIndex = 1;
const unsigned kStreamAppendDataOpIndex = 2;

// Append.
const unsigned kAppendValOpIndex = 2;

// Interlocked.
const unsigned kObjectInterlockedDestOpIndex = 2;
const unsigned kObjectInterlockedValueOpIndex = 3;
const unsigned kObjectInterlockedOriginalValueOpIndex = 4;

// InterlockedCompareExchange.
const unsigned kObjectInterlockedCmpDestOpIndex = 2;
const unsigned kObjectInterlockedCmpCompareValueOpIndex = 3;
const unsigned kObjectInterlockedCmpValueOpIndex = 4;
const unsigned kObjectInterlockedCmpOriginalValueOpIndex = 5;

// GetSamplePosition.
const unsigned kGetSamplePositionSampleIdxOpIndex = 2;

// GetDimensions.
const unsigned kGetDimensionsMipLevelOpIndex = 2;
const unsigned kGetDimensionsMipWidthOpIndex = 3;
const unsigned kGetDimensionsNoMipWidthOpIndex = 2;

// WaveAllEqual.
const unsigned kWaveAllEqualValueOpIdx = 1;

// CreateHandle.
const unsigned kCreateHandleResourceOpIdx = 1;
const unsigned kCreateHandleIndexOpIdx = 2; // Only for array of cbuffer.

// Annotate(Node)(Record)Handle.
const unsigned kAnnotateHandleResourcePropertiesOpIdx = 2;
const unsigned kAnnotateHandleResourceTypeOpIdx = 3;

// AnnotateWaveMatrix.
const unsigned kAnnotateWaveMatrixPtrOpIdx = 1;
const unsigned kAnnotateWaveMatrixPropertiesOpIdx = 2;

// TraceRay.
const unsigned kTraceRayRayDescOpIdx = 7;
const unsigned kTraceRayPayLoadOpIdx = 8;

// CallShader.
const unsigned kCallShaderPayloadOpIdx = 2;

// TraceRayInline.
const unsigned kTraceRayInlineRayDescOpIdx = 5;

// ReportIntersection.
const unsigned kReportIntersectionAttributeOpIdx = 3;

// DispatchMesh
const unsigned kDispatchMeshOpThreadX = 1;
const unsigned kDispatchMeshOpThreadY = 2;
const unsigned kDispatchMeshOpThreadZ = 3;
const unsigned kDispatchMeshOpPayload = 4;

// WaveMatrix
const unsigned kWaveMatThisOpIdx = 1;
const unsigned kWaveMatFillScalarOpIdx = 2;
const unsigned kWaveMatScalarOpOpIdx = 2;
const unsigned kWaveMatOther1OpIdx = 2;
const unsigned kWaveMatOther2OpIdx = 3;
const unsigned kWaveMatLoadStoreBufOpIdx = 2;
const unsigned kWaveMatLoadStoreStartOpIdx = 3;
const unsigned kWaveMatLoadStoreStrideOpIdx = 4;
// Note: No ColMajor arg for fragments, so align idx is one less.
const unsigned kWaveMatLoadStoreColMajorOpIdx = 5;
const unsigned kWaveMatFragLoadStoreAlignmentOpIdx = 5;
const unsigned kWaveMatLoadStoreAlignmentOpIdx = 6;

// Work Graph

const unsigned kIncrementOutputCountCountIdx = 2;

const unsigned kBarrierMemoryTypeFlagsOpIdx = 1;
const unsigned kBarrierSemanticFlagsOpIdx = 2;

// Node Handles
const unsigned kAllocateRecordNumRecordsIdx = 2;
const unsigned kNodeOutputMetadataIDIdx = 1;
const unsigned kIndexNodeHandleArrayIDIdx = 2;
const unsigned kNodeInputRecordMetadataIDIdx = 1;
const unsigned kNodeHandleToResCastOpIdx = 1;
const unsigned kAnnotateNodeHandleNodePropIdx = 2;
const unsigned kAnnotateNodeRecordHandleNodeRecordPropIdx = 2;

} // namespace HLOperandIndex

llvm::Function *GetOrCreateHLFunction(llvm::Module &M,
                                      llvm::FunctionType *funcTy,
                                      HLOpcodeGroup group, unsigned opcode);
llvm::Function *GetOrCreateHLFunction(llvm::Module &M,
                                      llvm::FunctionType *funcTy,
                                      HLOpcodeGroup group,
                                      llvm::StringRef *groupName,
                                      llvm::StringRef *fnName, unsigned opcode);

llvm::Function *GetOrCreateHLFunction(llvm::Module &M,
                                      llvm::FunctionType *funcTy,
                                      HLOpcodeGroup group, unsigned opcode,
                                      const llvm::AttributeSet &attribs);
llvm::Function *GetOrCreateHLFunction(llvm::Module &M,
                                      llvm::FunctionType *funcTy,
                                      HLOpcodeGroup group,
                                      llvm::StringRef *groupName,
                                      llvm::StringRef *fnName, unsigned opcode,
                                      const llvm::AttributeSet &attribs);

llvm::Function *GetOrCreateHLFunctionWithBody(llvm::Module &M,
                                              llvm::FunctionType *funcTy,
                                              HLOpcodeGroup group,
                                              unsigned opcode,
                                              llvm::StringRef name);

llvm::Value *callHLFunction(llvm::Module &Module, HLOpcodeGroup OpcodeGroup,
                            unsigned Opcode, llvm::Type *RetTy,
                            llvm::ArrayRef<llvm::Value *> Args,
                            const llvm::AttributeSet &attribs,
                            llvm::IRBuilder<> &Builder);

llvm::Value *callHLFunction(llvm::Module &Module, HLOpcodeGroup OpcodeGroup,
                            unsigned Opcode, llvm::Type *RetTy,
                            llvm::ArrayRef<llvm::Value *> Args,
                            llvm::IRBuilder<> &Builder);

} // namespace hlsl
