//===- SPIRVInstruction.h - Class to represent SPIRV instruction -*- C++ -*-===//
//
//                     The LLVM/SPIRV Translator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Copyright (c) 2014 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal with the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimers.
// Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimers in the documentation
// and/or other materials provided with the distribution.
// Neither the names of Advanced Micro Devices, Inc., nor the names of its
// contributors may be used to endorse or promote products derived from this
// Software without specific prior written permission.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH
// THE SOFTWARE.
//
//===----------------------------------------------------------------------===//
/// \file
///
/// This file defines Instruction class for SPIR-V.
///
//===----------------------------------------------------------------------===//

#ifndef SPIRVINSTRUCTION_HPP_
#define SPIRVINSTRUCTION_HPP_

#include "SPIRVEnum.h"
#include "SPIRVIsValidEnum.h"
#include "SPIRVStream.h"
#include "SPIRVValue.h"
#include "SPIRVBasicBlock.h"
#include "SPIRVOpCode.h"

#include <cassert>
#include <cstdint>
#include <functional>
#include <iostream>
#include <map>
#include <utility>
#include <vector>
#include <unordered_set>

namespace SPIRV{

typedef std::vector<SPIRVValue *> ValueVec;
typedef std::pair<ValueVec::iterator, ValueVec::iterator> ValueRange;

class SPIRVBasicBlock;
class SPIRVFunction;

bool isSpecConstantOpAllowedOp(Op OC);

class SPIRVComponentExecutionScope {
public:
  SPIRVComponentExecutionScope(Scope TheScope = ScopeInvocation):
    ExecScope(TheScope){}
  Scope ExecScope;
};

class SPIRVComponentMemorySemanticsMask {
public:
  SPIRVComponentMemorySemanticsMask(SPIRVWord TheSema = SPIRVWORD_MAX):
    MemSema(TheSema){}
  SPIRVWord MemSema;
};

class SPIRVComponentOperands {
public:
  SPIRVComponentOperands(){};
  SPIRVComponentOperands(const std::vector<SPIRVValue *> &TheOperands):
    Operands(TheOperands){};
  SPIRVComponentOperands(std::vector<SPIRVValue *> &&TheOperands):
    Operands(std::move(TheOperands)){};
  std::vector<SPIRVValue *> getCompOperands() {
    return Operands;
  }
  std::vector<SPIRVType *> getCompOperandTypes() {
    std::vector<SPIRVType *> Tys;
    for (auto &I:getCompOperands())
      Tys.push_back(I->getType());
    return Tys;
  }
protected:
  std::vector<SPIRVValue *> Operands;
};

class SPIRVInstruction: public SPIRVValue {
public:
  // Complete constructor for instruction with type and id
  SPIRVInstruction(unsigned TheWordCount, Op TheOC, SPIRVType *TheType,
      SPIRVId TheId, SPIRVBasicBlock *TheBB);
  // Complete constructor for instruction with module, type and id
  SPIRVInstruction(unsigned TheWordCount, Op TheOC,
      SPIRVType *TheType, SPIRVId TheId, SPIRVBasicBlock *TheBB,
      SPIRVModule *TheBM);
  // Complete constructor for instruction with id but no type
  SPIRVInstruction(unsigned TheWordCount, Op TheOC, SPIRVId TheId,
      SPIRVBasicBlock *TheBB);
  // Complete constructor for instruction without type and id
  SPIRVInstruction(unsigned TheWordCount, Op TheOC,
      SPIRVBasicBlock *TheBB);
  // Complete constructor for instruction with type but no id
  SPIRVInstruction(unsigned TheWordCount, Op TheOC, SPIRVType *TheType,
      SPIRVBasicBlock *TheBB);
  // Incomplete constructor
  SPIRVInstruction(Op TheOC = OpNop):SPIRVValue(TheOC), BB(NULL){}

  virtual bool isInst() const { return true;}
  SPIRVBasicBlock *getParent() const {return BB;}
  SPIRVInstruction *getPrevious() const { return BB->getPrevious(this);}
  SPIRVInstruction *getNext() const { return BB->getNext(this);}
  virtual std::vector<SPIRVValue *> getOperands();
  std::vector<SPIRVType*> getOperandTypes();
  static std::vector<SPIRVType*> getOperandTypes(
      const std::vector<SPIRVValue *> &Ops);

  void setParent(SPIRVBasicBlock *);
  void setScope(SPIRVEntry *);
  void addFPRoundingMode(SPIRVFPRoundingModeKind Kind) {
    addDecorate(DecorationFPRoundingMode, Kind);
  }
  void eraseFPRoundingMode() {
    eraseDecorate(DecorationFPRoundingMode);
  }
  void setSaturatedConversion(bool Enable) {
    if (Enable)
      addDecorate(DecorationSaturatedConversion);
    else
      eraseDecorate(DecorationSaturatedConversion);
  }
  bool hasFPRoundingMode(SPIRVFPRoundingModeKind *Kind = nullptr) {
    SPIRVWord V;
    auto Found = hasDecorate(DecorationFPRoundingMode, 0, &V);
    if (Found && Kind)
      *Kind = static_cast<SPIRVFPRoundingModeKind>(V);
    return Found;
  }
  bool isSaturatedConversion() {
    return hasDecorate(DecorationSaturatedConversion) ||
        OpCode == OpSatConvertSToU ||
        OpCode == OpSatConvertUToS;
  }

  SPIRVBasicBlock* getBasicBlock() const {
    return BB;
  }

  void setBasicBlock(SPIRVBasicBlock* TheBB) {
    BB = TheBB;
    if (TheBB)
      setModule(TheBB->getModule());
  }

protected:
  void validate()const {
    SPIRVValue::validate();
  }
private:
  SPIRVBasicBlock *BB;
};

class SPIRVInstTemplateBase:public SPIRVInstruction {
public:
  /// Create an empty instruction. Mainly for getting format information,
  /// e.g. whether an operand is literal.
  static SPIRVInstTemplateBase *create(Op TheOC){
    auto Inst = static_cast<SPIRVInstTemplateBase *>(SPIRVEntry::create(TheOC));
    assert(Inst);
    Inst->init();
    return Inst;
  }
  /// Create a instruction without operands.
  static SPIRVInstTemplateBase *create(Op TheOC, SPIRVType *TheType,
      SPIRVId TheId, SPIRVBasicBlock *TheBB,
      SPIRVModule *TheModule){
    auto Inst = create(TheOC);
    Inst->init(TheType, TheId, TheBB, TheModule);
    return Inst;
  }
  /// Create a complete and valid instruction.
  static SPIRVInstTemplateBase *create(Op TheOC, SPIRVType *TheType,
      SPIRVId TheId, const std::vector<SPIRVWord> &TheOps, SPIRVBasicBlock *TheBB,
      SPIRVModule *TheModule){
    auto Inst = create(TheOC);
    Inst->init(TheType, TheId, TheBB, TheModule);
    Inst->setOpWords(TheOps);
    Inst->validate();
    return Inst;
  }
  SPIRVInstTemplateBase(Op OC = OpNop)
    :SPIRVInstruction(OC), HasVariWC(false){
    init();
  }
  virtual ~SPIRVInstTemplateBase(){}
  SPIRVInstTemplateBase *init(SPIRVType *TheType,
      SPIRVId TheId, SPIRVBasicBlock *TheBB,
      SPIRVModule *TheModule){
    assert((TheBB || TheModule) && "Invalid BB or Module");
    if (TheBB)
      setBasicBlock(TheBB);
    else {
      setModule(TheModule);
    }
    setId(hasId() ? TheId : SPIRVID_INVALID);
    setType(hasType() ? TheType : nullptr);
    return this;
  }
  virtual void init() {}
  virtual void initImpl(Op OC, bool HasId = true, SPIRVWord WC = 0,
      bool VariWC = false, unsigned Lit1 = ~0U,
      unsigned Lit2 = ~0U, unsigned Lit3 = ~0U){
    OpCode = OC;
    if (!HasId) {
      setHasNoId();
      setHasNoType();
    }
    if (WC)
      SPIRVEntry::setWordCount(WC);
    setHasVariableWordCount(VariWC);
    addLit(Lit1);
    addLit(Lit2);
    addLit(Lit3);
  }
  virtual bool isOperandLiteral(unsigned I) const {
    return Lit.count(I);
  }
  void addLit(unsigned L) {
    if (L != ~0U)
      Lit.insert(L);
  }
  /// \return Expected number of operands. If the instruction has variable
  /// number of words, return the minimum.
  SPIRVWord getExpectedNumOperands() const {
    assert(WordCount > 0 && "Word count not initialized");
    auto Exp = WordCount - 1;
    if (hasId())
      --Exp;
    if (hasType())
      --Exp;
    return Exp;
  }
  virtual void setOpWordsAndValidate(const std::vector<SPIRVWord> &TheOps) {
    setOpWords(TheOps);
    validate();
  }
  virtual void setOpWords(const std::vector<SPIRVWord> &TheOps) {
    SPIRVWord WC = TheOps.size() + 1;
    if (hasId())
      ++WC;
    if (hasType())
      ++WC;
    if (WordCount) {
      if (WordCount == WC) {
        // do nothing
      } else {
        assert(HasVariWC && WC >= WordCount && "Invalid word count");
        SPIRVEntry::setWordCount(WC);
      }
    } else
      SPIRVEntry::setWordCount(WC);
    Ops = TheOps;
  }
  virtual void setWordCount(SPIRVWord TheWordCount) {
    SPIRVEntry::setWordCount(TheWordCount);
    auto NumOps = WordCount - 1;
    if (hasId())
      --NumOps;
    if (hasType())
      --NumOps;
    Ops.resize(NumOps);
  }

  std::vector<SPIRVWord> &getOpWords() {
    return Ops;
  }

  const std::vector<SPIRVWord> &getOpWords() const {
    return Ops;
  }

  SPIRVWord getOpWord(int I) const {
    return Ops[I];
  }

  /// Get operand as value.
  /// If the operand is a literal, return it as a uint32 constant.
  SPIRVValue *getOpValue(int I) {
    return isOperandLiteral(I) ? Module->getLiteralAsConstant(Ops[I]) :
        getValue(Ops[I]);
  }

  // Get the offset of operands.
  // Some instructions skip literals when returning operands.
  size_t getOperandOffset() const {
    if (hasExecScope() && !isGroupOpCode(OpCode) && !isPipeOpCode(OpCode))
      return 1;
    return 0;
  }

  // Get operands which are values.
  // Drop execution scope and group operation literals.
  // Return other literals as uint32 constants.
  virtual std::vector<SPIRVValue *> getOperands() {
    std::vector<SPIRVValue*> VOps;
    auto Offset = getOperandOffset();
    for (size_t I = 0, E = Ops.size() - Offset; I != E; ++I)
      VOps.push_back(getOperand(I));
    return VOps;
  }

  virtual std::vector<SPIRVEntry*> getNonLiteralOperands() const {
    std::vector<SPIRVEntry*> Operands;
    for (size_t I = getOperandOffset(), E = Ops.size(); I < E; ++I)
      if (!isOperandLiteral(I))
        Operands.push_back(getEntry(Ops[I]));
    return Operands;
  }

  virtual SPIRVValue *getOperand(unsigned I) {
    return getOpValue(I + getOperandOffset());
  }

  bool hasExecScope() const {
    return SPIRV::hasExecScope(OpCode);
  }

  bool hasGroupOperation() const {
    return SPIRV::hasGroupOperation(OpCode);
  }

  bool getSPIRVGroupOperation(SPIRVGroupOperationKind &GroupOp) const {
    if (!hasGroupOperation())
      return false;
    GroupOp = static_cast<SPIRVGroupOperationKind>(Ops[1]);
    return true;
  }

  Scope getExecutionScope() const {
    if(!hasExecScope())
      return ScopeInvocation;
    return static_cast<Scope>(
        static_cast<SPIRVConstant*>(getValue(Ops[0]))->getZExtIntValue());
  }

  bool hasVariableWordCount() const {
    return HasVariWC;
  }

  void setHasVariableWordCount(bool VariWC) {
    HasVariWC = VariWC;
  }

protected:
  virtual void encode(spv_ostream &O) const {
    auto E = getEncoder(O);
    if (hasType())
      E << Type;
    if (hasId())
      E << Id;
    E << Ops;
  }
  virtual void decode(std::istream &I) {
    auto D = getDecoder(I);
    if (hasType())
      D >> Type;
    if (hasId())
      D >> Id;
    D >> Ops;
  }
  std::vector<SPIRVWord> Ops;
  bool HasVariWC;
  std::unordered_set<unsigned> Lit; // Literal operand index
};

template<typename BT        = SPIRVInstTemplateBase,
         Op OC              = OpNop,
         bool HasId         = true,
         SPIRVWord WC        = 0,
         bool HasVariableWC = false,
         unsigned Literal1  = ~0U,
         unsigned Literal2  = ~0U,
         unsigned Literal3  = ~0U>
class SPIRVInstTemplate:public BT {
public:
  typedef BT BaseTy;
  SPIRVInstTemplate(){
    init();
  }
  virtual ~SPIRVInstTemplate(){}
  virtual void init() {
    this->initImpl(OC, HasId, WC, HasVariableWC, Literal1, Literal2, Literal3);
  }
};

class SPIRVMemoryAccess {
public:
  SPIRVMemoryAccess(const std::vector<SPIRVWord> &TheMemoryAccess):
    TheMemoryAccessMask(0), Alignment(0) {
    MemoryAccessUpdate(TheMemoryAccess);
  }

  SPIRVMemoryAccess() : TheMemoryAccessMask(0), Alignment(0){}

  void MemoryAccessUpdate(const std::vector<SPIRVWord> &MemoryAccess) {
    if (!MemoryAccess.size())
      return;
    assert((MemoryAccess.size() == 1 || MemoryAccess.size() == 2) && "Invalid memory access operand size");
    TheMemoryAccessMask = MemoryAccess[0];
    if (MemoryAccess[0] & MemoryAccessAlignedMask) {
      assert(MemoryAccess.size() == 2 && "Alignment operand is missing");
      Alignment = MemoryAccess[1];
    }
  }
  SPIRVWord isVolatile() const { return getMemoryAccessMask() & MemoryAccessVolatileMask; }
  SPIRVWord isNonTemporal() const { return getMemoryAccessMask() & MemoryAccessNontemporalMask; }
  SPIRVWord getMemoryAccessMask() const { return TheMemoryAccessMask; }
  SPIRVWord getAlignment() const { return Alignment; }

protected:
  SPIRVWord TheMemoryAccessMask;
  SPIRVWord Alignment;
};

class SPIRVVariable : public SPIRVInstruction {
public:
  // Complete constructor for integer constant
  SPIRVVariable(SPIRVType *TheType, SPIRVId TheId,
    SPIRVValue *TheInitializer, const std::string &TheName,
    SPIRVStorageClassKind TheStorageClass, SPIRVBasicBlock *TheBB,
    SPIRVModule *TheM)
    :SPIRVInstruction(TheInitializer ? 5 : 4, OpVariable, TheType,
        TheId, TheBB, TheM),
    StorageClass(TheStorageClass){
    if (TheInitializer)
      Initializer.push_back(TheInitializer->getId());
    Name = TheName;
    validate();
  }
  // Incomplete constructor
  SPIRVVariable() :SPIRVInstruction(OpVariable),
      StorageClass(StorageClassFunction){}

  SPIRVStorageClassKind getStorageClass() const { return StorageClass; }
  SPIRVValue *getInitializer() const {
    if (Initializer.empty())
      return nullptr;
    assert(Initializer.size() == 1);
    return getValue(Initializer[0]);
  }
  bool isConstant() const {
    return hasDecorate(DecorationConstant);
  }
  bool isBuiltin(SPIRVBuiltinVariableKind *BuiltinKind = nullptr) const {
    SPIRVWord Kind;
    bool Found = hasDecorate(DecorationBuiltIn, 0, &Kind);
    if (!Found)
      return false;
    if (BuiltinKind)
      *BuiltinKind = static_cast<SPIRVBuiltinVariableKind>(Kind);
    return true;
  }
  void setBuiltin(SPIRVBuiltinVariableKind Kind) {
    assert(isValid(Kind));
    addDecorate(new SPIRVDecorate(DecorationBuiltIn, this, Kind));
  }
  void setIsConstant(bool Is) {
    if (Is)
      addDecorate(new SPIRVDecorate(DecorationConstant, this));
    else
      eraseDecorate(DecorationConstant);
  }
  virtual std::vector<SPIRVEntry*> getNonLiteralOperands() const {
    if (SPIRVValue *V = getInitializer())
      return std::vector<SPIRVEntry*>(1, V);
    return std::vector<SPIRVEntry*>();
  }
protected:
  void validate() const {
    SPIRVValue::validate();
    assert(isValid(StorageClass));
    assert(Initializer.size() == 1 || Initializer.empty());
  }
  void setWordCount(SPIRVWord TheWordCount) {
    SPIRVEntry::setWordCount(TheWordCount);
    Initializer.resize(WordCount - 4);
  }
  _SPIRV_DEF_ENCDEC4(Type, Id, StorageClass, Initializer)

    SPIRVStorageClassKind StorageClass;
  std::vector<SPIRVId> Initializer;
};

class SPIRVStore:public SPIRVInstruction, public SPIRVMemoryAccess {
public:
  const static SPIRVWord FixedWords = 3;
  // Complete constructor
  SPIRVStore(SPIRVId PointerId, SPIRVId ValueId,
      const std::vector<SPIRVWord> &TheMemoryAccess, SPIRVBasicBlock *TheBB)
    :SPIRVInstruction(FixedWords + TheMemoryAccess.size(), OpStore,
        TheBB),
     SPIRVMemoryAccess(TheMemoryAccess),
     MemoryAccess(TheMemoryAccess),
     PtrId(PointerId),
     ValId(ValueId){
    setAttr();
    validate();
    assert(TheBB && "Invalid BB");
  }
  // Incomplete constructor
  SPIRVStore():SPIRVInstruction(OpStore), SPIRVMemoryAccess(),
      PtrId(SPIRVID_INVALID), ValId(SPIRVID_INVALID){
    setAttr();
  }

  SPIRVValue *getSrc() const { return getValue(ValId);}
  SPIRVValue *getDst() const { return getValue(PtrId);}
protected:
  void setAttr() {
    setHasNoType();
    setHasNoId();
  }

  void setWordCount(SPIRVWord TheWordCount) {
    SPIRVEntry::setWordCount(TheWordCount);
    MemoryAccess.resize(TheWordCount - FixedWords);
  }
  void encode(spv_ostream &O) const {
    getEncoder(O) << PtrId << ValId << MemoryAccess;
  }

  void decode(std::istream &I) {
    getDecoder(I) >> PtrId >> ValId >> MemoryAccess;
    MemoryAccessUpdate(MemoryAccess);
  }

  void validate()const {
    SPIRVInstruction::validate();
    if (getSrc()->isForward() || getDst()->isForward())
      return;
    assert(getValueType(PtrId)->getPointerElementType() == getValueType(ValId)
        && "Inconsistent operand types");
  }
private:
  std::vector<SPIRVWord> MemoryAccess;
  SPIRVId PtrId;
  SPIRVId ValId;
};

class SPIRVLoad:public SPIRVInstruction, public SPIRVMemoryAccess {
public:
  const static SPIRVWord FixedWords = 4;
  // Complete constructor
  SPIRVLoad(SPIRVId TheId, SPIRVId PointerId,
      const std::vector<SPIRVWord> &TheMemoryAccess, SPIRVBasicBlock *TheBB)
    :SPIRVInstruction(FixedWords + TheMemoryAccess.size() , OpLoad,
        TheBB->getValueType(PointerId)->getPointerElementType(), TheId, TheBB),
        SPIRVMemoryAccess(TheMemoryAccess), PtrId(PointerId),
        MemoryAccess(TheMemoryAccess) {
      validate();
      assert(TheBB && "Invalid BB");
    }
  // Incomplete constructor
  SPIRVLoad():SPIRVInstruction(OpLoad), SPIRVMemoryAccess(),
      PtrId(SPIRVID_INVALID){}

  SPIRVValue *getSrc() const { return Module->get<SPIRVValue>(PtrId);}

protected:
  void setWordCount(SPIRVWord TheWordCount) {
    SPIRVEntry::setWordCount(TheWordCount);
    MemoryAccess.resize(TheWordCount - FixedWords);
  }

  void encode(spv_ostream &O) const {
    getEncoder(O) << Type << Id << PtrId << MemoryAccess;
  }

  void decode(std::istream &I) {
    getDecoder(I) >> Type >> Id >> PtrId >> MemoryAccess;
    MemoryAccessUpdate(MemoryAccess);
  }

  void validate()const {
    SPIRVInstruction::validate();
    assert((getValue(PtrId)->isForward() ||
        Type == getValueType(PtrId)->getPointerElementType()) &&
        "Inconsistent types");
  }
private:
  SPIRVId PtrId;
  std::vector<SPIRVWord> MemoryAccess;
};

class SPIRVBinary:public SPIRVInstTemplateBase {
protected:
  void validate()const {
    SPIRVId Op1 = Ops[0];
    SPIRVId Op2 = Ops[1];
    SPIRVType *op1Ty, *op2Ty;
    SPIRVInstruction::validate();
    if (getValue(Op1)->isForward() || getValue(Op2)->isForward())
      return;
    if (getValueType(Op1)->isTypeVector()) {
      op1Ty = getValueType(Op1)->getVectorComponentType();
      op2Ty = getValueType(Op2)->getVectorComponentType();
      assert(getValueType(Op1)->getVectorComponentCount() ==
             getValueType(Op2)->getVectorComponentCount() &&
               "Inconsistent Vector component width");
    }
    else {
      op1Ty = getValueType(Op1);
      op2Ty = getValueType(Op2);
    }

    if (isBinaryOpCode(OpCode)) {
      assert(getValueType(Op1)== getValueType(Op2) &&
             "Invalid type for binary instruction");
      assert((op1Ty->isTypeInt() || op2Ty->isTypeFloat()) &&
               "Invalid type for Binary instruction");
      assert((op1Ty->getBitWidth() == op2Ty->getBitWidth()) &&
               "Inconsistent BitWidth");
    } else if (isShiftOpCode(OpCode)) {
      assert((op1Ty->isTypeInt() || op2Ty->isTypeInt()) &&
          "Invalid type for shift instruction");
    } else if (isLogicalOpCode(OpCode)) {
      assert((op1Ty->isTypeBool() || op2Ty->isTypeBool()) &&
          "Invalid type for logical instruction");
    } else if (isBitwiseOpCode(OpCode)) {
      assert((op1Ty->isTypeInt() || op2Ty->isTypeInt()) &&
          "Invalid type for bitwise instruction");
      assert((op1Ty->getIntegerBitWidth() == op2Ty->getIntegerBitWidth()) &&
          "Inconsistent BitWidth");
    } else {
      assert(0 && "Invalid op code!");
    }
  }
};

template<Op OC>
class SPIRVBinaryInst:public SPIRVInstTemplate<SPIRVBinary, OC, true, 5, false> {
};

/* ToDo: SMod and FMod to be added */
#define _SPIRV_OP(x) typedef SPIRVBinaryInst<Op##x> SPIRV##x;
_SPIRV_OP(IAdd)
_SPIRV_OP(FAdd)
_SPIRV_OP(ISub)
_SPIRV_OP(FSub)
_SPIRV_OP(IMul)
_SPIRV_OP(FMul)
_SPIRV_OP(UDiv)
_SPIRV_OP(SDiv)
_SPIRV_OP(FDiv)
_SPIRV_OP(SRem)
_SPIRV_OP(FRem)
_SPIRV_OP(UMod)
_SPIRV_OP(ShiftLeftLogical)
_SPIRV_OP(ShiftRightLogical)
_SPIRV_OP(ShiftRightArithmetic)
_SPIRV_OP(LogicalAnd)
_SPIRV_OP(LogicalOr)
_SPIRV_OP(LogicalEqual)
_SPIRV_OP(LogicalNotEqual)
_SPIRV_OP(BitwiseAnd)
_SPIRV_OP(BitwiseOr)
_SPIRV_OP(BitwiseXor)
_SPIRV_OP(Dot)
#undef _SPIRV_OP

template<Op TheOpCode>
class SPIRVInstNoOperand:public SPIRVInstruction {
public:
  // Complete constructor
  SPIRVInstNoOperand(SPIRVBasicBlock *TheBB):SPIRVInstruction(1, TheOpCode,
      TheBB){
    setAttr();
    validate();
  }
  // Incomplete constructor
  SPIRVInstNoOperand():SPIRVInstruction(TheOpCode){
    setAttr();
  }
protected:
  void setAttr() {
    setHasNoId();
    setHasNoType();
  }
  _SPIRV_DEF_ENCDEC0
};

typedef SPIRVInstNoOperand<OpReturn> SPIRVReturn;

class SPIRVReturnValue:public SPIRVInstruction {
public:
  static const Op OC = OpReturnValue;
  // Complete constructor
  SPIRVReturnValue(SPIRVValue *TheReturnValue, SPIRVBasicBlock *TheBB)
    :SPIRVInstruction(2, OC, TheBB), ReturnValueId(TheReturnValue->getId()){
    setAttr();
    validate();
    assert(TheBB && "Invalid BB");
  }
  // Incomplete constructor
  SPIRVReturnValue():SPIRVInstruction(OC), ReturnValueId(SPIRVID_INVALID) {
    setAttr();
  }

  SPIRVValue *getReturnValue() const {
    return getValue(ReturnValueId);
  }
protected:
  void setAttr() {
    setHasNoId();
    setHasNoType();
  }
  _SPIRV_DEF_ENCDEC1(ReturnValueId)
  void validate()const {
    SPIRVInstruction::validate();
  }
  SPIRVId ReturnValueId;
};

class SPIRVBranch:public SPIRVInstruction {
public:
  static const Op OC = OpBranch;
  // Complete constructor
  SPIRVBranch(SPIRVLabel *TheTargetLabel,SPIRVBasicBlock *TheBB)
    :SPIRVInstruction(2, OC, TheBB), TargetLabelId(TheTargetLabel->getId()) {
    validate();
    assert(TheBB && "Invalid BB");
  }
  // Incomplete constructor
  SPIRVBranch():SPIRVInstruction(OC), TargetLabelId(SPIRVID_INVALID) {
    setHasNoId();
    setHasNoType();
  }
  SPIRVValue *getTargetLabel() const {
    return getValue(TargetLabelId);
  }
protected:
  _SPIRV_DEF_ENCDEC1(TargetLabelId)
  void validate()const {
    SPIRVInstruction::validate();
    assert(WordCount == 2);
    assert(OpCode == OC);
    assert(getTargetLabel()->isLabel() || getTargetLabel()->isForward());
  }
  SPIRVId TargetLabelId;
};

class SPIRVBranchConditional:public SPIRVInstruction {
public:
  static const Op OC = OpBranchConditional;
  // Complete constructor
  SPIRVBranchConditional(SPIRVValue *TheCondition, SPIRVLabel *TheTrueLabel,
      SPIRVLabel *TheFalseLabel, SPIRVBasicBlock *TheBB)
    :SPIRVInstruction(4, OC, TheBB), ConditionId(TheCondition->getId()),
     TrueLabelId(TheTrueLabel->getId()), FalseLabelId(TheFalseLabel->getId()){
    validate();
  }
  SPIRVBranchConditional(SPIRVValue *TheCondition, SPIRVLabel *TheTrueLabel,
      SPIRVLabel *TheFalseLabel, SPIRVBasicBlock *TheBB, SPIRVWord TrueWeight,
      SPIRVWord FalseWeight)
    :SPIRVInstruction(6, OC, TheBB), ConditionId(TheCondition->getId()),
     TrueLabelId(TheTrueLabel->getId()), FalseLabelId(TheFalseLabel->getId()){
    BranchWeights.push_back(TrueWeight);
    BranchWeights.push_back(FalseWeight);
    validate();
    assert(TheBB && "Invalid BB");
  }
  // Incomplete constructor
  SPIRVBranchConditional():SPIRVInstruction(OC), ConditionId(SPIRVID_INVALID),
      TrueLabelId(SPIRVID_INVALID), FalseLabelId(SPIRVID_INVALID) {
    setHasNoId();
    setHasNoType();
  }
  SPIRVValue *getCondition() const {
    return getValue(ConditionId);
  }
  SPIRVLabel *getTrueLabel() const {
    return get<SPIRVLabel>(TrueLabelId);
  }
  SPIRVLabel *getFalseLabel() const {
    return get<SPIRVLabel>(FalseLabelId);
  }
protected:
  void setWordCount(SPIRVWord TheWordCount) {
    SPIRVEntry::setWordCount(TheWordCount);
    BranchWeights.resize(TheWordCount - 4);
  }
  _SPIRV_DEF_ENCDEC4(ConditionId, TrueLabelId, FalseLabelId, BranchWeights)
  void validate()const {
    SPIRVInstruction::validate();
    assert(WordCount == 4 || WordCount == 6);
    assert(WordCount == BranchWeights.size() + 4);
    assert(OpCode == OC);
    assert(getCondition()->isForward() ||
        getCondition()->getType()->isTypeBool());
    assert(getTrueLabel()->isForward() || getTrueLabel()->isLabel());
    assert(getFalseLabel()->isForward() || getFalseLabel()->isLabel());
  }
  SPIRVId ConditionId;
  SPIRVId TrueLabelId;
  SPIRVId FalseLabelId;
  std::vector<SPIRVWord> BranchWeights;
};

class SPIRVPhi: public SPIRVInstruction {
public:
  static const Op OC = OpPhi;
  static const SPIRVWord FixedWordCount = 3;
  SPIRVPhi(SPIRVType *TheType, SPIRVId TheId,
      const std::vector<SPIRVValue *> &ThePairs, SPIRVBasicBlock *BB)
    :SPIRVInstruction(ThePairs.size() + FixedWordCount, OC, TheType, TheId, BB){
    Pairs = getIds(ThePairs);
    validate();
    assert(BB && "Invalid BB");
  }
  SPIRVPhi():SPIRVInstruction(OC) {}
  std::vector<SPIRVValue *> getPairs() {
    return getValues(Pairs);
  }
  void addPair(SPIRVValue *Value, SPIRVBasicBlock *BB) {
    Pairs.push_back(Value->getId());
    Pairs.push_back(BB->getId());
    WordCount = Pairs.size() + FixedWordCount;
    validate();
  }
  void setPairs(const std::vector<SPIRVValue *> &ThePairs) {
    Pairs = getIds(ThePairs);
    WordCount = Pairs.size() + FixedWordCount;
    validate();
  }
  void foreachPair(std::function<void(SPIRVValue *, SPIRVBasicBlock *,
      size_t)> Func) {
    for (size_t I = 0, E = Pairs.size()/2; I != E; ++I) {
      SPIRVEntry *Value, *BB;
      if (!Module->exist(Pairs[2*I], &Value) ||
          !Module->exist(Pairs[2*I+1], &BB))
        continue;
      Func(static_cast<SPIRVValue *>(Value), static_cast<SPIRVBasicBlock *>(BB),
          I);
    }
  }
  void foreachPair(std::function<void(SPIRVValue *, SPIRVBasicBlock *)> Func)
    const {
    for (size_t I = 0, E = Pairs.size()/2; I != E; ++I) {
      SPIRVEntry *Value, *BB;
      if (!Module->exist(Pairs[2*I], &Value) ||
          !Module->exist(Pairs[2*I+1], &BB))
        continue;
      Func(static_cast<SPIRVValue *>(Value), static_cast<SPIRVBasicBlock *>(BB));
    }
  }
  void setWordCount(SPIRVWord TheWordCount) {
    SPIRVEntry::setWordCount(TheWordCount);
    Pairs.resize(TheWordCount - FixedWordCount);
  }
  _SPIRV_DEF_ENCDEC3(Type, Id, Pairs)
  void validate()const {
    assert(WordCount == Pairs.size() + FixedWordCount);
    assert(OpCode == OC);
    assert(Pairs.size() % 2 == 0);
    foreachPair([=](SPIRVValue *IncomingV, SPIRVBasicBlock *IncomingBB){
      assert(IncomingV->isForward() || IncomingV->getType() == Type);
      assert(IncomingBB->isBasicBlock() || IncomingBB->isForward());
    });
    SPIRVInstruction::validate();
  }
protected:
  std::vector<SPIRVId> Pairs;
};

class SPIRVCompare:public SPIRVInstTemplateBase {
protected:
  void validate()const {
    auto Op1 = Ops[0];
    auto Op2 = Ops[1];
    SPIRVType *op1Ty, *op2Ty, *resTy;
    SPIRVInstruction::validate();
    if (getValue(Op1)->isForward() || getValue(Op2)->isForward())
      return;

    if (getValueType(Op1)->isTypeVector()) {
      op1Ty = getValueType(Op1)->getVectorComponentType();
      op2Ty = getValueType(Op2)->getVectorComponentType();
      resTy = Type->getVectorComponentType();
      assert(getValueType(Op1)->getVectorComponentCount() ==
             getValueType(Op2)->getVectorComponentCount() &&
               "Inconsistent Vector component width");
    }
    else {
      op1Ty = getValueType(Op1);
      op2Ty = getValueType(Op2);
      resTy = Type;
    }
    assert(isCmpOpCode(OpCode) && "Invalid op code for cmp inst");
    assert((resTy->isTypeBool() || resTy->isTypeInt()) &&
        "Invalid type for compare instruction");
    assert(op1Ty == op2Ty && "Inconsistent types");
  }
};

template<Op OC>
class SPIRVCmpInst:public SPIRVInstTemplate<SPIRVCompare, OC, true, 5, false> {
};

#define _SPIRV_OP(x) typedef SPIRVCmpInst<Op##x> SPIRV##x;
_SPIRV_OP(IEqual)
_SPIRV_OP(FOrdEqual)
_SPIRV_OP(FUnordEqual)
_SPIRV_OP(INotEqual)
_SPIRV_OP(FOrdNotEqual)
_SPIRV_OP(FUnordNotEqual)
_SPIRV_OP(ULessThan)
_SPIRV_OP(SLessThan)
_SPIRV_OP(FOrdLessThan)
_SPIRV_OP(FUnordLessThan)
_SPIRV_OP(UGreaterThan)
_SPIRV_OP(SGreaterThan)
_SPIRV_OP(FOrdGreaterThan)
_SPIRV_OP(FUnordGreaterThan)
_SPIRV_OP(ULessThanEqual)
_SPIRV_OP(SLessThanEqual)
_SPIRV_OP(FOrdLessThanEqual)
_SPIRV_OP(FUnordLessThanEqual)
_SPIRV_OP(UGreaterThanEqual)
_SPIRV_OP(SGreaterThanEqual)
_SPIRV_OP(FOrdGreaterThanEqual)
_SPIRV_OP(FUnordGreaterThanEqual)
_SPIRV_OP(LessOrGreater)
_SPIRV_OP(Ordered)
_SPIRV_OP(Unordered)
#undef _SPIRV_OP

class SPIRVSelect:public SPIRVInstruction {
public:
  // Complete constructor
  SPIRVSelect(SPIRVId TheId, SPIRVId TheCondition, SPIRVId TheOp1, SPIRVId TheOp2,
      SPIRVBasicBlock *TheBB)
    :SPIRVInstruction(6, OpSelect, TheBB->getValueType(TheOp1), TheId,
        TheBB), Condition(TheCondition), Op1(TheOp1), Op2(TheOp2){
    validate();
    assert(TheBB && "Invalid BB");
  }
  // Incomplete constructor
  SPIRVSelect():SPIRVInstruction(OpSelect), Condition(SPIRVID_INVALID),
      Op1(SPIRVID_INVALID), Op2(SPIRVID_INVALID){}
  SPIRVValue *getCondition() { return getValue(Condition);}
  SPIRVValue *getTrueValue() { return getValue(Op1);}
  SPIRVValue *getFalseValue() { return getValue(Op2);}
protected:
  _SPIRV_DEF_ENCDEC5(Type, Id, Condition, Op1, Op2)
  void validate()const {
    SPIRVInstruction::validate();
    if (getValue(Condition)->isForward() ||
        getValue(Op1)->isForward() ||
        getValue(Op2)->isForward())
      return;

    SPIRVType *conTy = getValueType(Condition)->isTypeVector() ?
        getValueType(Condition)->getVectorComponentType() :
        getValueType(Condition);
    (void) conTy;
    assert(conTy->isTypeBool() && "Invalid type");
    assert(getType() == getValueType(Op1) && getType() == getValueType(Op2) &&
        "Inconsistent type");
  }
  SPIRVId Condition;
  SPIRVId Op1;
  SPIRVId Op2;
};

class SPIRVSwitch: public SPIRVInstruction {
public:
  static const Op OC = OpSwitch;
  static const SPIRVWord FixedWordCount = 3;
  SPIRVSwitch(SPIRVValue *TheSelect, SPIRVBasicBlock *TheDefault,
      const std::vector<std::pair<SPIRVWord, SPIRVBasicBlock *>> &ThePairs,
      SPIRVBasicBlock *BB)
    :SPIRVInstruction(ThePairs.size() * 2 + FixedWordCount, OC, BB),
     Select(TheSelect->getId()), Default(TheDefault->getId()) {
    for (auto &I:ThePairs) {
      Pairs.push_back(I.first);
      Pairs.push_back(I.second->getId());
    }
    validate();
    assert(BB && "Invalid BB");
  }
  SPIRVSwitch():SPIRVInstruction(OC), Select(SPIRVWORD_MAX),
      Default(SPIRVWORD_MAX) {
    setHasNoId();
    setHasNoType();
  }
  std::vector<SPIRVValue *> getPairs() {
    return getValues(Pairs);
  }
  SPIRVValue *getSelect() const { return getValue(Select);}
  SPIRVBasicBlock *getDefault() const {
    return static_cast<SPIRVBasicBlock *>(getValue(Default));
  }
  size_t getNumPairs() const { return Pairs.size()/2;}
  void foreachPair(std::function<void(SPIRVWord, SPIRVBasicBlock *, size_t)> Func)
    const {
    for (size_t I = 0, E = Pairs.size()/2; I != E; ++I) {
      SPIRVEntry *BB;
      if (!Module->exist(Pairs[2*I+1], &BB))
        continue;
      Func(Pairs[2*I], static_cast<SPIRVBasicBlock *>(BB), I);
    }
  }
  void setWordCount(SPIRVWord TheWordCount) {
    SPIRVEntry::setWordCount(TheWordCount);
    Pairs.resize(TheWordCount - FixedWordCount);
  }
  _SPIRV_DEF_ENCDEC3(Select, Default, Pairs)
  void validate()const {
    assert(WordCount == Pairs.size() + FixedWordCount);
    assert(OpCode == OC);
    assert(Pairs.size() % 2 == 0);
    foreachPair([=](SPIRVWord Literal, SPIRVBasicBlock *BB, size_t Index){
      assert(BB->isBasicBlock() || BB->isForward());
    });
    SPIRVInstruction::validate();
  }
protected:
  SPIRVId Select;
  SPIRVId Default;
  std::vector<SPIRVWord> Pairs;
};

class SPIRVUnary:public SPIRVInstTemplateBase {
protected:
  void validate()const {
    auto Op = Ops[0];
    SPIRVInstruction::validate();
    if (getValue(Op)->isForward())
      return;
    if (isGenericNegateOpCode(OpCode)) {
      SPIRVType *resTy = Type->isTypeVector() ?
        Type->getVectorComponentType() : Type;
      SPIRVType *opTy = Type->isTypeVector() ?
        getValueType(Op)->getVectorComponentType() : getValueType(Op);

      assert(getType() == getValueType(Op)  &&
        "Inconsistent type");
      (void) resTy;
      assert((resTy->isTypeInt() || resTy->isTypeFloat()) &&
        "Invalid type for Generic Negate instruction");
      (void) opTy;
      assert((resTy->getBitWidth() == opTy->getBitWidth()) &&
        "Invalid bitwidth for Generic Negate instruction");
      assert((Type->isTypeVector() ? (Type->getVectorComponentCount() ==
          getValueType(Op)->getVectorComponentCount()): 1) &&
          "Invalid vector component Width for Generic Negate instruction");
    }
  }
};

template<Op OC>
class SPIRVUnaryInst:public SPIRVInstTemplate<SPIRVUnary, OC, true, 4, false> {
};

#define _SPIRV_OP(x) typedef SPIRVUnaryInst<Op##x> SPIRV##x;
_SPIRV_OP(ConvertFToU)
_SPIRV_OP(ConvertFToS)
_SPIRV_OP(ConvertSToF)
_SPIRV_OP(ConvertUToF)
_SPIRV_OP(UConvert)
_SPIRV_OP(SConvert)
_SPIRV_OP(FConvert)
_SPIRV_OP(SatConvertSToU)
_SPIRV_OP(SatConvertUToS)
_SPIRV_OP(ConvertPtrToU)
_SPIRV_OP(ConvertUToPtr)
_SPIRV_OP(PtrCastToGeneric)
_SPIRV_OP(GenericCastToPtr)
_SPIRV_OP(Bitcast)
_SPIRV_OP(SNegate)
_SPIRV_OP(FNegate)
_SPIRV_OP(Not)
_SPIRV_OP(LogicalNot)
_SPIRV_OP(IsNan)
_SPIRV_OP(IsInf)
_SPIRV_OP(IsFinite)
_SPIRV_OP(IsNormal)
_SPIRV_OP(SignBitSet)
_SPIRV_OP(Any)
_SPIRV_OP(All)
#undef _SPIRV_OP

class SPIRVAccessChainBase :public SPIRVInstTemplateBase {
public:
  SPIRVValue *getBase() { return this->getValue(this->Ops[0]);}
  std::vector<SPIRVValue *> getIndices()const {
    std::vector<SPIRVWord> IndexWords(this->Ops.begin() + 1, this->Ops.end());
    return this->getValues(IndexWords);
  }
  bool isInBounds() {
    return OpCode == OpInBoundsAccessChain ||
        OpCode == OpInBoundsPtrAccessChain;
  }
  bool hasPtrIndex() {
    return OpCode == OpPtrAccessChain ||
        OpCode == OpInBoundsPtrAccessChain;
  }
};

template<Op OC, unsigned FixedWC>
class SPIRVAccessChainGeneric
    :public SPIRVInstTemplate<SPIRVAccessChainBase, OC, true, FixedWC, true> {
};

typedef SPIRVAccessChainGeneric<OpAccessChain, 4> SPIRVAccessChain;
typedef SPIRVAccessChainGeneric<OpInBoundsAccessChain, 4>
  SPIRVInBoundsAccessChain;
typedef SPIRVAccessChainGeneric<OpPtrAccessChain, 5> SPIRVPtrAccessChain;
typedef SPIRVAccessChainGeneric<OpInBoundsPtrAccessChain, 5>
  SPIRVInBoundsPtrAccessChain;

template<Op OC, SPIRVWord FixedWordCount>
class SPIRVFunctionCallGeneric: public SPIRVInstruction {
public:
  SPIRVFunctionCallGeneric(SPIRVType *TheType, SPIRVId TheId,
      const std::vector<SPIRVWord> &TheArgs, SPIRVBasicBlock *BB)
    :SPIRVInstruction(TheArgs.size() + FixedWordCount, OC, TheType, TheId, BB),
     Args(TheArgs){
    validate();
    assert(BB && "Invalid BB");
  }
  SPIRVFunctionCallGeneric(SPIRVType *TheType, SPIRVId TheId,
      const std::vector<SPIRVValue *> &TheArgs, SPIRVBasicBlock *BB)
    :SPIRVInstruction(TheArgs.size() + FixedWordCount, OC, TheType, TheId, BB) {
    Args = getIds(TheArgs);
    validate();
    assert(BB && "Invalid BB");
  }
  SPIRVFunctionCallGeneric():SPIRVInstruction(OC) {}
  const std::vector<SPIRVWord> &getArguments() {
    return Args;
  }
  std::vector<SPIRVValue *> getArgumentValues() {
    return getValues(Args);
  }
  std::vector<SPIRVType *> getArgumentValueTypes()const {
    std::vector<SPIRVType *> ArgTypes;
    for (auto &I:Args)
      ArgTypes.push_back(getValue(I)->getType());
    return ArgTypes;
  }
  void setWordCount(SPIRVWord TheWordCount) {
    SPIRVEntry::setWordCount(TheWordCount);
    Args.resize(TheWordCount - FixedWordCount);
  }
  void validate()const {
    SPIRVInstruction::validate();
  }
protected:
  std::vector<SPIRVWord> Args;
};

class SPIRVFunctionCall:
    public SPIRVFunctionCallGeneric<OpFunctionCall, 4> {
public:
  SPIRVFunctionCall(SPIRVId TheId, SPIRVFunction *TheFunction,
      const std::vector<SPIRVWord> &TheArgs, SPIRVBasicBlock *BB);
  SPIRVFunctionCall():FunctionId(SPIRVID_INVALID) {}
  SPIRVFunction *getFunction()const {
    return get<SPIRVFunction>(FunctionId);
  }
  _SPIRV_DEF_ENCDEC4(Type, Id, FunctionId, Args)
  void validate()const;
  bool isOperandLiteral(unsigned Index) const { return false;}
protected:
  SPIRVId FunctionId;
};

class SPIRVExtInst: public SPIRVFunctionCallGeneric<OpExtInst, 5> {
public:
  SPIRVExtInst(SPIRVType *TheType, SPIRVId TheId,
      SPIRVId TheBuiltinSet, SPIRVWord TheEntryPoint,
      const std::vector<SPIRVWord> &TheArgs, SPIRVBasicBlock *BB)
    :SPIRVFunctionCallGeneric(TheType, TheId, TheArgs, BB),
     ExtSetId(TheBuiltinSet),
     ExtOp(TheEntryPoint) {
    setExtSetKindById();
    validate();
  }
  SPIRVExtInst(SPIRVType *TheType, SPIRVId TheId,
      SPIRVId TheBuiltinSet, SPIRVWord TheEntryPoint,
      const std::vector<SPIRVValue *> &TheArgs, SPIRVBasicBlock *BB)
    :SPIRVFunctionCallGeneric(TheType, TheId, TheArgs, BB),
     ExtSetId(TheBuiltinSet),
     ExtOp(TheEntryPoint) {
    setExtSetKindById();
    validate();
  }
  SPIRVExtInst(SPIRVExtInstSetKind SetKind = SPIRVEIS_Count,
      unsigned ExtOC = SPIRVWORD_MAX)
    :ExtSetId(SPIRVWORD_MAX), ExtOp(ExtOC), ExtSetKind(SetKind) {}
  void setExtSetId(unsigned Set) { ExtSetId = Set;}
  void setExtOp(unsigned ExtOC) { ExtOp = ExtOC;}
  SPIRVId getExtSetId()const {
    return ExtSetId;
  }
  SPIRVWord getExtOp()const {
    return ExtOp;
  }
  void setExtSetKindById() {
    assert(Module && "Invalid module");
    ExtSetKind = Module->getBuiltinSet(ExtSetId);
    assert(ExtSetKind == SPIRVEIS_OpenCL && "not supported");
  }
  void encode(spv_ostream &O) const {
    getEncoder(O) << Type << Id << ExtSetId;
    switch(ExtSetKind) {
    case SPIRVEIS_OpenCL:
      getEncoder(O) << ExtOpOCL;
      break;
    default:
      assert(0 && "not supported");
      getEncoder(O) << ExtOp;
    }
    getEncoder(O) << Args;
  }
  void decode(std::istream &I) {
    getDecoder(I) >> Type >> Id >> ExtSetId;
    setExtSetKindById();
    switch(ExtSetKind) {
    case SPIRVEIS_OpenCL:
      getDecoder(I) >> ExtOpOCL;
      break;
    default:
      assert(0 && "not supported");
      getDecoder(I) >> ExtOp;
    }
    getDecoder(I) >> Args;
  }
  void validate()const {
    SPIRVFunctionCallGeneric::validate();
    validateBuiltin(ExtSetId, ExtOp);
  }
  bool isOperandLiteral(unsigned Index) const {
    assert(ExtSetKind == SPIRVEIS_OpenCL &&
        "Unsupported extended instruction set");
    auto EOC = static_cast<OCLExtOpKind>(ExtOp);
    switch(EOC) {
    default:
      return false;
    case OpenCLLIB::Vloadn:
    case OpenCLLIB::Vload_halfn:
    case OpenCLLIB::Vloada_halfn:
      return Index == 2;
    case OpenCLLIB::Vstore_half_r:
    case OpenCLLIB::Vstore_halfn_r:
    case OpenCLLIB::Vstorea_halfn_r:
      return Index == 3;
    }
  }
protected:
  SPIRVId ExtSetId;
  union {
    SPIRVWord ExtOp;
    OCLExtOpKind ExtOpOCL;
  };
  SPIRVExtInstSetKind ExtSetKind;
};

class SPIRVCompositeExtract:public SPIRVInstruction {
public:
  const static Op OC = OpCompositeExtract;
  // Complete constructor
  SPIRVCompositeExtract(SPIRVType *TheType, SPIRVId TheId, SPIRVValue *TheComposite,
      const std::vector<SPIRVWord>& TheIndices, SPIRVBasicBlock *TheBB):
        SPIRVInstruction(TheIndices.size() + 4, OC, TheType, TheId, TheBB),
        Composite(TheComposite->getId()), Indices(TheIndices){
    validate();
    assert(TheBB && "Invalid BB");
  }
  // Incomplete constructor
  SPIRVCompositeExtract():SPIRVInstruction(OC), Composite(SPIRVID_INVALID){}

  SPIRVValue *getComposite() { return getValue(Composite);}
  const std::vector<SPIRVWord>& getIndices()const { return Indices;}
protected:
  void setWordCount(SPIRVWord TheWordCount) {
    SPIRVEntry::setWordCount(TheWordCount);
    Indices.resize(TheWordCount - 4);
  }
  _SPIRV_DEF_ENCDEC4(Type, Id, Composite, Indices)
  // ToDo: validate the result type is consistent with the base type and indices
  // need to trace through the base type for struct types
  void validate()const {
    SPIRVInstruction::validate();
    assert(getValueType(Composite)->isTypeArray() ||
        getValueType(Composite)->isTypeStruct() ||
        getValueType(Composite)->isTypeVector());
  }
  SPIRVId Composite;
  std::vector<SPIRVWord> Indices;
};

class SPIRVCompositeInsert:public SPIRVInstruction {
public:
  const static Op OC = OpCompositeInsert;
  const static SPIRVWord FixedWordCount = 5;
  // Complete constructor
  SPIRVCompositeInsert(SPIRVId TheId, SPIRVValue *TheObject,
      SPIRVValue *TheComposite, const std::vector<SPIRVWord>& TheIndices,
      SPIRVBasicBlock *TheBB):
        SPIRVInstruction(TheIndices.size() + FixedWordCount, OC,
            TheComposite->getType(), TheId, TheBB),
        Object(TheObject->getId()), Composite(TheComposite->getId()),
        Indices(TheIndices){
    validate();
    assert(TheBB && "Invalid BB");
  }
  // Incomplete constructor
  SPIRVCompositeInsert():SPIRVInstruction(OC), Object(SPIRVID_INVALID),
      Composite(SPIRVID_INVALID){}

  SPIRVValue *getObject() { return getValue(Object);}
  SPIRVValue *getComposite() { return getValue(Composite);}
  const std::vector<SPIRVWord>& getIndices()const { return Indices;}
protected:
  void setWordCount(SPIRVWord TheWordCount) {
    SPIRVEntry::setWordCount(TheWordCount);
    Indices.resize(TheWordCount - FixedWordCount);
  }
  _SPIRV_DEF_ENCDEC5(Type, Id, Object, Composite, Indices)
  // ToDo: validate the object type is consistent with the base type and indices
  // need to trace through the base type for struct types
  void validate()const {
    SPIRVInstruction::validate();
    assert(OpCode == OC);
    assert(WordCount == Indices.size() + FixedWordCount);
    assert(getValueType(Composite)->isTypeArray() ||
        getValueType(Composite)->isTypeStruct() ||
        getValueType(Composite)->isTypeVector());
    assert(Type == getValueType(Composite));
  }
  SPIRVId Object;
  SPIRVId Composite;
  std::vector<SPIRVWord> Indices;
};

class SPIRVCopyObject :public SPIRVInstruction {
public:
  const static Op OC = OpCopyObject;

  // Complete constructor
  SPIRVCopyObject(SPIRVType *TheType, SPIRVId TheId, SPIRVValue *TheOperand,
    SPIRVBasicBlock *TheBB) :
    SPIRVInstruction(4, OC, TheType, TheId, TheBB),
    Operand(TheOperand->getId()) {
    validate();
    assert(TheBB && "Invalid BB");
  }
  // Incomplete constructor
  SPIRVCopyObject() :SPIRVInstruction(OC), Operand(SPIRVID_INVALID) {}

  SPIRVValue *getOperand() { return getValue(Operand); }

protected:
  _SPIRV_DEF_ENCDEC3(Type, Id, Operand)

    void validate()const {
    SPIRVInstruction::validate();
  }
  SPIRVId Operand;
};


class SPIRVCopyMemory :public SPIRVInstruction, public SPIRVMemoryAccess {
public:
  const static Op OC = OpCopyMemory;
  const static SPIRVWord FixedWords = 3;
  // Complete constructor
  SPIRVCopyMemory(SPIRVValue *TheTarget, SPIRVValue *TheSource,
      const std::vector<SPIRVWord> &TheMemoryAccess,
    SPIRVBasicBlock *TheBB) :
    SPIRVInstruction(FixedWords + TheMemoryAccess.size(), OC, TheBB),
    SPIRVMemoryAccess(TheMemoryAccess),
    MemoryAccess(TheMemoryAccess),
    Target(TheTarget->getId()),
    Source(TheSource->getId()) {
    validate();
    assert(TheBB && "Invalid BB");
  }

  // Incomplete constructor
  SPIRVCopyMemory() :SPIRVInstruction(OC), SPIRVMemoryAccess(),
      Target(SPIRVID_INVALID),
    Source(SPIRVID_INVALID) {
    setHasNoId();
    setHasNoType();
  }

  SPIRVValue *getSource() { return getValue(Source); }
  SPIRVValue *getTarget() { return getValue(Target); }

protected:
  void setWordCount(SPIRVWord TheWordCount) {
    SPIRVEntry::setWordCount(TheWordCount);
    MemoryAccess.resize(TheWordCount - FixedWords);
  }

  void encode(spv_ostream &O) const {
    getEncoder(O) << Target << Source << MemoryAccess;
  }

  void decode(std::istream &I) {
    getDecoder(I) >> Target >> Source >> MemoryAccess;
    MemoryAccessUpdate(MemoryAccess);
  }

  void validate()const {
    assert((getValueType(Id) == getValueType(Source)) && "Inconsistent type");
    assert(getValueType(Id)->isTypePointer() && "Invalid type");
    assert(!(getValueType(Id)->getPointerElementType()->isTypeVoid()) &&
        "Invalid type");
    SPIRVInstruction::validate();
  }

  std::vector<SPIRVWord> MemoryAccess;
  SPIRVId Target;
  SPIRVId Source;
};

class SPIRVCopyMemorySized :public SPIRVInstruction, public SPIRVMemoryAccess {
public:
  const static Op OC = OpCopyMemorySized;
  const static SPIRVWord FixedWords = 4;
  // Complete constructor
  SPIRVCopyMemorySized(SPIRVValue *TheTarget, SPIRVValue *TheSource,
      SPIRVValue *TheSize, const std::vector<SPIRVWord> &TheMemoryAccess,
      SPIRVBasicBlock *TheBB) :
      SPIRVInstruction(FixedWords + TheMemoryAccess.size(), OC, TheBB),
      SPIRVMemoryAccess(TheMemoryAccess),
      MemoryAccess(TheMemoryAccess),
      Target(TheTarget->getId()),
      Source(TheSource->getId()),
      Size(TheSize->getId()) {
    validate();
    assert(TheBB && "Invalid BB");
  }
  // Incomplete constructor
  SPIRVCopyMemorySized() :SPIRVInstruction(OC), SPIRVMemoryAccess(),
      Target(SPIRVID_INVALID), Source(SPIRVID_INVALID), Size(0) {
    setHasNoId();
    setHasNoType();
  }

  SPIRVValue *getSource() { return getValue(Source); }
  SPIRVValue *getTarget() { return getValue(Target); }
  SPIRVValue *getSize() { return getValue(Size); }

protected:
  void setWordCount(SPIRVWord TheWordCount) {
    SPIRVEntry::setWordCount(TheWordCount);
    MemoryAccess.resize(TheWordCount - FixedWords);
  }

  void encode(spv_ostream &O) const {
    getEncoder(O) << Target << Source << Size << MemoryAccess;
  }

  void decode(std::istream &I) {
    getDecoder(I) >> Target >> Source >> Size >> MemoryAccess;
    MemoryAccessUpdate(MemoryAccess);
  }

    void validate()const {
    SPIRVInstruction::validate();
  }

  std::vector<SPIRVWord> MemoryAccess;
  SPIRVId Target;
  SPIRVId Source;
  SPIRVId Size;
};

class SPIRVVectorExtractDynamic:public SPIRVInstruction {
public:
  const static Op OC = OpVectorExtractDynamic;
  // Complete constructor
  SPIRVVectorExtractDynamic(SPIRVId TheId, SPIRVValue *TheVector,
      SPIRVValue* TheIndex, SPIRVBasicBlock *TheBB)
  :SPIRVInstruction(5, OC, TheVector->getType()->getVectorComponentType(),
      TheId, TheBB), VectorId(TheVector->getId()),
      IndexId(TheIndex->getId()){
    validate();
    assert(TheBB && "Invalid BB");
  }
  // Incomplete constructor
  SPIRVVectorExtractDynamic():SPIRVInstruction(OC), VectorId(SPIRVID_INVALID),
      IndexId(SPIRVID_INVALID){}

  SPIRVValue *getVector() { return getValue(VectorId);}
  SPIRVValue *getIndex()const { return getValue(IndexId);}
protected:
  _SPIRV_DEF_ENCDEC4(Type, Id, VectorId, IndexId)
  void validate()const {
    SPIRVInstruction::validate();
    if (getValue(VectorId)->isForward())
      return;
    assert(getValueType(VectorId)->isTypeVector());
  }
  SPIRVId VectorId;
  SPIRVId IndexId;
};

class SPIRVVectorInsertDynamic :public SPIRVInstruction {
public:
  const static Op OC = OpVectorInsertDynamic;
  // Complete constructor
  SPIRVVectorInsertDynamic(SPIRVId TheId, SPIRVValue *TheVector,
      SPIRVValue* TheComponent, SPIRVValue* TheIndex, SPIRVBasicBlock *TheBB)
    :SPIRVInstruction(6, OC, TheVector->getType()->getVectorComponentType(),
    TheId, TheBB), VectorId(TheVector->getId()),
    IndexId(TheIndex->getId()), ComponentId(TheComponent->getId()){
    validate();
    assert(TheBB && "Invalid BB");
  }
  // Incomplete constructor
  SPIRVVectorInsertDynamic() :SPIRVInstruction(OC), VectorId(SPIRVID_INVALID),
    IndexId(SPIRVID_INVALID), ComponentId(SPIRVID_INVALID){}

  SPIRVValue *getVector() { return getValue(VectorId); }
  SPIRVValue *getIndex()const { return getValue(IndexId); }
  SPIRVValue *getComponent() { return getValue(ComponentId); }
protected:
  _SPIRV_DEF_ENCDEC5(Type, Id, VectorId, ComponentId, IndexId)
    void validate()const {
    SPIRVInstruction::validate();
    if (getValue(VectorId)->isForward())
      return;
    assert(getValueType(VectorId)->isTypeVector());
  }
  SPIRVId VectorId;
  SPIRVId IndexId;
  SPIRVId ComponentId;
};

class SPIRVVectorShuffle:public SPIRVInstruction {
public:
  const static Op OC = OpVectorShuffle;
  const static SPIRVWord FixedWordCount = 5;
  // Complete constructor
  SPIRVVectorShuffle(SPIRVId TheId, SPIRVType *TheType, SPIRVValue *TheVector1,
      SPIRVValue *TheVector2, const std::vector<SPIRVWord>& TheComponents,
      SPIRVBasicBlock *TheBB):
        SPIRVInstruction(TheComponents.size() + FixedWordCount, OC, TheType,
            TheId, TheBB),
        Vector1(TheVector1->getId()), Vector2(TheVector2->getId()),
        Components(TheComponents){
    validate();
    assert(TheBB && "Invalid BB");
  }
  // Incomplete constructor
  SPIRVVectorShuffle():SPIRVInstruction(OC), Vector1(SPIRVID_INVALID),
      Vector2(SPIRVID_INVALID){}

  SPIRVValue *getVector1() { return getValue(Vector1);}
  SPIRVValue *getVector2() { return getValue(Vector2);}
  const std::vector<SPIRVWord>& getComponents()const { return Components;}
protected:
  void setWordCount(SPIRVWord TheWordCount) {
    SPIRVEntry::setWordCount(TheWordCount);
    Components.resize(TheWordCount - FixedWordCount);
  }
  _SPIRV_DEF_ENCDEC5(Type, Id, Vector1, Vector2, Components)
  void validate()const {
    SPIRVInstruction::validate();
    assert(OpCode == OC);
    assert(WordCount == Components.size() + FixedWordCount);
    assert(Type->isTypeVector());
    assert(Type->getVectorComponentType() ==
        getValueType(Vector1)->getVectorComponentType());
    if (getValue(Vector1)->isForward() ||
        getValue(Vector2)->isForward())
      return;
    assert(getValueType(Vector1) == getValueType(Vector2));
    size_t CompCount = Type->getVectorComponentCount();
    (void) CompCount;
    assert(Components.size() == CompCount);
    assert(Components.size() > 1);
  }
  SPIRVId Vector1;
  SPIRVId Vector2;
  std::vector<SPIRVWord> Components;
};

class SPIRVControlBarrier:public SPIRVInstruction {
public:
  static const Op OC = OpControlBarrier;
  // Complete constructor
  SPIRVControlBarrier(SPIRVValue *TheScope,
      SPIRVValue *TheMemScope, SPIRVValue *TheMemSema,
      SPIRVBasicBlock *TheBB)
    :SPIRVInstruction(4, OC, TheBB), ExecScope(TheScope->getId()),
    MemScope(TheMemScope->getId()), MemSema(TheMemSema->getId()){
    validate();
    assert(TheBB && "Invalid BB");
  }
  // Incomplete constructor
  SPIRVControlBarrier():SPIRVInstruction(OC), ExecScope(ScopeInvocation) {
    setHasNoId();
    setHasNoType();
  }
  void setWordCount(SPIRVWord TheWordCount) {
    SPIRVEntry::setWordCount(TheWordCount);
  }
  SPIRVValue *getExecScope() const { return getValue(ExecScope); }
  SPIRVValue *getMemScope() const { return getValue(MemScope); }
  SPIRVValue *getMemSemantic() const { return getValue(MemSema); }
  std::vector<SPIRVValue *> getOperands() {
    std::vector<SPIRVId> Operands;
    Operands.push_back(ExecScope);
    Operands.push_back(MemScope);
    Operands.push_back(MemSema);
    return getValues(Operands);
  }
protected:
  _SPIRV_DEF_ENCDEC3(ExecScope, MemScope, MemSema)
  void validate()const {
    assert(OpCode == OC);
    assert(WordCount == 4);
    SPIRVInstruction::validate();
  }
  SPIRVId ExecScope;
  SPIRVId MemScope;
  SPIRVId MemSema;
};

class SPIRVGroupAsyncCopy:public SPIRVInstruction {
public:
  static const Op OC = OpGroupAsyncCopy;
  static const SPIRVWord WC = 9;
  // Complete constructor
  SPIRVGroupAsyncCopy(SPIRVValue *TheScope, SPIRVId TheId,
      SPIRVValue *TheDest, SPIRVValue *TheSrc, SPIRVValue *TheNumElems,
      SPIRVValue *TheStride, SPIRVValue *TheEvent, SPIRVBasicBlock *TheBB)
    :SPIRVInstruction(WC, OC, TheEvent->getType(), TheId, TheBB),
    ExecScope(TheScope->getId()), Destination(TheDest->getId()),
     Source(TheSrc->getId()), NumElements(TheNumElems->getId()),
     Stride(TheStride->getId()), Event(TheEvent->getId()){
    validate();
    assert(TheBB && "Invalid BB");
  }
  // Incomplete constructor
  SPIRVGroupAsyncCopy():SPIRVInstruction(OC), ExecScope(SPIRVID_INVALID),
      Destination(SPIRVID_INVALID), Source(SPIRVID_INVALID),
      NumElements(SPIRVID_INVALID), Stride(SPIRVID_INVALID),
      Event(SPIRVID_INVALID){
  }
  SPIRVValue *getExecScope() const { return getValue(ExecScope); }
  SPIRVValue *getDestination()const { return getValue(Destination);}
  SPIRVValue *getSource()const { return getValue(Source);}
  SPIRVValue *getNumElements()const { return getValue(NumElements);}
  SPIRVValue *getStride()const { return getValue(Stride);}
  SPIRVValue *getEvent()const { return getValue(Event);}
  std::vector<SPIRVValue *> getOperands() {
    std::vector<SPIRVId> Operands;
    Operands.push_back(Destination);
    Operands.push_back(Source);
    Operands.push_back(NumElements);
    Operands.push_back(Stride);
    Operands.push_back(Event);
    return getValues(Operands);
  }

protected:
  _SPIRV_DEF_ENCDEC8(Type, Id, ExecScope, Destination, Source, NumElements,
      Stride, Event)
  void validate()const {
    assert(OpCode == OC);
    assert(WordCount == WC);
    SPIRVInstruction::validate();
  }
  SPIRVId ExecScope;
  SPIRVId Destination;
  SPIRVId Source;
  SPIRVId NumElements;
  SPIRVId Stride;
  SPIRVId Event;
};

enum SPIRVOpKind {
  SPIRVOPK_Id,
  SPIRVOPK_Literal,
  SPIRVOPK_Count
};

class SPIRVDevEnqInstBase:public SPIRVInstTemplateBase {
public:
  SPIRVCapVec getRequiriedCapability() const {
    return getVec(CapabilityDeviceEnqueue);
  }
};

#define _SPIRV_OP(x, ...) \
  typedef SPIRVInstTemplate<SPIRVDevEnqInstBase, Op##x, __VA_ARGS__> \
      SPIRV##x;
// CL 2.0 enqueue kernel builtins
_SPIRV_OP(EnqueueMarker, true, 7)
_SPIRV_OP(EnqueueKernel, true, 13, true)
_SPIRV_OP(GetKernelNDrangeSubGroupCount, true, 8)
_SPIRV_OP(GetKernelNDrangeMaxSubGroupSize, true, 8)
_SPIRV_OP(GetKernelWorkGroupSize, true, 7)
_SPIRV_OP(GetKernelPreferredWorkGroupSizeMultiple, true, 7)
_SPIRV_OP(RetainEvent, false, 2)
_SPIRV_OP(ReleaseEvent, false, 2)
_SPIRV_OP(CreateUserEvent, true, 3)
_SPIRV_OP(IsValidEvent, true, 4)
_SPIRV_OP(SetUserEventStatus, false, 3)
_SPIRV_OP(CaptureEventProfilingInfo, false, 4)
_SPIRV_OP(GetDefaultQueue, true, 3)
_SPIRV_OP(BuildNDRange, true, 6)
#undef _SPIRV_OP

class SPIRVPipeInstBase:public SPIRVInstTemplateBase {
public:
  SPIRVCapVec getRequiriedCapability() const {
    return getVec(CapabilityPipes);
  }
};

#define _SPIRV_OP(x, ...) \
  typedef SPIRVInstTemplate<SPIRVPipeInstBase, Op##x, __VA_ARGS__> \
      SPIRV##x;
// CL 2.0 pipe builtins
_SPIRV_OP(ReadPipe, true, 7)
_SPIRV_OP(WritePipe, true, 7)
_SPIRV_OP(ReservedReadPipe, true, 9)
_SPIRV_OP(ReservedWritePipe, true, 9)
_SPIRV_OP(ReserveReadPipePackets, true, 7)
_SPIRV_OP(ReserveWritePipePackets, true, 7)
_SPIRV_OP(CommitReadPipe, false, 5)
_SPIRV_OP(CommitWritePipe, false, 5)
_SPIRV_OP(IsValidReserveId, true, 4)
_SPIRV_OP(GetNumPipePackets, true, 6)
_SPIRV_OP(GetMaxPipePackets, true, 6)
#undef _SPIRV_OP

class SPIRVPipeStorageInstBase :public SPIRVInstTemplateBase {
public:
  SPIRVCapVec getRequiriedCapability() const {
    return getVec(CapabilityPipeStorage, CapabilityPipes);
  }
};

#define _SPIRV_OP(x, ...) \
  typedef SPIRVInstTemplate<SPIRVPipeStorageInstBase, Op##x, __VA_ARGS__> \
  SPIRV##x;

_SPIRV_OP(CreatePipeFromPipeStorage, true, 4)
#undef _SPIRV_OP

class SPIRVGroupInstBase:public SPIRVInstTemplateBase {
public:
  SPIRVCapVec getRequiriedCapability() const {
    return getVec(CapabilityGroups);
  }
};

#define _SPIRV_OP(x, ...) \
  typedef SPIRVInstTemplate<SPIRVGroupInstBase, Op##x, __VA_ARGS__> \
      SPIRV##x;
// Group instructions
_SPIRV_OP(GroupWaitEvents, false, 4)
_SPIRV_OP(GroupAll, true, 5)
_SPIRV_OP(GroupAny, true, 5)
_SPIRV_OP(GroupBroadcast, true, 6)
_SPIRV_OP(GroupIAdd, true, 6, false, 1)
_SPIRV_OP(GroupFAdd, true, 6, false, 1)
_SPIRV_OP(GroupFMin, true, 6, false, 1)
_SPIRV_OP(GroupUMin, true, 6, false, 1)
_SPIRV_OP(GroupSMin, true, 6, false, 1)
_SPIRV_OP(GroupFMax, true, 6, false, 1)
_SPIRV_OP(GroupUMax, true, 6, false, 1)
_SPIRV_OP(GroupSMax, true, 6, false, 1)
_SPIRV_OP(GroupReserveReadPipePackets, true, 8)
_SPIRV_OP(GroupReserveWritePipePackets, true, 8)
_SPIRV_OP(GroupCommitReadPipe, false, 6)
_SPIRV_OP(GroupCommitWritePipe, false, 6)
#undef _SPIRV_OP

class SPIRVAtomicInstBase:public SPIRVInstTemplateBase {
public:
  SPIRVCapVec getRequiriedCapability() const {
    return getVec(CapabilityInt64Atomics);
  }
};

#define _SPIRV_OP(x, ...) \
  typedef SPIRVInstTemplate<SPIRVAtomicInstBase, Op##x, __VA_ARGS__> \
      SPIRV##x;
// Atomic builtins
_SPIRV_OP(AtomicFlagTestAndSet, true, 6)
_SPIRV_OP(AtomicFlagClear, false, 4)
_SPIRV_OP(AtomicLoad, true, 6)
_SPIRV_OP(AtomicStore, false, 5)
_SPIRV_OP(AtomicExchange, true, 7)
_SPIRV_OP(AtomicCompareExchange, true, 9)
_SPIRV_OP(AtomicCompareExchangeWeak, true, 9)
_SPIRV_OP(AtomicIIncrement, true, 6)
_SPIRV_OP(AtomicIDecrement, true, 6)
_SPIRV_OP(AtomicIAdd, true, 7)
_SPIRV_OP(AtomicISub, true, 7)
_SPIRV_OP(AtomicUMin, true, 7)
_SPIRV_OP(AtomicUMax, true, 7)
_SPIRV_OP(AtomicSMin, true, 7)
_SPIRV_OP(AtomicSMax, true, 7)
_SPIRV_OP(AtomicAnd, true, 7)
_SPIRV_OP(AtomicOr, true, 7)
_SPIRV_OP(AtomicXor, true, 7)
_SPIRV_OP(MemoryBarrier, false, 3)
#undef _SPIRV_OP

class SPIRVImageInstBase:public SPIRVInstTemplateBase {
public:
  SPIRVCapVec getRequiriedCapability() const {
    return getVec(CapabilityImageBasic);
  }
};

#define _SPIRV_OP(x, ...) \
  typedef SPIRVInstTemplate<SPIRVImageInstBase, Op##x, __VA_ARGS__> \
      SPIRV##x;
// Image instructions
_SPIRV_OP(SampledImage, true, 5)
_SPIRV_OP(ImageSampleImplicitLod, true, 5, true)
_SPIRV_OP(ImageSampleExplicitLod, true, 7, true, 2)
_SPIRV_OP(ImageRead, true, 5, true, 2)
_SPIRV_OP(ImageWrite, false, 4, true, 3)
_SPIRV_OP(ImageQueryFormat, true, 4)
_SPIRV_OP(ImageQueryOrder, true, 4)
_SPIRV_OP(ImageQuerySizeLod, true, 5)
_SPIRV_OP(ImageQuerySize, true, 4)
_SPIRV_OP(ImageQueryLod, true, 5)
_SPIRV_OP(ImageQueryLevels, true, 4)
_SPIRV_OP(ImageQuerySamples, true, 4)
#undef _SPIRV_OP

#define _SPIRV_OP(x, ...) \
  typedef SPIRVInstTemplate<SPIRVInstTemplateBase, Op##x, __VA_ARGS__> \
      SPIRV##x;
// Other instructions
_SPIRV_OP(SpecConstantOp, true, 4, true, 0)
_SPIRV_OP(GenericPtrMemSemantics, true, 4, false)
_SPIRV_OP(GenericCastToPtrExplicit, true, 5, false, 1)
#undef _SPIRV_OP

SPIRVSpecConstantOp *createSpecConstantOpInst(SPIRVInstruction *Inst);
SPIRVInstruction *createInstFromSpecConstantOp(SPIRVSpecConstantOp *C);
}

#endif // SPIRVINSTRUCTION_HPP_