#include <cstring>

#include "spirv_module.h"

namespace dxvk {
  
  SpirvModule::SpirvModule(uint32_t version)
  : m_version(version) {
    this->instImportGlsl450();
  }
  
  
  SpirvModule::~SpirvModule() {
    
  }
  
  
  SpirvCodeBuffer SpirvModule::compile() const {
    SpirvCodeBuffer result;
    result.putHeader(m_version, m_id);
    result.append(m_capabilities);
    result.append(m_extensions);
    result.append(m_instExt);
    result.append(m_memoryModel);
    result.append(m_entryPoints);
    result.append(m_execModeInfo);
    result.append(m_debugNames);
    result.append(m_annotations);
    result.append(m_typeConstDefs);
    result.append(m_variables);
    result.append(m_code);
    return result;
  }
  
  
  uint32_t SpirvModule::allocateId() {
    return m_id++;
  }
  
  
  void SpirvModule::enableCapability(
          spv::Capability         capability) {
    // Scan the generated instructions to check
    // whether we already enabled the capability.
    for (auto ins : m_capabilities) {
      if (ins.opCode() == spv::OpCapability && ins.arg(1) == capability)
        return;
    }
    
    m_capabilities.putIns (spv::OpCapability, 2);
    m_capabilities.putWord(capability);
  }
  
  
  void SpirvModule::enableExtension(
    const char*                   extensionName) {
    m_extensions.putIns (spv::OpExtension, 1 + m_extensions.strLen(extensionName));
    m_extensions.putStr (extensionName);
  }
  
  
  void SpirvModule::addEntryPoint(
          uint32_t                entryPointId,
          spv::ExecutionModel     executionModel,
    const char*                   name,
          uint32_t                interfaceCount,
    const uint32_t*               interfaceIds) {
    m_entryPoints.putIns  (spv::OpEntryPoint, 3 + m_entryPoints.strLen(name) + interfaceCount);
    m_entryPoints.putWord (executionModel);
    m_entryPoints.putWord (entryPointId);
    m_entryPoints.putStr  (name);
    
    for (uint32_t i = 0; i < interfaceCount; i++)
      m_entryPoints.putWord(interfaceIds[i]);
  }
  
  
  void SpirvModule::setMemoryModel(
          spv::AddressingModel    addressModel,
          spv::MemoryModel        memoryModel) {
    m_memoryModel.putIns  (spv::OpMemoryModel, 3);
    m_memoryModel.putWord (addressModel);
    m_memoryModel.putWord (memoryModel);
  }
  
    
  void SpirvModule::setExecutionMode(
          uint32_t                entryPointId,
          spv::ExecutionMode      executionMode) {
    m_execModeInfo.putIns (spv::OpExecutionMode, 3);
    m_execModeInfo.putWord(entryPointId);
    m_execModeInfo.putWord(executionMode);
  }
  
  
  void SpirvModule::setInvocations(
          uint32_t                entryPointId,
          uint32_t                invocations) {
    m_execModeInfo.putIns  (spv::OpExecutionMode, 4);
    m_execModeInfo.putWord (entryPointId);
    m_execModeInfo.putWord (spv::ExecutionModeInvocations);
    m_execModeInfo.putInt32(invocations);
  }
  
  
  void SpirvModule::setLocalSize(
          uint32_t                entryPointId,
          uint32_t                x,
          uint32_t                y,
          uint32_t                z) {
    m_execModeInfo.putIns  (spv::OpExecutionMode, 6);
    m_execModeInfo.putWord (entryPointId);
    m_execModeInfo.putWord (spv::ExecutionModeLocalSize);
    m_execModeInfo.putInt32(x);
    m_execModeInfo.putInt32(y);
    m_execModeInfo.putInt32(z);
  }
  
  
  void SpirvModule::setOutputVertices(
          uint32_t                entryPointId,
          uint32_t                vertexCount) {
    m_execModeInfo.putIns (spv::OpExecutionMode, 4);
    m_execModeInfo.putWord(entryPointId);
    m_execModeInfo.putWord(spv::ExecutionModeOutputVertices);
    m_execModeInfo.putWord(vertexCount);
  }
  
  
  uint32_t SpirvModule::addDebugString(
    const char*                   string) {
    uint32_t resultId = this->allocateId();
    
    m_debugNames.putIns (spv::OpString,
      2 + m_debugNames.strLen(string));
    m_debugNames.putWord(resultId);
    m_debugNames.putStr (string);
    return resultId;
  }
  
  
  void SpirvModule::setDebugSource(
          spv::SourceLanguage     language,
          uint32_t                version,
          uint32_t                file,
    const char*                   source) {
    uint32_t strLen = source != nullptr
      ? m_debugNames.strLen(source) : 0;
    
    m_debugNames.putIns (spv::OpSource, 4 + strLen);
    m_debugNames.putWord(language);
    m_debugNames.putWord(version);
    m_debugNames.putWord(file);
    
    if (source != nullptr)
      m_debugNames.putStr(source);
  }
  
  void SpirvModule::setDebugName(
          uint32_t                expressionId,
    const char*                   debugName) {
    m_debugNames.putIns (spv::OpName, 2 + m_debugNames.strLen(debugName));
    m_debugNames.putWord(expressionId);
    m_debugNames.putStr (debugName);
  }
  
  
  void SpirvModule::setDebugMemberName(
          uint32_t                structId,
          uint32_t                memberId,
    const char*                   debugName) {
    m_debugNames.putIns (spv::OpMemberName, 3 + m_debugNames.strLen(debugName));
    m_debugNames.putWord(structId);
    m_debugNames.putWord(memberId);
    m_debugNames.putStr (debugName);
  }
  
  
  uint32_t SpirvModule::constBool(
          bool                    v) {
    return this->defConst(v
        ? spv::OpConstantTrue
        : spv::OpConstantFalse,
      this->defBoolType(),
      0, nullptr);
  }
  
  
  uint32_t SpirvModule::consti32(
          int32_t                 v) {
    std::array<uint32_t, 1> data;
    std::memcpy(data.data(), &v, sizeof(v));
    
    return this->defConst(
      spv::OpConstant,
      this->defIntType(32, 1),
      data.size(),
      data.data());
  }
  
  
  uint32_t SpirvModule::consti64(
          int64_t                 v) {
    std::array<uint32_t, 2> data;
    std::memcpy(data.data(), &v, sizeof(v));
    
    return this->defConst(
      spv::OpConstant,
      this->defIntType(64, 1),
      data.size(),
      data.data());
  }
  
  
  uint32_t SpirvModule::constu32(
          uint32_t                v) {
    std::array<uint32_t, 1> data;
    std::memcpy(data.data(), &v, sizeof(v));
    
    return this->defConst(
      spv::OpConstant,
      this->defIntType(32, 0),
      data.size(),
      data.data());
  }
  
  
  uint32_t SpirvModule::constu64(
          uint64_t                v) {
    std::array<uint32_t, 2> data;
    std::memcpy(data.data(), &v, sizeof(v));
    
    return this->defConst(
      spv::OpConstant,
      this->defIntType(64, 0),
      data.size(),
      data.data());
  }
  
  
  uint32_t SpirvModule::constf32(
          float                   v) {
    std::array<uint32_t, 1> data;
    std::memcpy(data.data(), &v, sizeof(v));
    
    return this->defConst(
      spv::OpConstant,
      this->defFloatType(32),
      data.size(),
      data.data());
  }
  
  
  uint32_t SpirvModule::constf64(
          double                  v) {
    std::array<uint32_t, 2> data;
    std::memcpy(data.data(), &v, sizeof(v));
    
    return this->defConst(
      spv::OpConstant,
      this->defFloatType(64),
      data.size(),
      data.data());
  }
  
  
  uint32_t SpirvModule::constvec4i32(
          int32_t                 x,
          int32_t                 y,
          int32_t                 z,
          int32_t                 w) {
    std::array<uint32_t, 4> args = {{
      this->consti32(x), this->consti32(y),
      this->consti32(z), this->consti32(w),
    }};
    
    uint32_t scalarTypeId = this->defIntType(32, 1);
    uint32_t vectorTypeId = this->defVectorType(scalarTypeId, 4);
    
    return this->constComposite(vectorTypeId, args.size(), args.data());
  }


  uint32_t SpirvModule::constvec4b32(
          bool                    x,
          bool                    y,
          bool                    z,
          bool                    w) {
    std::array<uint32_t, 4> args = {{
      this->constBool(x), this->constBool(y),
      this->constBool(z), this->constBool(w),
    }};
    
    uint32_t scalarTypeId = this->defBoolType();
    uint32_t vectorTypeId = this->defVectorType(scalarTypeId, 4);
    
    return this->constComposite(vectorTypeId, args.size(), args.data());
  }
  
  
  uint32_t SpirvModule::constvec4u32(
          uint32_t                x,
          uint32_t                y,
          uint32_t                z,
          uint32_t                w) {
    std::array<uint32_t, 4> args = {{
      this->constu32(x), this->constu32(y),
      this->constu32(z), this->constu32(w),
    }};
    
    uint32_t scalarTypeId = this->defIntType(32, 0);
    uint32_t vectorTypeId = this->defVectorType(scalarTypeId, 4);
    
    return this->constComposite(vectorTypeId, args.size(), args.data());
  }
  

  uint32_t SpirvModule::constvec2f32(
          float                   x,
          float                   y) {
    std::array<uint32_t, 2> args = {{
      this->constf32(x), this->constf32(y),
    }};
    
    uint32_t scalarTypeId = this->defFloatType(32);
    uint32_t vectorTypeId = this->defVectorType(scalarTypeId, 2);
    
    return this->constComposite(vectorTypeId, args.size(), args.data());
  }
  

  uint32_t SpirvModule::constvec3f32(
          float                   x,
          float                   y,
          float                   z) {
    std::array<uint32_t, 3> args = {{
      this->constf32(x), this->constf32(y),
      this->constf32(z),
    }};
    
    uint32_t scalarTypeId = this->defFloatType(32);
    uint32_t vectorTypeId = this->defVectorType(scalarTypeId, 3);
    
    return this->constComposite(vectorTypeId, args.size(), args.data());
  }

  
  uint32_t SpirvModule::constvec4f32(
          float                   x,
          float                   y,
          float                   z,
          float                   w) {
    std::array<uint32_t, 4> args = {{
      this->constf32(x), this->constf32(y),
      this->constf32(z), this->constf32(w),
    }};
    
    uint32_t scalarTypeId = this->defFloatType(32);
    uint32_t vectorTypeId = this->defVectorType(scalarTypeId, 4);
    
    return this->constComposite(vectorTypeId, args.size(), args.data());
  }


  uint32_t SpirvModule::constfReplicant(
          float                   replicant,
          uint32_t                count) {
    uint32_t value = this->constf32(replicant);

    std::array<uint32_t, 4> args = { value, value, value, value };

    // Can't make a scalar composite.
    if (count == 1)
      return args[0];
    
    uint32_t scalarTypeId = this->defFloatType(32);
    uint32_t vectorTypeId = this->defVectorType(scalarTypeId, count);
    
    return this->constComposite(vectorTypeId, count, args.data());
  }


  uint32_t SpirvModule::constbReplicant(
          bool                    replicant,
          uint32_t                count) {
    uint32_t value = this->constBool(replicant);

    std::array<uint32_t, 4> args = { value, value, value, value };

    // Can't make a scalar composite.
    if (count == 1)
      return args[0];
    
    uint32_t scalarTypeId = this->defBoolType();
    uint32_t vectorTypeId = this->defVectorType(scalarTypeId, count);
    
    return this->constComposite(vectorTypeId, count, args.data());
  }


  uint32_t SpirvModule::constiReplicant(
          int32_t                 replicant,
          uint32_t                count) {
    uint32_t value = this->consti32(replicant);

    std::array<uint32_t, 4> args = { value, value, value, value };

    // Can't make a scalar composite.
    if (count == 1)
      return args[0];
    
    uint32_t scalarTypeId = this->defIntType(32, 1);
    uint32_t vectorTypeId = this->defVectorType(scalarTypeId, count);
    
    return this->constComposite(vectorTypeId, count, args.data());
  }


  uint32_t SpirvModule::constuReplicant(
          int32_t                 replicant,
          uint32_t                count) {
    uint32_t value = this->constu32(replicant);

    std::array<uint32_t, 4> args = { value, value, value, value };

    // Can't make a scalar composite.
    if (count == 1)
      return args[0];
    
    uint32_t scalarTypeId = this->defIntType(32, 0);
    uint32_t vectorTypeId = this->defVectorType(scalarTypeId, count);
    
    return this->constComposite(vectorTypeId, count, args.data());
  }
  
  
  uint32_t SpirvModule::constComposite(
          uint32_t                typeId,
          uint32_t                constCount,
    const uint32_t*               constIds) {
    return this->defConst(
      spv::OpConstantComposite,
      typeId, constCount, constIds);
  }
  
  
  uint32_t SpirvModule::constUndef(
          uint32_t                typeId) {
    return this->defConst(spv::OpUndef,
      typeId, 0, nullptr);
  }


  uint32_t SpirvModule::lateConst32(
          uint32_t                typeId) {
    uint32_t resultId = this->allocateId();
    m_lateConsts.insert(resultId);

    m_typeConstDefs.putIns (spv::OpConstant, 4);
    m_typeConstDefs.putWord(typeId);
    m_typeConstDefs.putWord(resultId);
    m_typeConstDefs.putWord(0);
    return resultId;
  }


  void SpirvModule::setLateConst(
            uint32_t                constId,
      const uint32_t*               argIds) {
    for (auto ins : m_typeConstDefs) {
      if (ins.opCode() != spv::OpConstant
       && ins.opCode() != spv::OpConstantComposite)
        continue;
      
      if (ins.arg(2) != constId)
        continue;

      for (uint32_t i = 3; i < ins.length(); i++)
        ins.setArg(i, argIds[i - 3]);

      return;
    }
  }


  uint32_t SpirvModule::specConstBool(
          bool                    v) {
    uint32_t typeId   = this->defBoolType();
    uint32_t resultId = this->allocateId();
    
    const spv::Op op = v
      ? spv::OpSpecConstantTrue
      : spv::OpSpecConstantFalse;
    
    m_typeConstDefs.putIns  (op, 3);
    m_typeConstDefs.putWord (typeId);
    m_typeConstDefs.putWord (resultId);
    return resultId;
  }
    
  
  uint32_t SpirvModule::specConst32(
          uint32_t                typeId,
          uint32_t                value) {
    uint32_t resultId = this->allocateId();
    
    m_typeConstDefs.putIns  (spv::OpSpecConstant, 4);
    m_typeConstDefs.putWord (typeId);
    m_typeConstDefs.putWord (resultId);
    m_typeConstDefs.putWord (value);
    return resultId;
  }
  
  
  void SpirvModule::decorate(
          uint32_t                object,
          spv::Decoration         decoration) {
    m_annotations.putIns  (spv::OpDecorate, 3);
    m_annotations.putWord (object);
    m_annotations.putWord (decoration);
  }
  
  
  void SpirvModule::decorateArrayStride(
          uint32_t                object,
          uint32_t                stride) {
    m_annotations.putIns  (spv::OpDecorate, 4);
    m_annotations.putWord (object);
    m_annotations.putWord (spv::DecorationArrayStride);
    m_annotations.putInt32(stride);
  }
  
  
  void SpirvModule::decorateBinding(
          uint32_t                object,
          uint32_t                binding) {
    m_annotations.putIns  (spv::OpDecorate, 4);
    m_annotations.putWord (object);
    m_annotations.putWord (spv::DecorationBinding);
    m_annotations.putInt32(binding);
  }
  
  
  void SpirvModule::decorateBlock(uint32_t object) {
    m_annotations.putIns  (spv::OpDecorate, 3);
    m_annotations.putWord (object);
    m_annotations.putWord (spv::DecorationBlock);
  }
  
  
  void SpirvModule::decorateBuiltIn(
          uint32_t                object,
          spv::BuiltIn            builtIn) {
    m_annotations.putIns  (spv::OpDecorate, 4);
    m_annotations.putWord (object);
    m_annotations.putWord (spv::DecorationBuiltIn);
    m_annotations.putWord (builtIn);
  }
  
  
  void SpirvModule::decorateComponent(
          uint32_t                object,
          uint32_t                location) {
    m_annotations.putIns  (spv::OpDecorate, 4);
    m_annotations.putWord (object);
    m_annotations.putWord (spv::DecorationComponent);
    m_annotations.putInt32(location);
  }
  
  
  void SpirvModule::decorateDescriptorSet(
          uint32_t                object,
          uint32_t                set) {
    m_annotations.putIns  (spv::OpDecorate, 4);
    m_annotations.putWord (object);
    m_annotations.putWord (spv::DecorationDescriptorSet);
    m_annotations.putInt32(set);
  }
  
  
  void SpirvModule::decorateIndex(
          uint32_t                object,
          uint32_t                index) {
    m_annotations.putIns  (spv::OpDecorate, 4);
    m_annotations.putWord (object);
    m_annotations.putWord (spv::DecorationIndex);
    m_annotations.putInt32(index);
  }


  void SpirvModule::decorateLocation(
          uint32_t                object,
          uint32_t                location) {
    m_annotations.putIns  (spv::OpDecorate, 4);
    m_annotations.putWord (object);
    m_annotations.putWord (spv::DecorationLocation);
    m_annotations.putInt32(location);
  }
  
  
  void SpirvModule::decorateSpecId(
          uint32_t                object,
          uint32_t                specId) {
    m_annotations.putIns  (spv::OpDecorate, 4);
    m_annotations.putWord (object);
    m_annotations.putWord (spv::DecorationSpecId);
    m_annotations.putInt32(specId);
  }
  

  void SpirvModule::decorateXfb(
          uint32_t                object,
          uint32_t                streamId,
          uint32_t                bufferId,
          uint32_t                offset,
          uint32_t                stride) {
    m_annotations.putIns  (spv::OpDecorate, 4);
    m_annotations.putWord (object);
    m_annotations.putWord (spv::DecorationStream);
    m_annotations.putInt32(streamId);

    m_annotations.putIns  (spv::OpDecorate, 4);
    m_annotations.putWord (object);
    m_annotations.putWord (spv::DecorationXfbBuffer);
    m_annotations.putInt32(bufferId);

    m_annotations.putIns  (spv::OpDecorate, 4);
    m_annotations.putWord (object);
    m_annotations.putWord (spv::DecorationXfbStride);
    m_annotations.putInt32(stride);

    m_annotations.putIns  (spv::OpDecorate, 4);
    m_annotations.putWord (object);
    m_annotations.putWord (spv::DecorationOffset);
    m_annotations.putInt32(offset);
  }
  
  
  void SpirvModule::memberDecorateBuiltIn(
          uint32_t                structId,
          uint32_t                memberId,
          spv::BuiltIn            builtIn) {
    m_annotations.putIns  (spv::OpMemberDecorate, 5);
    m_annotations.putWord (structId);
    m_annotations.putWord (memberId);
    m_annotations.putWord (spv::DecorationBuiltIn);
    m_annotations.putWord (builtIn);
  }


  void SpirvModule::memberDecorate(
          uint32_t                structId,
          uint32_t                memberId,
          spv::Decoration         decoration) {
    m_annotations.putIns  (spv::OpMemberDecorate, 4);
    m_annotations.putWord (structId);
    m_annotations.putWord (memberId);
    m_annotations.putWord (decoration);
  }


  void SpirvModule::memberDecorateMatrixStride(
          uint32_t                structId,
          uint32_t                memberId,
          uint32_t                stride) {
    m_annotations.putIns  (spv::OpMemberDecorate, 5);
    m_annotations.putWord (structId);
    m_annotations.putWord (memberId);
    m_annotations.putWord (spv::DecorationMatrixStride);
    m_annotations.putWord (stride);
  }
  
  
  void SpirvModule::memberDecorateOffset(
          uint32_t                structId,
          uint32_t                memberId,
          uint32_t                offset) {
    m_annotations.putIns  (spv::OpMemberDecorate, 5);
    m_annotations.putWord (structId);
    m_annotations.putWord (memberId);
    m_annotations.putWord (spv::DecorationOffset);
    m_annotations.putWord (offset);
  }
  
  
  uint32_t SpirvModule::defVoidType() {
    return this->defType(spv::OpTypeVoid, 0, nullptr);
  }
  
  
  uint32_t SpirvModule::defBoolType() {
    return this->defType(spv::OpTypeBool, 0, nullptr);
  }
  
  
  uint32_t SpirvModule::defIntType(
          uint32_t                width,
          uint32_t                isSigned) {
    std::array<uint32_t, 2> args = {{ width, isSigned }};
    return this->defType(spv::OpTypeInt,
      args.size(), args.data());
  }
  
  
  uint32_t SpirvModule::defFloatType(
          uint32_t                width) {
    std::array<uint32_t, 1> args = {{ width }};
    return this->defType(spv::OpTypeFloat,
      args.size(), args.data());
  }
  
  
  uint32_t SpirvModule::defVectorType(
          uint32_t                elementType,
          uint32_t                elementCount) {
    std::array<uint32_t, 2> args =
      {{ elementType, elementCount }};
    
    return this->defType(spv::OpTypeVector,
      args.size(), args.data());
  }
  
  
  uint32_t SpirvModule::defMatrixType(
          uint32_t                columnType,
          uint32_t                columnCount) {
    std::array<uint32_t, 2> args =
      {{ columnType, columnCount }};
    
    return this->defType(spv::OpTypeMatrix,
      args.size(), args.data());
  }
  
  
  uint32_t SpirvModule::defArrayType(
          uint32_t                typeId,
          uint32_t                length) {
    std::array<uint32_t, 2> args = {{ typeId, length }};
    
    return this->defType(spv::OpTypeArray,
      args.size(), args.data());
  }
  
  
  uint32_t SpirvModule::defArrayTypeUnique(
          uint32_t                typeId,
          uint32_t                length) {
    uint32_t resultId = this->allocateId();
    
    m_typeConstDefs.putIns (spv::OpTypeArray, 4);
    m_typeConstDefs.putWord(resultId);
    m_typeConstDefs.putWord(typeId);
    m_typeConstDefs.putWord(length);
    return resultId;
  }
  
  
  uint32_t SpirvModule::defRuntimeArrayType(
          uint32_t                typeId) {
    std::array<uint32_t, 1> args = { typeId };
    
    return this->defType(spv::OpTypeRuntimeArray,
      args.size(), args.data());
  }
  
  
  uint32_t SpirvModule::defRuntimeArrayTypeUnique(
          uint32_t                typeId) {
    uint32_t resultId = this->allocateId();
    
    m_typeConstDefs.putIns (spv::OpTypeRuntimeArray, 3);
    m_typeConstDefs.putWord(resultId);
    m_typeConstDefs.putWord(typeId);
    return resultId;
  }
  
  
  uint32_t SpirvModule::defFunctionType(
          uint32_t                returnType,
          uint32_t                argCount,
    const uint32_t*               argTypes) {
    std::vector<uint32_t> args;
    args.push_back(returnType);
    
    for (uint32_t i = 0; i < argCount; i++)
      args.push_back(argTypes[i]);
    
    return this->defType(spv::OpTypeFunction,
      args.size(), args.data());
  }
  
  
  uint32_t SpirvModule::defStructType(
          uint32_t                memberCount,
    const uint32_t*               memberTypes) {
    return this->defType(spv::OpTypeStruct,
      memberCount, memberTypes);
  }
  
  
  uint32_t SpirvModule::defStructTypeUnique(
          uint32_t                memberCount,
    const uint32_t*               memberTypes) {
    uint32_t resultId = this->allocateId();
    
    m_typeConstDefs.putIns (spv::OpTypeStruct, 2 + memberCount);
    m_typeConstDefs.putWord(resultId);
    
    for (uint32_t i = 0; i < memberCount; i++)
      m_typeConstDefs.putWord(memberTypes[i]);
    return resultId;
  }
  
  
  uint32_t SpirvModule::defPointerType(
          uint32_t                variableType,
          spv::StorageClass       storageClass) {
    std::array<uint32_t, 2> args = {{
      static_cast<uint32_t>(storageClass),
      variableType,
    }};
    
    return this->defType(spv::OpTypePointer,
      args.size(), args.data());
  }
  
  
  uint32_t SpirvModule::defSamplerType() {
    return this->defType(spv::OpTypeSampler, 0, nullptr);
  }
  
  
  uint32_t SpirvModule::defImageType(
          uint32_t                sampledType,
          spv::Dim                dimensionality,
          uint32_t                depth,
          uint32_t                arrayed,
          uint32_t                multisample,
          uint32_t                sampled,
          spv::ImageFormat        format) {
    std::array<uint32_t, 7> args = {{
      sampledType,
    static_cast<uint32_t>(dimensionality),
      depth, arrayed,
      multisample,
      sampled,
    static_cast<uint32_t>(format)
    }};
    
    return this->defType(spv::OpTypeImage,
      args.size(), args.data());
  }
  
  
  uint32_t SpirvModule::defSampledImageType(
          uint32_t                imageType) {
    return this->defType(spv::OpTypeSampledImage, 1, &imageType);
  }
  
  
  uint32_t SpirvModule::newVar(
          uint32_t                pointerType,
          spv::StorageClass       storageClass) {
    uint32_t resultId = this->allocateId();
    
    auto& code = storageClass != spv::StorageClassFunction
      ? m_variables : m_code;
    
    code.putIns  (spv::OpVariable, 4);
    code.putWord (pointerType);
    code.putWord (resultId);
    code.putWord (storageClass);
    return resultId;
  }
  
  
  uint32_t SpirvModule::newVarInit(
          uint32_t                pointerType,
          spv::StorageClass       storageClass,
          uint32_t                initialValue) {
    uint32_t resultId = this->allocateId();
    
    auto& code = storageClass != spv::StorageClassFunction
      ? m_variables : m_code;
    
    code.putIns  (spv::OpVariable, 5);
    code.putWord (pointerType);
    code.putWord (resultId);
    code.putWord (storageClass);
    code.putWord (initialValue);
    return resultId;
  }
  
  
  void SpirvModule::functionBegin(
          uint32_t                returnType,
          uint32_t                functionId,
          uint32_t                functionType,
    spv::FunctionControlMask      functionControl) {
    m_code.putIns (spv::OpFunction, 5);
    m_code.putWord(returnType);
    m_code.putWord(functionId);
    m_code.putWord(functionControl);
    m_code.putWord(functionType);
  }
  
  
  uint32_t SpirvModule::functionParameter(
          uint32_t                parameterType) {
    uint32_t parameterId = this->allocateId();
    
    m_code.putIns (spv::OpFunctionParameter, 3);
    m_code.putWord(parameterType);
    m_code.putWord(parameterId);
    return parameterId;
  }
  
  
  void SpirvModule::functionEnd() {
    m_code.putIns (spv::OpFunctionEnd, 1);
  }


  uint32_t SpirvModule::opAccessChain(
          uint32_t                resultType,
          uint32_t                composite,
          uint32_t                indexCount,
    const uint32_t*               indexArray) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpAccessChain, 4 + indexCount);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(composite);
    
    for (uint32_t i = 0; i < indexCount; i++)
      m_code.putInt32(indexArray[i]);
    return resultId;
  }


  uint32_t SpirvModule::opArrayLength(
          uint32_t                resultType,
          uint32_t                structure,
          uint32_t                memberId) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpArrayLength, 5);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(structure);
    m_code.putWord(memberId);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opAny(
          uint32_t                resultType,
          uint32_t                vector) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpAny, 4);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(vector);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opAll(
          uint32_t                resultType,
          uint32_t                vector) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpAll, 4);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(vector);
    return resultId;
  }
  
    
  uint32_t SpirvModule::opAtomicLoad(
          uint32_t                resultType,
          uint32_t                pointer,
          uint32_t                scope,
          uint32_t                semantics) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpAtomicLoad, 6);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(pointer);
    m_code.putWord(scope);
    m_code.putWord(semantics);
    return resultId;
  }
  
  
  void SpirvModule::opAtomicStore(
          uint32_t                pointer,
          uint32_t                scope,
          uint32_t                semantics,
          uint32_t                value) {
    m_code.putIns (spv::OpAtomicStore, 5);
    m_code.putWord(pointer);
    m_code.putWord(scope);
    m_code.putWord(semantics);
    m_code.putWord(value);
  }
  
  
  uint32_t SpirvModule::opAtomicExchange(
          uint32_t                resultType,
          uint32_t                pointer,
          uint32_t                scope,
          uint32_t                semantics,
          uint32_t                value) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpAtomicExchange, 7);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(pointer);
    m_code.putWord(scope);
    m_code.putWord(semantics);
    m_code.putWord(value);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opAtomicCompareExchange(
          uint32_t                resultType,
          uint32_t                pointer,
          uint32_t                scope,
          uint32_t                equal,
          uint32_t                unequal,
          uint32_t                value,
          uint32_t                comparator) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpAtomicCompareExchange, 9);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(pointer);
    m_code.putWord(scope);
    m_code.putWord(equal);
    m_code.putWord(unequal);
    m_code.putWord(value);
    m_code.putWord(comparator);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opAtomicIIncrement(
          uint32_t                resultType,
          uint32_t                pointer,
          uint32_t                scope,
          uint32_t                semantics) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpAtomicIIncrement, 6);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(pointer);
    m_code.putWord(scope);
    m_code.putWord(semantics);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opAtomicIDecrement(
          uint32_t                resultType,
          uint32_t                pointer,
          uint32_t                scope,
          uint32_t                semantics) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpAtomicIDecrement, 6);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(pointer);
    m_code.putWord(scope);
    m_code.putWord(semantics);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opAtomicIAdd(
          uint32_t                resultType,
          uint32_t                pointer,
          uint32_t                scope,
          uint32_t                semantics,
          uint32_t                value) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpAtomicIAdd, 7);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(pointer);
    m_code.putWord(scope);
    m_code.putWord(semantics);
    m_code.putWord(value);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opAtomicISub(
          uint32_t                resultType,
          uint32_t                pointer,
          uint32_t                scope,
          uint32_t                semantics,
          uint32_t                value) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpAtomicISub, 7);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(pointer);
    m_code.putWord(scope);
    m_code.putWord(semantics);
    m_code.putWord(value);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opAtomicSMin(
          uint32_t                resultType,
          uint32_t                pointer,
          uint32_t                scope,
          uint32_t                semantics,
          uint32_t                value) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpAtomicSMin, 7);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(pointer);
    m_code.putWord(scope);
    m_code.putWord(semantics);
    m_code.putWord(value);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opAtomicSMax(
          uint32_t                resultType,
          uint32_t                pointer,
          uint32_t                scope,
          uint32_t                semantics,
          uint32_t                value) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpAtomicSMax, 7);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(pointer);
    m_code.putWord(scope);
    m_code.putWord(semantics);
    m_code.putWord(value);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opAtomicUMin(
          uint32_t                resultType,
          uint32_t                pointer,
          uint32_t                scope,
          uint32_t                semantics,
          uint32_t                value) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpAtomicUMin, 7);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(pointer);
    m_code.putWord(scope);
    m_code.putWord(semantics);
    m_code.putWord(value);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opAtomicUMax(
          uint32_t                resultType,
          uint32_t                pointer,
          uint32_t                scope,
          uint32_t                semantics,
          uint32_t                value) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpAtomicUMax, 7);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(pointer);
    m_code.putWord(scope);
    m_code.putWord(semantics);
    m_code.putWord(value);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opAtomicAnd(
          uint32_t                resultType,
          uint32_t                pointer,
          uint32_t                scope,
          uint32_t                semantics,
          uint32_t                value) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpAtomicAnd, 7);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(pointer);
    m_code.putWord(scope);
    m_code.putWord(semantics);
    m_code.putWord(value);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opAtomicOr(
          uint32_t                resultType,
          uint32_t                pointer,
          uint32_t                scope,
          uint32_t                semantics,
          uint32_t                value) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpAtomicOr, 7);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(pointer);
    m_code.putWord(scope);
    m_code.putWord(semantics);
    m_code.putWord(value);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opAtomicXor(
          uint32_t                resultType,
          uint32_t                pointer,
          uint32_t                scope,
          uint32_t                semantics,
          uint32_t                value) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpAtomicXor, 7);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(pointer);
    m_code.putWord(scope);
    m_code.putWord(semantics);
    m_code.putWord(value);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opBitcast(
          uint32_t                resultType,
          uint32_t                operand) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpBitcast, 4);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(operand);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opBitCount(
          uint32_t                resultType,
          uint32_t                operand) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpBitCount, 4);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(operand);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opBitReverse(
          uint32_t                resultType,
          uint32_t                operand) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpBitReverse, 4);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(operand);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opFindILsb(
          uint32_t                resultType,
          uint32_t                operand) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpExtInst, 6);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(m_instExtGlsl450);
    m_code.putWord(spv::GLSLstd450FindILsb);
    m_code.putWord(operand);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opFindUMsb(
          uint32_t                resultType,
          uint32_t                operand) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpExtInst, 6);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(m_instExtGlsl450);
    m_code.putWord(spv::GLSLstd450FindUMsb);
    m_code.putWord(operand);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opFindSMsb(
          uint32_t                resultType,
          uint32_t                operand) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpExtInst, 6);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(m_instExtGlsl450);
    m_code.putWord(spv::GLSLstd450FindSMsb);
    m_code.putWord(operand);
    return resultId;
  }
  
            
  uint32_t SpirvModule::opBitFieldInsert(
          uint32_t                resultType,
          uint32_t                base,
          uint32_t                insert,
          uint32_t                offset,
          uint32_t                count) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpBitFieldInsert, 7);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(base);
    m_code.putWord(insert);
    m_code.putWord(offset);
    m_code.putWord(count);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opBitFieldSExtract(
          uint32_t                resultType,
          uint32_t                base,
          uint32_t                offset,
          uint32_t                count) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpBitFieldSExtract, 6);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(base);
    m_code.putWord(offset);
    m_code.putWord(count);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opBitFieldUExtract(
          uint32_t                resultType,
          uint32_t                base,
          uint32_t                offset,
          uint32_t                count) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpBitFieldUExtract, 6);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(base);
    m_code.putWord(offset);
    m_code.putWord(count);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opBitwiseAnd(
          uint32_t                resultType,
          uint32_t                operand1,
          uint32_t                operand2) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpBitwiseAnd, 5);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(operand1);
    m_code.putWord(operand2);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opBitwiseOr(
          uint32_t                resultType,
          uint32_t                operand1,
          uint32_t                operand2) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpBitwiseOr, 5);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(operand1);
    m_code.putWord(operand2);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opBitwiseXor(
          uint32_t                resultType,
          uint32_t                operand1,
          uint32_t                operand2) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpBitwiseXor, 5);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(operand1);
    m_code.putWord(operand2);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opNot(
          uint32_t                resultType,
          uint32_t                operand) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpNot, 4);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(operand);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opShiftLeftLogical(
          uint32_t                resultType,
          uint32_t                base,
          uint32_t                shift) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpShiftLeftLogical, 5);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(base);
    m_code.putWord(shift);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opShiftRightArithmetic(
          uint32_t                resultType,
          uint32_t                base,
          uint32_t                shift) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpShiftRightArithmetic, 5);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(base);
    m_code.putWord(shift);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opShiftRightLogical(
          uint32_t                resultType,
          uint32_t                base,
          uint32_t                shift) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpShiftRightLogical, 5);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(base);
    m_code.putWord(shift);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opConvertFtoS(
          uint32_t                resultType,
          uint32_t                operand) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpConvertFToS, 4);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(operand);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opConvertFtoU(
          uint32_t                resultType,
          uint32_t                operand) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpConvertFToU, 4);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(operand);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opConvertStoF(
          uint32_t                resultType,
          uint32_t                operand) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpConvertSToF, 4);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(operand);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opConvertUtoF(
          uint32_t                resultType,
          uint32_t                operand) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpConvertUToF, 4);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(operand);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opCompositeConstruct(
          uint32_t                resultType,
          uint32_t                valueCount,
    const uint32_t*               valueArray) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpCompositeConstruct, 3 + valueCount);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    
    for (uint32_t i = 0; i < valueCount; i++)
      m_code.putWord(valueArray[i]);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opCompositeExtract(
          uint32_t                resultType,
          uint32_t                composite,
          uint32_t                indexCount,
    const uint32_t*               indexArray) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpCompositeExtract, 4 + indexCount);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(composite);
    
    for (uint32_t i = 0; i < indexCount; i++)
      m_code.putInt32(indexArray[i]);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opCompositeInsert(
          uint32_t                resultType,
          uint32_t                object,
          uint32_t                composite,
          uint32_t                indexCount,
    const uint32_t*               indexArray) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpCompositeInsert, 5 + indexCount);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(object);
    m_code.putWord(composite);
    
    for (uint32_t i = 0; i < indexCount; i++)
      m_code.putInt32(indexArray[i]);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opDpdx(
          uint32_t                resultType,
          uint32_t                operand) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpDPdx, 4);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(operand);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opDpdy(
          uint32_t                resultType,
          uint32_t                operand) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpDPdy, 4);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(operand);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opDpdxCoarse(
          uint32_t                resultType,
          uint32_t                operand) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpDPdxCoarse, 4);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(operand);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opDpdyCoarse(
          uint32_t                resultType,
          uint32_t                operand) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpDPdyCoarse, 4);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(operand);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opDpdxFine(
          uint32_t                resultType,
          uint32_t                operand) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpDPdxFine, 4);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(operand);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opDpdyFine(
          uint32_t                resultType,
          uint32_t                operand) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpDPdyFine, 4);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(operand);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opVectorExtractDynamic(
          uint32_t                resultType,
          uint32_t                vector,
          uint32_t                index) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpVectorExtractDynamic, 5);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(vector);
    m_code.putWord(index);
    return resultId;
  }


  uint32_t SpirvModule::opVectorShuffle(
          uint32_t                resultType,
          uint32_t                vectorLeft,
          uint32_t                vectorRight,
          uint32_t                indexCount,
    const uint32_t*               indexArray) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpVectorShuffle, 5 + indexCount);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(vectorLeft);
    m_code.putWord(vectorRight);
    
    for (uint32_t i = 0; i < indexCount; i++)
      m_code.putInt32(indexArray[i]);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opSNegate(
          uint32_t                resultType,
          uint32_t                operand) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpSNegate, 4);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(operand);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opFNegate(
          uint32_t                resultType,
          uint32_t                operand) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpFNegate, 4);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(operand);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opSAbs(
          uint32_t                resultType,
          uint32_t                operand) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpExtInst, 6);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(m_instExtGlsl450);
    m_code.putWord(spv::GLSLstd450SAbs);
    m_code.putWord(operand);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opFAbs(
          uint32_t                resultType,
          uint32_t                operand) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpExtInst, 6);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(m_instExtGlsl450);
    m_code.putWord(spv::GLSLstd450FAbs);
    m_code.putWord(operand);
    return resultId;
  }


  uint32_t SpirvModule::opFMix(
          uint32_t                resultType,
          uint32_t                x,
          uint32_t                y,
          uint32_t                a) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpExtInst, 8);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(m_instExtGlsl450);
    m_code.putWord(spv::GLSLstd450FMix);
    m_code.putWord(x);
    m_code.putWord(y);
    m_code.putWord(a);
    return resultId;
  }


  uint32_t SpirvModule::opCross(
          uint32_t                resultType,
          uint32_t                x,
          uint32_t                y) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpExtInst, 7);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(m_instExtGlsl450);
    m_code.putWord(spv::GLSLstd450Cross);
    m_code.putWord(x);
    m_code.putWord(y);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opIAdd(
          uint32_t                resultType,
          uint32_t                a,
          uint32_t                b) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpIAdd, 5);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(a);
    m_code.putWord(b);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opISub(
          uint32_t                resultType,
          uint32_t                a,
          uint32_t                b) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpISub, 5);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(a);
    m_code.putWord(b);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opFAdd(
          uint32_t                resultType,
          uint32_t                a,
          uint32_t                b) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpFAdd, 5);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(a);
    m_code.putWord(b);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opFSub(
          uint32_t                resultType,
          uint32_t                a,
          uint32_t                b) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpFSub, 5);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(a);
    m_code.putWord(b);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opSDiv(
          uint32_t                resultType,
          uint32_t                a,
          uint32_t                b) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpSDiv, 5);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(a);
    m_code.putWord(b);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opUDiv(
          uint32_t                resultType,
          uint32_t                a,
          uint32_t                b) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpUDiv, 5);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(a);
    m_code.putWord(b);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opSRem(
          uint32_t                resultType,
          uint32_t                a,
          uint32_t                b) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpSRem, 5);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(a);
    m_code.putWord(b);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opUMod(
          uint32_t                resultType,
          uint32_t                a,
          uint32_t                b) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpUMod, 5);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(a);
    m_code.putWord(b);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opFDiv(
          uint32_t                resultType,
          uint32_t                a,
          uint32_t                b) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpFDiv, 5);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(a);
    m_code.putWord(b);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opIMul(
          uint32_t                resultType,
          uint32_t                a,
          uint32_t                b) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpIMul, 5);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(a);
    m_code.putWord(b);
    return resultId;
  }
  
    
  uint32_t SpirvModule::opFMul(
          uint32_t                resultType,
          uint32_t                a,
          uint32_t                b) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpFMul, 5);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(a);
    m_code.putWord(b);
    return resultId;
  }


  uint32_t SpirvModule::opVectorTimesScalar(
    uint32_t                resultType,
    uint32_t                vector,
    uint32_t                scalar) {
    uint32_t resultId = this->allocateId();

    m_code.putIns(spv::OpVectorTimesScalar, 5);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(vector);
    m_code.putWord(scalar);
    return resultId;
  }


  uint32_t SpirvModule::opMatrixTimesMatrix(
    uint32_t                resultType,
    uint32_t                a,
    uint32_t                b) {
    uint32_t resultId = this->allocateId();

    m_code.putIns(spv::OpMatrixTimesMatrix, 5);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(a);
    m_code.putWord(b);
    return resultId;
  }


  uint32_t SpirvModule::opMatrixTimesVector(
    uint32_t                resultType,
    uint32_t                matrix,
    uint32_t                vector) {
    uint32_t resultId = this->allocateId();

    m_code.putIns(spv::OpMatrixTimesVector, 5);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(matrix);
    m_code.putWord(vector);
    return resultId;
  }


  uint32_t SpirvModule::opVectorTimesMatrix(
    uint32_t                resultType,
    uint32_t                vector,
    uint32_t                matrix) {
    uint32_t resultId = this->allocateId();

    m_code.putIns(spv::OpVectorTimesMatrix, 5);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(vector);
    m_code.putWord(matrix);
    return resultId;
  }


  uint32_t SpirvModule::opTranspose(
    uint32_t                resultType,
    uint32_t                matrix) {
    uint32_t resultId = this->allocateId();

    m_code.putIns(spv::OpTranspose, 4);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(matrix);
    return resultId;
  }


  uint32_t SpirvModule::opInverse(
    uint32_t                resultType,
    uint32_t                matrix) {
    uint32_t resultId = this->allocateId();

    m_code.putIns(spv::OpExtInst, 6);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(m_instExtGlsl450);
    m_code.putWord(spv::GLSLstd450MatrixInverse);
    m_code.putWord(matrix);
    return resultId;
  }


  uint32_t SpirvModule::opFFma(
          uint32_t                resultType,
          uint32_t                a,
          uint32_t                b,
          uint32_t                c) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpExtInst, 8);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(m_instExtGlsl450);
    m_code.putWord(spv::GLSLstd450Fma);
    m_code.putWord(a);
    m_code.putWord(b);
    m_code.putWord(c);
    return resultId;
  }
    
  
  uint32_t SpirvModule::opFMax(
          uint32_t                resultType,
          uint32_t                a,
          uint32_t                b) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpExtInst, 7);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(m_instExtGlsl450);
    m_code.putWord(spv::GLSLstd450FMax);
    m_code.putWord(a);
    m_code.putWord(b);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opFMin(
          uint32_t                resultType,
          uint32_t                a,
          uint32_t                b) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpExtInst, 7);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(m_instExtGlsl450);
    m_code.putWord(spv::GLSLstd450FMin);
    m_code.putWord(a);
    m_code.putWord(b);
    return resultId;
  }
    
  
  uint32_t SpirvModule::opNMax(
          uint32_t                resultType,
          uint32_t                a,
          uint32_t                b) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpExtInst, 7);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(m_instExtGlsl450);
    m_code.putWord(spv::GLSLstd450NMax);
    m_code.putWord(a);
    m_code.putWord(b);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opNMin(
          uint32_t                resultType,
          uint32_t                a,
          uint32_t                b) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpExtInst, 7);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(m_instExtGlsl450);
    m_code.putWord(spv::GLSLstd450NMin);
    m_code.putWord(a);
    m_code.putWord(b);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opSMax(
          uint32_t                resultType,
          uint32_t                a,
          uint32_t                b) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpExtInst, 7);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(m_instExtGlsl450);
    m_code.putWord(spv::GLSLstd450SMax);
    m_code.putWord(a);
    m_code.putWord(b);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opSMin(
          uint32_t                resultType,
          uint32_t                a,
          uint32_t                b) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpExtInst, 7);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(m_instExtGlsl450);
    m_code.putWord(spv::GLSLstd450SMin);
    m_code.putWord(a);
    m_code.putWord(b);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opUMax(
          uint32_t                resultType,
          uint32_t                a,
          uint32_t                b) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpExtInst, 7);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(m_instExtGlsl450);
    m_code.putWord(spv::GLSLstd450UMax);
    m_code.putWord(a);
    m_code.putWord(b);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opUMin(
          uint32_t                resultType,
          uint32_t                a,
          uint32_t                b) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpExtInst, 7);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(m_instExtGlsl450);
    m_code.putWord(spv::GLSLstd450UMin);
    m_code.putWord(a);
    m_code.putWord(b);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opFClamp(
          uint32_t                resultType,
          uint32_t                x,
          uint32_t                minVal,
          uint32_t                maxVal) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpExtInst, 8);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(m_instExtGlsl450);
    m_code.putWord(spv::GLSLstd450FClamp);
    m_code.putWord(x);
    m_code.putWord(minVal);
    m_code.putWord(maxVal);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opNClamp(
          uint32_t                resultType,
          uint32_t                x,
          uint32_t                minVal,
          uint32_t                maxVal) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpExtInst, 8);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(m_instExtGlsl450);
    m_code.putWord(spv::GLSLstd450NClamp);
    m_code.putWord(x);
    m_code.putWord(minVal);
    m_code.putWord(maxVal);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opIEqual(
          uint32_t                resultType,
          uint32_t                vector1,
          uint32_t                vector2) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpIEqual, 5);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(vector1);
    m_code.putWord(vector2);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opINotEqual(
          uint32_t                resultType,
          uint32_t                vector1,
          uint32_t                vector2) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpINotEqual, 5);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(vector1);
    m_code.putWord(vector2);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opSLessThan(
          uint32_t                resultType,
          uint32_t                vector1,
          uint32_t                vector2) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpSLessThan, 5);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(vector1);
    m_code.putWord(vector2);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opSLessThanEqual(
          uint32_t                resultType,
          uint32_t                vector1,
          uint32_t                vector2) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpSLessThanEqual, 5);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(vector1);
    m_code.putWord(vector2);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opSGreaterThan(
          uint32_t                resultType,
          uint32_t                vector1,
          uint32_t                vector2) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpSGreaterThan, 5);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(vector1);
    m_code.putWord(vector2);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opSGreaterThanEqual(
          uint32_t                resultType,
          uint32_t                vector1,
          uint32_t                vector2) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpSGreaterThanEqual, 5);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(vector1);
    m_code.putWord(vector2);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opULessThan(
          uint32_t                resultType,
          uint32_t                vector1,
          uint32_t                vector2) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpULessThan, 5);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(vector1);
    m_code.putWord(vector2);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opULessThanEqual(
          uint32_t                resultType,
          uint32_t                vector1,
          uint32_t                vector2) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpULessThanEqual, 5);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(vector1);
    m_code.putWord(vector2);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opUGreaterThan(
          uint32_t                resultType,
          uint32_t                vector1,
          uint32_t                vector2) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpUGreaterThan, 5);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(vector1);
    m_code.putWord(vector2);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opUGreaterThanEqual(
          uint32_t                resultType,
          uint32_t                vector1,
          uint32_t                vector2) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpUGreaterThanEqual, 5);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(vector1);
    m_code.putWord(vector2);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opFOrdEqual(
          uint32_t                resultType,
          uint32_t                vector1,
          uint32_t                vector2) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpFOrdEqual, 5);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(vector1);
    m_code.putWord(vector2);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opFOrdNotEqual(
          uint32_t                resultType,
          uint32_t                vector1,
          uint32_t                vector2) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpFOrdNotEqual, 5);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(vector1);
    m_code.putWord(vector2);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opFOrdLessThan(
          uint32_t                resultType,
          uint32_t                vector1,
          uint32_t                vector2) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpFOrdLessThan, 5);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(vector1);
    m_code.putWord(vector2);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opFOrdLessThanEqual(
          uint32_t                resultType,
          uint32_t                vector1,
          uint32_t                vector2) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpFOrdLessThanEqual, 5);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(vector1);
    m_code.putWord(vector2);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opFOrdGreaterThan(
          uint32_t                resultType,
          uint32_t                vector1,
          uint32_t                vector2) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpFOrdGreaterThan, 5);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(vector1);
    m_code.putWord(vector2);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opFOrdGreaterThanEqual(
          uint32_t                resultType,
          uint32_t                vector1,
          uint32_t                vector2) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpFOrdGreaterThanEqual, 5);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(vector1);
    m_code.putWord(vector2);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opLogicalEqual(
          uint32_t                resultType,
          uint32_t                operand1,
          uint32_t                operand2) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpLogicalEqual, 5);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(operand1);
    m_code.putWord(operand2);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opLogicalNotEqual(
          uint32_t                resultType,
          uint32_t                operand1,
          uint32_t                operand2) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpLogicalNotEqual, 5);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(operand1);
    m_code.putWord(operand2);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opLogicalAnd(
          uint32_t                resultType,
          uint32_t                operand1,
          uint32_t                operand2) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpLogicalAnd, 5);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(operand1);
    m_code.putWord(operand2);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opLogicalOr(
          uint32_t                resultType,
          uint32_t                operand1,
          uint32_t                operand2) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpLogicalOr, 5);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(operand1);
    m_code.putWord(operand2);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opLogicalNot(
          uint32_t                resultType,
          uint32_t                operand) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpLogicalNot, 4);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(operand);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opDot(
          uint32_t                resultType,
          uint32_t                vector1,
          uint32_t                vector2) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpDot, 5);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(vector1);
    m_code.putWord(vector2);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opSin(
          uint32_t                resultType,
          uint32_t                vector) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpExtInst, 6);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(m_instExtGlsl450);
    m_code.putWord(spv::GLSLstd450Sin);
    m_code.putWord(vector);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opCos(
          uint32_t                resultType,
          uint32_t                vector) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpExtInst, 6);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(m_instExtGlsl450);
    m_code.putWord(spv::GLSLstd450Cos);
    m_code.putWord(vector);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opSqrt(
          uint32_t                resultType,
          uint32_t                operand) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpExtInst, 6);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(m_instExtGlsl450);
    m_code.putWord(spv::GLSLstd450Sqrt);
    m_code.putWord(operand);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opInverseSqrt(
          uint32_t                resultType,
          uint32_t                operand) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpExtInst, 6);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(m_instExtGlsl450);
    m_code.putWord(spv::GLSLstd450InverseSqrt);
    m_code.putWord(operand);
    return resultId;
  }


  uint32_t SpirvModule::opNormalize(
          uint32_t                resultType,
          uint32_t                operand) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpExtInst, 6);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(m_instExtGlsl450);
    m_code.putWord(spv::GLSLstd450Normalize);
    m_code.putWord(operand);
    return resultId;
  }


  uint32_t SpirvModule::opReflect(
          uint32_t                resultType,
          uint32_t                incident,
          uint32_t                normal) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpExtInst, 7);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(m_instExtGlsl450);
    m_code.putWord(spv::GLSLstd450Reflect);
    m_code.putWord(incident);
    m_code.putWord(normal);
    return resultId;
  }


  uint32_t SpirvModule::opLength(
          uint32_t                resultType,
          uint32_t                operand) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpExtInst, 6);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(m_instExtGlsl450);
    m_code.putWord(spv::GLSLstd450Length);
    m_code.putWord(operand);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opExp2(
          uint32_t                resultType,
          uint32_t                operand) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpExtInst, 6);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(m_instExtGlsl450);
    m_code.putWord(spv::GLSLstd450Exp2);
    m_code.putWord(operand);
    return resultId;
  }


  uint32_t SpirvModule::opExp(
          uint32_t                resultType,
          uint32_t                operand) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpExtInst, 6);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(m_instExtGlsl450);
    m_code.putWord(spv::GLSLstd450Exp);
    m_code.putWord(operand);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opLog2(
          uint32_t                resultType,
          uint32_t                operand) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpExtInst, 6);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(m_instExtGlsl450);
    m_code.putWord(spv::GLSLstd450Log2);
    m_code.putWord(operand);
    return resultId;
  }

  uint32_t SpirvModule::opPow(
    uint32_t                resultType,
    uint32_t                base,
    uint32_t                exponent) {
    uint32_t resultId = this->allocateId();

    m_code.putIns(spv::OpExtInst, 7);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(m_instExtGlsl450);
    m_code.putWord(spv::GLSLstd450Pow);
    m_code.putWord(base);
    m_code.putWord(exponent);
    return resultId;
  }
  
  uint32_t SpirvModule::opFract(
          uint32_t                resultType,
          uint32_t                operand) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpExtInst, 6);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(m_instExtGlsl450);
    m_code.putWord(spv::GLSLstd450Fract);
    m_code.putWord(operand);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opCeil(
          uint32_t                resultType,
          uint32_t                operand) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpExtInst, 6);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(m_instExtGlsl450);
    m_code.putWord(spv::GLSLstd450Ceil);
    m_code.putWord(operand);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opFloor(
          uint32_t                resultType,
          uint32_t                operand) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpExtInst, 6);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(m_instExtGlsl450);
    m_code.putWord(spv::GLSLstd450Floor);
    m_code.putWord(operand);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opRound(
          uint32_t                resultType,
          uint32_t                operand) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpExtInst, 6);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(m_instExtGlsl450);
    m_code.putWord(spv::GLSLstd450Round);
    m_code.putWord(operand);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opRoundEven(
          uint32_t                resultType,
          uint32_t                operand) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpExtInst, 6);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(m_instExtGlsl450);
    m_code.putWord(spv::GLSLstd450RoundEven);
    m_code.putWord(operand);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opTrunc(
          uint32_t                resultType,
          uint32_t                operand) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpExtInst, 6);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(m_instExtGlsl450);
    m_code.putWord(spv::GLSLstd450Trunc);
    m_code.putWord(operand);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opFConvert(
          uint32_t                resultType,
          uint32_t                operand) {
    uint32_t resultId = this->allocateId();

    m_code.putIns (spv::OpFConvert, 4);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(operand);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opPackHalf2x16(
          uint32_t                resultType,
          uint32_t                operand) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpExtInst, 6);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(m_instExtGlsl450);
    m_code.putWord(spv::GLSLstd450PackHalf2x16);
    m_code.putWord(operand);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opUnpackHalf2x16(
          uint32_t                resultType,
          uint32_t                operand) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpExtInst, 6);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(m_instExtGlsl450);
    m_code.putWord(spv::GLSLstd450UnpackHalf2x16);
    m_code.putWord(operand);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opSelect(
          uint32_t                resultType,
          uint32_t                condition,
          uint32_t                operand1,
          uint32_t                operand2) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpSelect, 6);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(condition);
    m_code.putWord(operand1);
    m_code.putWord(operand2);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opIsNan(
          uint32_t                resultType,
          uint32_t                operand) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpIsNan, 4);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(operand);
    return resultId;
  }


  uint32_t SpirvModule::opIsInf(
          uint32_t                resultType,
          uint32_t                operand) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpIsInf, 4);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(operand);
    return resultId;
  }


  uint32_t SpirvModule::opFunctionCall(
          uint32_t                resultType,
          uint32_t                functionId,
          uint32_t                argCount,
    const uint32_t*               argIds) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpFunctionCall, 4 + argCount);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(functionId);
    
    for (uint32_t i = 0; i < argCount; i++)
      m_code.putWord(argIds[i]);
    return resultId;
  }
  
  
  void SpirvModule::opLabel(uint32_t labelId) {
    m_code.putIns (spv::OpLabel, 2);
    m_code.putWord(labelId);
  }
  
  
  uint32_t SpirvModule::opLoad(
          uint32_t                typeId,
          uint32_t                pointerId) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpLoad, 4);
    m_code.putWord(typeId);
    m_code.putWord(resultId);
    m_code.putWord(pointerId);
    return resultId;
  }
  
  
  void SpirvModule::opStore(
          uint32_t                pointerId,
          uint32_t                valueId) {
    m_code.putIns (spv::OpStore, 3);
    m_code.putWord(pointerId);
    m_code.putWord(valueId);
  }
  
  
  uint32_t SpirvModule::opInterpolateAtCentroid(
          uint32_t                resultType,
          uint32_t                interpolant) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpExtInst, 6);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(m_instExtGlsl450);
    m_code.putWord(spv::GLSLstd450InterpolateAtCentroid);
    m_code.putWord(interpolant);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opInterpolateAtSample(
          uint32_t                resultType,
          uint32_t                interpolant,
          uint32_t                sample) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpExtInst, 7);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(m_instExtGlsl450);
    m_code.putWord(spv::GLSLstd450InterpolateAtSample);
    m_code.putWord(interpolant);
    m_code.putWord(sample);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opInterpolateAtOffset(
          uint32_t                resultType,
          uint32_t                interpolant,
          uint32_t                offset) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpExtInst, 7);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(m_instExtGlsl450);
    m_code.putWord(spv::GLSLstd450InterpolateAtOffset);
    m_code.putWord(interpolant);
    m_code.putWord(offset);
    return resultId;
  }


  uint32_t SpirvModule::opImage(
          uint32_t                resultType,
          uint32_t                sampledImage) {
    uint32_t resultId = this->allocateId();

    m_code.putIns(spv::OpImage, 4);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(sampledImage);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opImageRead(
          uint32_t                resultType,
          uint32_t                image,
          uint32_t                coordinates,
    const SpirvImageOperands&     operands) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpImageRead,
      5 + getImageOperandWordCount(operands));
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(image);
    m_code.putWord(coordinates);
    
    putImageOperands(operands);
    return resultId;
  }
  
  
  void SpirvModule::opImageWrite(
          uint32_t                image,
          uint32_t                coordinates,
          uint32_t                texel,
    const SpirvImageOperands&     operands) {
    m_code.putIns (spv::OpImageWrite,
      4 + getImageOperandWordCount(operands));
    m_code.putWord(image);
    m_code.putWord(coordinates);
    m_code.putWord(texel);
    
    putImageOperands(operands);
  }
  
  
  uint32_t SpirvModule::opSampledImage(
          uint32_t                resultType,
          uint32_t                image,
          uint32_t                sampler) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpSampledImage, 5);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(image);
    m_code.putWord(sampler);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opImageTexelPointer(
          uint32_t                resultType,
          uint32_t                image,
          uint32_t                coordinates,
          uint32_t                sample) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpImageTexelPointer, 6);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(image);
    m_code.putWord(coordinates);
    m_code.putWord(sample);
    return resultId;
  }
  
    
  uint32_t SpirvModule::opImageQuerySizeLod(
          uint32_t                resultType,
          uint32_t                image,
          uint32_t                lod) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpImageQuerySizeLod, 5);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(image);
    m_code.putWord(lod);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opImageQuerySize(
          uint32_t                resultType,
          uint32_t                image) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpImageQuerySize, 4);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(image);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opImageQueryLevels(
          uint32_t                resultType,
          uint32_t                image) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpImageQueryLevels, 4);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(image);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opImageQueryLod(
          uint32_t                resultType,
          uint32_t                sampledImage,
          uint32_t                coordinates) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpImageQueryLod, 5);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(sampledImage);
    m_code.putWord(coordinates);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opImageQuerySamples(
          uint32_t                resultType,
          uint32_t                image) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpImageQuerySamples, 4);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(image);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opImageFetch(
          uint32_t                resultType,
          uint32_t                image,
          uint32_t                coordinates,
    const SpirvImageOperands&     operands) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns(spv::OpImageFetch,
      5 + getImageOperandWordCount(operands));
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(image);
    m_code.putWord(coordinates);
    
    putImageOperands(operands);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opImageGather(
          uint32_t                resultType,
          uint32_t                sampledImage,
          uint32_t                coordinates,
          uint32_t                component,
    const SpirvImageOperands&     operands) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns(spv::OpImageGather,
      6 + getImageOperandWordCount(operands));
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(sampledImage);
    m_code.putWord(coordinates);
    m_code.putWord(component);
    
    putImageOperands(operands);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opImageDrefGather(
          uint32_t                resultType,
          uint32_t                sampledImage,
          uint32_t                coordinates,
          uint32_t                reference,
    const SpirvImageOperands&     operands) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns(spv::OpImageDrefGather,
      6 + getImageOperandWordCount(operands));
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(sampledImage);
    m_code.putWord(coordinates);
    m_code.putWord(reference);
    
    putImageOperands(operands);
    return resultId;
  }
  
    
  uint32_t SpirvModule::opImageSampleImplicitLod(
          uint32_t                resultType,
          uint32_t                sampledImage,
          uint32_t                coordinates,
    const SpirvImageOperands&     operands) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns(spv::OpImageSampleImplicitLod,
      5 + getImageOperandWordCount(operands));
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(sampledImage);
    m_code.putWord(coordinates);
    
    putImageOperands(operands);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opImageSampleExplicitLod(
          uint32_t                resultType,
          uint32_t                sampledImage,
          uint32_t                coordinates,
    const SpirvImageOperands&     operands) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns(spv::OpImageSampleExplicitLod,
      5 + getImageOperandWordCount(operands));
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(sampledImage);
    m_code.putWord(coordinates);
    
    putImageOperands(operands);
    return resultId;
  }


  uint32_t SpirvModule::opImageSampleProjImplicitLod(
          uint32_t                resultType,
          uint32_t                sampledImage,
          uint32_t                coordinates,
    const SpirvImageOperands&     operands) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns(spv::OpImageSampleProjImplicitLod,
      5 + getImageOperandWordCount(operands));
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(sampledImage);
    m_code.putWord(coordinates);
    
    putImageOperands(operands);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opImageSampleProjExplicitLod(
          uint32_t                resultType,
          uint32_t                sampledImage,
          uint32_t                coordinates,
    const SpirvImageOperands&     operands) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns(spv::OpImageSampleProjExplicitLod,
      5 + getImageOperandWordCount(operands));
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(sampledImage);
    m_code.putWord(coordinates);
    
    putImageOperands(operands);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opImageSampleDrefImplicitLod(
          uint32_t                resultType,
          uint32_t                sampledImage,
          uint32_t                coordinates,
          uint32_t                reference,
    const SpirvImageOperands&     operands) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns(spv::OpImageSampleDrefImplicitLod,
      6 + getImageOperandWordCount(operands));
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(sampledImage);
    m_code.putWord(coordinates);
    m_code.putWord(reference);
    
    putImageOperands(operands);
    return resultId;
  }
  
  
  uint32_t SpirvModule::opImageSampleDrefExplicitLod(
          uint32_t                resultType,
          uint32_t                sampledImage,
          uint32_t                coordinates,
          uint32_t                reference,
    const SpirvImageOperands&     operands) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns(spv::OpImageSampleDrefExplicitLod,
      6 + getImageOperandWordCount(operands));
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(sampledImage);
    m_code.putWord(coordinates);
    m_code.putWord(reference);
    
    putImageOperands(operands);
    return resultId;
  }
  

  uint32_t SpirvModule::opImageSampleProjDrefImplicitLod(
          uint32_t                resultType,
          uint32_t                sampledImage,
          uint32_t                coordinates,
          uint32_t                reference,
    const SpirvImageOperands&     operands) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns(spv::OpImageSampleProjDrefImplicitLod,
      6 + getImageOperandWordCount(operands));
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(sampledImage);
    m_code.putWord(coordinates);
    m_code.putWord(reference);
    
    putImageOperands(operands);
    return resultId;
  }


  uint32_t SpirvModule::opImageSampleProjDrefExplicitLod(
          uint32_t                resultType,
          uint32_t                sampledImage,
          uint32_t                coordinates,
          uint32_t                reference,
    const SpirvImageOperands&     operands) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns(spv::OpImageSampleProjDrefExplicitLod,
      6 + getImageOperandWordCount(operands));
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(sampledImage);
    m_code.putWord(coordinates);
    m_code.putWord(reference);
    
    putImageOperands(operands);
    return resultId;
  }

  
  uint32_t SpirvModule::opGroupNonUniformBallot(
          uint32_t                resultType,
          uint32_t                execution,
          uint32_t                predicate) {
    uint32_t resultId = this->allocateId();

    m_code.putIns(spv::OpGroupNonUniformBallot, 5);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(execution);
    m_code.putWord(predicate);
    return resultId;
  }

  
  uint32_t SpirvModule::opGroupNonUniformBallotBitCount(
          uint32_t                resultType,
          uint32_t                execution,
          uint32_t                operation,
          uint32_t                ballot) {
    uint32_t resultId = this->allocateId();

    m_code.putIns(spv::OpGroupNonUniformBallotBitCount, 6);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(execution);
    m_code.putWord(operation);
    m_code.putWord(ballot);
    return resultId;
  }


  uint32_t SpirvModule::opGroupNonUniformElect(
          uint32_t                resultType,
          uint32_t                execution) {
    uint32_t resultId = this->allocateId();

    m_code.putIns(spv::OpGroupNonUniformElect, 4);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(execution);
    return resultId;
  }

  
  uint32_t SpirvModule::opGroupNonUniformBroadcastFirst(
          uint32_t                resultType,
          uint32_t                execution,
          uint32_t                value) {
    uint32_t resultId = this->allocateId();

    m_code.putIns(spv::OpGroupNonUniformBroadcastFirst, 5);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    m_code.putWord(execution);
    m_code.putWord(value);
    return resultId;
  }


  void SpirvModule::opControlBarrier(
          uint32_t                execution,
          uint32_t                memory,
          uint32_t                semantics) {
    m_code.putIns (spv::OpControlBarrier, 4);
    m_code.putWord(execution);
    m_code.putWord(memory);
    m_code.putWord(semantics);
  }
  
  
  void SpirvModule::opMemoryBarrier(
          uint32_t                memory,
          uint32_t                semantics) {
    m_code.putIns (spv::OpMemoryBarrier, 3);
    m_code.putWord(memory);
    m_code.putWord(semantics);
  }
  
  
  void SpirvModule::opLoopMerge(
          uint32_t                mergeBlock,
          uint32_t                continueTarget,
          uint32_t                loopControl) {
    m_code.putIns (spv::OpLoopMerge, 4);
    m_code.putWord(mergeBlock);
    m_code.putWord(continueTarget);
    m_code.putWord(loopControl);
  }
  
  
  void SpirvModule::opSelectionMerge(
          uint32_t                mergeBlock,
          uint32_t                selectionControl) {
    m_code.putIns (spv::OpSelectionMerge, 3);
    m_code.putWord(mergeBlock);
    m_code.putWord(selectionControl);
  }
  
  
  void SpirvModule::opBranch(
          uint32_t                label) {
    m_code.putIns (spv::OpBranch, 2);
    m_code.putWord(label);
  }
  
  
  void SpirvModule::opBranchConditional(
          uint32_t                condition,
          uint32_t                trueLabel,
          uint32_t                falseLabel) {
    m_code.putIns (spv::OpBranchConditional, 4);
    m_code.putWord(condition);
    m_code.putWord(trueLabel);
    m_code.putWord(falseLabel);
  }
  
  
  void SpirvModule::opSwitch(
          uint32_t                selector,
          uint32_t                jumpDefault,
          uint32_t                caseCount,
    const SpirvSwitchCaseLabel*   caseLabels) {
    m_code.putIns (spv::OpSwitch, 3 + 2 * caseCount);
    m_code.putWord(selector);
    m_code.putWord(jumpDefault);
    
    for (uint32_t i = 0; i < caseCount; i++) {
      m_code.putWord(caseLabels[i].literal);
      m_code.putWord(caseLabels[i].labelId);
    }
  }
  
  
  uint32_t SpirvModule::opPhi(
          uint32_t                resultType,
          uint32_t                sourceCount,
    const SpirvPhiLabel*          sourceLabels) {
    uint32_t resultId = this->allocateId();
    
    m_code.putIns (spv::OpPhi, 3 + 2 * sourceCount);
    m_code.putWord(resultType);
    m_code.putWord(resultId);
    
    for (uint32_t i = 0; i < sourceCount; i++) {
      m_code.putWord(sourceLabels[i].varId);
      m_code.putWord(sourceLabels[i].labelId);
    }
    
    return resultId;
  }
  
    
  void SpirvModule::opReturn() {
    m_code.putIns (spv::OpReturn, 1);
  }
  
  
  void SpirvModule::opKill() {
    m_code.putIns (spv::OpKill, 1);
  }
  
  
  void SpirvModule::opDemoteToHelperInvocation() {
    m_code.putIns (spv::OpDemoteToHelperInvocationEXT, 1);
  }
  
  
  void SpirvModule::opEmitVertex(
          uint32_t                streamId) {
    if (streamId == 0) {
      m_code.putIns (spv::OpEmitVertex, 1);
    } else {
      m_code.putIns (spv::OpEmitStreamVertex, 2);
      m_code.putWord(streamId);
    }
  }
  
  
  void SpirvModule::opEndPrimitive(
          uint32_t                streamId) {
    if (streamId == 0) {
      m_code.putIns (spv::OpEndPrimitive, 1);
    } else {
      m_code.putIns (spv::OpEndStreamPrimitive, 2);
      m_code.putWord(streamId);
    }
  }
  
  
  uint32_t SpirvModule::defType(
          spv::Op                 op, 
          uint32_t                argCount,
    const uint32_t*               argIds) {
    // Since the type info is stored in the code buffer,
    // we can use the code buffer to look up type IDs as
    // well. Result IDs are always stored as argument 1.
    for (auto ins : m_typeConstDefs) {
      bool match = ins.opCode() == op
                && ins.length() == 2 + argCount;
      
      for (uint32_t i = 0; i < argCount && match; i++)
        match &= ins.arg(2 + i) == argIds[i];
      
      if (match)
        return ins.arg(1);
    }
    
    // Type not yet declared, create a new one.
    uint32_t resultId = this->allocateId();
    m_typeConstDefs.putIns (op, 2 + argCount);
    m_typeConstDefs.putWord(resultId);
    
    for (uint32_t i = 0; i < argCount; i++)
      m_typeConstDefs.putWord(argIds[i]);
    return resultId;
  }
  
  
  uint32_t SpirvModule::defConst(
          spv::Op                 op,
          uint32_t                typeId,
          uint32_t                argCount,
    const uint32_t*               argIds) {
    // Avoid declaring constants multiple times
    for (auto ins : m_typeConstDefs) {
      bool match = ins.opCode() == op
                && ins.length() == 3 + argCount
                && ins.arg(1)   == typeId;
      
      for (uint32_t i = 0; i < argCount && match; i++)
        match &= ins.arg(3 + i) == argIds[i];
      
      if (!match)
        continue;
      
      uint32_t id = ins.arg(2);

      if (m_lateConsts.find(id) == m_lateConsts.end())
        return id;
    }
    
    // Constant not yet declared, make a new one
    uint32_t resultId = this->allocateId();
    m_typeConstDefs.putIns (op, 3 + argCount);
    m_typeConstDefs.putWord(typeId);
    m_typeConstDefs.putWord(resultId);
    
    for (uint32_t i = 0; i < argCount; i++)
      m_typeConstDefs.putWord(argIds[i]);
    return resultId;
  }
  
  
  void SpirvModule::instImportGlsl450() {
    m_instExtGlsl450 = this->allocateId();
    const char* name = "GLSL.std.450";
    
    m_instExt.putIns (spv::OpExtInstImport, 2 + m_instExt.strLen(name));
    m_instExt.putWord(m_instExtGlsl450);
    m_instExt.putStr (name);
  }
  
  
  uint32_t SpirvModule::getImageOperandWordCount(const SpirvImageOperands& op) const {
    // Each flag may add one or more operands
    const uint32_t result
      = ((op.flags & spv::ImageOperandsBiasMask)        ? 1 : 0)
      + ((op.flags & spv::ImageOperandsLodMask)         ? 1 : 0)
      + ((op.flags & spv::ImageOperandsConstOffsetMask) ? 1 : 0)
      + ((op.flags & spv::ImageOperandsGradMask)        ? 2 : 0)
      + ((op.flags & spv::ImageOperandsOffsetMask)      ? 1 : 0)
      + ((op.flags & spv::ImageOperandsConstOffsetsMask)? 1 : 0)
      + ((op.flags & spv::ImageOperandsSampleMask)      ? 1 : 0)
      + ((op.flags & spv::ImageOperandsMinLodMask)      ? 1 : 0);
    
    // Add a DWORD for the operand mask if it is non-zero
    return result != 0 ? result + 1 : 0;
  }
  
  
  void SpirvModule::putImageOperands(const SpirvImageOperands& op) {
    if (op.flags != 0) {
      m_code.putWord(op.flags);
      
      if (op.flags & spv::ImageOperandsBiasMask)
        m_code.putWord(op.sLodBias);
      
      if (op.flags & spv::ImageOperandsLodMask)
        m_code.putWord(op.sLod);
      
      if (op.flags & spv::ImageOperandsConstOffsetMask)
        m_code.putWord(op.sConstOffset);
      
      if (op.flags & spv::ImageOperandsGradMask) {
        m_code.putWord(op.sGradX);
        m_code.putWord(op.sGradY);
      }
      
      if (op.flags & spv::ImageOperandsOffsetMask)
        m_code.putWord(op.gOffset);
      
      if (op.flags & spv::ImageOperandsConstOffsetsMask)
        m_code.putWord(op.gConstOffsets);
      
      if (op.flags & spv::ImageOperandsSampleMask)
        m_code.putWord(op.sSampleId);
      
      if (op.flags & spv::ImageOperandsMinLodMask)
        m_code.putWord(op.sMinLod);
    }
  }
  
}