void Program::MakeRDDisassemblyString()

in renderdoc/driver/shaders/dxil/dxil_disassemble.cpp [2029:3986]


void Program::MakeRDDisassemblyString(const DXBC::Reflection *reflection)
{
  DXIL::dxcStyleFormatting = false;
  DXIL::dxilIdentifier = '_';

  m_Disassembly.clear();
  m_DisassemblyInstructionLine = 1;

  m_Disassembly += StringFormat::Fmt("; %s Shader, compiled under SM%u.%u",
                                     shaderNames[int(m_Type)], m_Major, m_Minor);
  DisassemblyAddNewLine(2);

  // TODO: output structs using named meta data if it exists
  m_Disassembly += DisassembleTypes(m_DisassemblyInstructionLine);
  m_Disassembly += DisassembleGlobalVars(m_DisassemblyInstructionLine);

  const char *swizzle = "xyzw";

  rdcarray<EntryPointInterface> entryPoints;
  FetchEntryPointInterfaces(entryPoints);

  for(size_t i = 0; i < m_Functions.size(); i++)
  {
    const Function &func = *m_Functions[i];

    m_Accum.processFunction(m_Functions[i]);

    if(func.external)
      continue;

    if(!func.external)
    {
      EntryPointInterface *entryPoint = NULL;
      for(size_t e = 0; e < entryPoints.size(); ++e)
      {
        if(func.name == entryPoints[e].name)
        {
          entryPoint = &entryPoints[e];
          break;
        }
      }

      // Display inputs/outputs and resource
      if(entryPoint)
      {
        bool needBlankLine = false;

        if(!entryPoint->inputs.empty())
        {
          m_Disassembly += "Inputs";
          DisassemblyAddNewLine();
          for(size_t j = 0; j < entryPoint->inputs.size(); ++j)
          {
            if(needBlankLine)
              DisassemblyAddNewLine();
            EntryPointInterface::Signature &sig = entryPoint->inputs[j];
            VarType varType = VarTypeForComponentType(sig.type);
            m_Disassembly += "  ";
            m_Disassembly += ToStr(varType).c_str();

            if(sig.cols > 1)
              m_Disassembly += ToStr(sig.cols);

            if(reflection && sig.rows == 1)
            {
              const SigParameter &sigParam = reflection->InputSig[j];
              if(sigParam.semanticName == sig.name)
              {
                sig.name = sigParam.semanticIdxName;
              }
            }
            m_Disassembly += " " + sig.name;
            if(sig.rows > 1)
              m_Disassembly += "[" + ToStr(sig.rows) + "]";
            m_Disassembly += ";";
            needBlankLine = true;
          }
          DisassemblyAddNewLine();
        }

        if(!entryPoint->outputs.empty())
        {
          if(needBlankLine)
          {
            DisassemblyAddNewLine();
            needBlankLine = false;
          }

          m_Disassembly += "Outputs";
          DisassemblyAddNewLine();
          for(size_t j = 0; j < entryPoint->outputs.size(); ++j)
          {
            if(needBlankLine)
              DisassemblyAddNewLine();
            EntryPointInterface::Signature &sig = entryPoint->outputs[j];
            VarType varType = VarTypeForComponentType(sig.type);
            m_Disassembly += "  ";
            m_Disassembly += ToStr(varType).c_str();

            if(sig.cols > 1)
              m_Disassembly += ToStr(sig.cols);

            if(reflection && sig.rows == 1)
            {
              const SigParameter &sigParam = reflection->OutputSig[j];
              if(sigParam.semanticName == sig.name)
                sig.name = sigParam.semanticIdxName;
            }
            m_Disassembly += " " + sig.name;
            if(sig.rows > 1)
              m_Disassembly += "[" + ToStr(sig.rows) + "]";
            m_Disassembly += ";";
            needBlankLine = true;
          }
          DisassemblyAddNewLine();
        }

        if(!entryPoint->srvs.empty())
        {
          for(size_t j = 0; j < entryPoint->srvs.size(); ++j)
          {
            if(needBlankLine)
              DisassemblyAddNewLine();
            EntryPointInterface::SRV &srv = entryPoint->srvs[j];
            if(srv.name.empty() && reflection)
            {
              for(DXBC::ShaderInputBind bind : reflection->SRVs)
              {
                if((bind.space == srv.space) && (bind.reg == srv.regBase) &&
                   (bind.bindCount == srv.regCount))
                  srv.name = bind.name;
              }
            }
            m_Disassembly += GetResourceShapeName(srv.shape, false);
            m_Disassembly += "<" + GetResourceTypeName(srv.type) + ">";
            m_Disassembly += " " + srv.name;
            if(srv.regCount > 1)
            {
              m_Disassembly += "[";
              if(srv.regCount != ~0U)
                m_Disassembly += ToStr(srv.regCount);
              m_Disassembly += "]";
            }
            m_Disassembly +=
                " : register(t" + ToStr(srv.regBase) + ", space" + ToStr(srv.space) + ")";
            m_Disassembly += ";";
            needBlankLine = true;
          }
          DisassemblyAddNewLine();
        }

        if(!entryPoint->uavs.empty())
        {
          for(size_t j = 0; j < entryPoint->uavs.size(); ++j)
          {
            if(needBlankLine)
              DisassemblyAddNewLine();
            EntryPointInterface::UAV &uav = entryPoint->uavs[j];
            if(uav.name.empty() && reflection)
            {
              for(DXBC::ShaderInputBind bind : reflection->UAVs)
              {
                if((bind.space == uav.space) && (bind.reg == uav.regBase) &&
                   (bind.bindCount == uav.regCount))
                  uav.name = bind.name;
              }
            }
            m_Disassembly += GetResourceShapeName(uav.shape, true);
            m_Disassembly += "<" + GetResourceTypeName(uav.type) + ">";
            m_Disassembly += " " + uav.name;
            if(uav.regCount > 1)
            {
              m_Disassembly += "[";
              if(uav.regCount != ~0U)
                m_Disassembly += ToStr(uav.regCount);
              m_Disassembly += "]";
            }
            m_Disassembly +=
                " : register(u" + ToStr(uav.regBase) + ", space" + ToStr(uav.space) + ")";
            m_Disassembly += ";";
            needBlankLine = true;
          }
          DisassemblyAddNewLine();
        }

        if(!entryPoint->cbuffers.empty())
        {
          for(size_t j = 0; j < entryPoint->cbuffers.size(); ++j)
          {
            if(needBlankLine)
              DisassemblyAddNewLine();
            EntryPointInterface::CBuffer &cbuffer = entryPoint->cbuffers[j];
            if(reflection)
            {
              for(size_t cbIdx = 0; cbIdx < reflection->CBuffers.size(); ++cbIdx)
              {
                const DXBC::CBuffer &cb = reflection->CBuffers[cbIdx];
                if((cb.space == cbuffer.space) && (cb.reg == cbuffer.regBase) &&
                   (cb.bindCount == cbuffer.regCount))
                {
                  if(cbuffer.name.empty())
                    cbuffer.name = cb.name;
                  if(!cbuffer.cbufferRefl)
                    cbuffer.cbufferRefl = &reflection->CBuffers[cbIdx];
                }
              }
            }
            m_Disassembly += "cbuffer " + cbuffer.name;
            if(cbuffer.regCount > 1)
            {
              m_Disassembly += "[";
              if(cbuffer.regCount != ~0U)
                m_Disassembly += ToStr(cbuffer.regCount);
              m_Disassembly += "]";
            }
            m_Disassembly +=
                " : register(b" + ToStr(cbuffer.regBase) + ", space" + ToStr(cbuffer.space) + ")";
            if(cbuffer.cbufferRefl)
            {
              if(!cbuffer.cbufferRefl->variables.empty())
              {
                DisassemblyAddNewLine();
                m_Disassembly += "{";
                DisassemblyAddNewLine();

                for(const DXBC::CBufferVariable &cbVar : cbuffer.cbufferRefl->variables)
                {
                  const DXBC::CBufferVariableType &cbType = cbVar.type;
                  m_Disassembly += "  ";
                  m_Disassembly += cbType.name;
                  m_Disassembly += " " + cbVar.name;
                  if(cbType.elements > 1)
                    m_Disassembly += "[" + ToStr(cbType.elements) + "]";
                  m_Disassembly += ";";
                  DisassemblyAddNewLine();
                }
                m_Disassembly += "};";
                DisassemblyAddNewLine();
              }
            }
            needBlankLine = true;
          }
        }

        if(!entryPoint->samplers.empty())
        {
          for(size_t j = 0; j < entryPoint->samplers.size(); ++j)
          {
            if(needBlankLine)
              DisassemblyAddNewLine();
            EntryPointInterface::Sampler &sampler = entryPoint->samplers[j];
            if(sampler.name.empty() && reflection)
            {
              for(DXBC::ShaderInputBind s : reflection->Samplers)
              {
                if((s.space == sampler.space) && (s.reg == sampler.regBase) &&
                   (s.bindCount == sampler.regCount))
                  sampler.name = s.name;
              }
            }
            m_Disassembly += GetResourceTypeName(sampler.type);
            m_Disassembly += " " + sampler.name;
            if(sampler.regCount > 1)
            {
              m_Disassembly += "[";
              if(sampler.regCount != ~0U)
                m_Disassembly += ToStr(sampler.regCount);
              m_Disassembly += "]";
            }
            m_Disassembly +=
                " : register(s" + ToStr(sampler.regBase) + ", space" + ToStr(sampler.space) + ")";
            m_Disassembly += ";";
            needBlankLine = true;
          }
          DisassemblyAddNewLine();
        }

        if(needBlankLine)
          DisassemblyAddNewLine();
      }

      if(func.internalLinkage)
        m_Disassembly += "internal ";
      m_Disassembly +=
          func.type->declFunction("@" + escapeStringIfNeeded(func.name), func.args, func.attrs);

      if(func.comdatIdx < m_Comdats.size())
        m_Disassembly += StringFormat::Fmt(
            " comdat($%s)", escapeStringIfNeeded(m_Comdats[func.comdatIdx].second).c_str());

      if(func.align)
        m_Disassembly += StringFormat::Fmt(" align %u", (1U << func.align) >> 1);

      if(func.attrs && func.attrs->functionSlot)
        m_Disassembly +=
            StringFormat::Fmt(" #%u", m_FuncAttrGroups.indexOf(func.attrs->functionSlot));

      DisassemblyAddNewLine();
      m_Disassembly += "{";
      DisassemblyAddNewLine();

      std::map<rdcstr, ResourceHandle> resHandles;
      std::map<rdcstr, rdcstr> ssaAliases;

      size_t curBlock = 0;

      // if the first block has a name, use it
      if(!func.blocks[curBlock]->name.empty())
      {
        m_Disassembly +=
            StringFormat::Fmt("%s:", escapeStringIfNeeded(func.blocks[curBlock]->name).c_str());
        DisassemblyAddNewLine(2);
      }

      for(size_t funcIdx = 0; funcIdx < func.instructions.size(); funcIdx++)
      {
        Instruction &inst = *func.instructions[funcIdx];

        inst.disassemblyLine = m_DisassemblyInstructionLine;
        rdcstr lineStr;
        if(!inst.type->isVoid())
        {
          lineStr += inst.type->toString();
          lineStr += " ";
        }
        rdcstr resultIdStr;
        if(!inst.getName().empty())
          resultIdStr = StringFormat::Fmt("%c%s", DXIL::dxilIdentifier,
                                          escapeStringIfNeeded(inst.getName()).c_str());
        else if(inst.slot != ~0U)
          resultIdStr = StringFormat::Fmt("%c%s", DXIL::dxilIdentifier, ToStr(inst.slot).c_str());

        if(!resultIdStr.empty())
          lineStr += resultIdStr + " = ";

        bool showDxFuncName = false;
        rdcstr commentStr;

        switch(inst.op)
        {
          case Operation::NoOp: lineStr += "??? "; break;
          case Operation::Call:
          {
            rdcstr funcCallName = inst.getFuncCall()->name;
            showDxFuncName = funcCallName.beginsWith("dx.op");
            if(showDxFuncName && funcCallName.beginsWith("dx.op.loadInput"))
            {
              // LoadInput(inputSigId,rowIndex,colIndex,gsVertexAxis)
              showDxFuncName = false;
              uint32_t dxopCode;
              RDCASSERT(getival<uint32_t>(inst.args[0], dxopCode));
              RDCASSERTEQUAL(dxopCode, (uint32_t)DXOp::loadInput);
              rdcstr name;
              rdcstr rowStr;
              rdcstr componentStr;
              uint32_t inputIdx;
              uint32_t rowIdx;
              bool hasRowIdx = getival<uint32_t>(inst.args[2], rowIdx);
              if(getival<uint32_t>(inst.args[1], inputIdx))
              {
                EntryPointInterface::Signature &sig = entryPoint->inputs[inputIdx];
                name = sig.name;
                if(hasRowIdx)
                {
                  if(sig.rows > 1)
                    rowStr = "[" + ToStr(rowIdx) + "]";
                }
              }
              else
              {
                name = ArgToString(inst.args[1], false);
                rowStr = "[";
                if(hasRowIdx)
                  rowStr += ToStr(rowIdx);
                else
                  rowStr += ArgToString(inst.args[2], false);
                rowStr += +"]";
              }
              uint32_t componentIdx;
              if(getival<uint32_t>(inst.args[3], componentIdx))
                componentStr = StringFormat::Fmt("%c", swizzle[componentIdx & 0x3]);
              else
                componentStr = ArgToString(inst.args[3], false);

              lineStr += "<IN>." + name + rowStr + "." + componentStr;
            }
            else if(showDxFuncName && funcCallName.beginsWith("dx.op.storeOutput"))
            {
              // StoreOutput(outputSigId,rowIndex,colIndex,value)
              showDxFuncName = false;
              uint32_t dxopCode;
              RDCASSERT(getival<uint32_t>(inst.args[0], dxopCode));
              RDCASSERTEQUAL(dxopCode, (uint32_t)DXOp::storeInput);
              rdcstr name;
              rdcstr rowStr;
              rdcstr componentStr;
              uint32_t outputIdx;
              uint32_t rowIdx;
              bool hasRowIdx = getival<uint32_t>(inst.args[2], rowIdx);
              if(getival<uint32_t>(inst.args[1], outputIdx))
              {
                EntryPointInterface::Signature &sig = entryPoint->outputs[outputIdx];
                name = sig.name;
                if(hasRowIdx)
                {
                  if(sig.rows > 1)
                    rowStr = "[" + ToStr(rowIdx) + "]";
                }
              }
              else
              {
                name = ArgToString(inst.args[1], false);
                rowStr = "[";
                if(hasRowIdx)
                  rowStr += ToStr(rowIdx);
                else
                  rowStr += ArgToString(inst.args[2], false);
                rowStr += +"]";
              }
              uint32_t componentIdx;
              if(getival<uint32_t>(inst.args[3], componentIdx))
                componentStr = StringFormat::Fmt("%c", swizzle[componentIdx & 0x3]);
              else
                componentStr = ArgToString(inst.args[3], false);

              lineStr += "<OUT>." + name + rowStr + "." + componentStr;
              lineStr += " = " + ArgToString(inst.args[4], false);
            }
            else if(showDxFuncName && funcCallName.beginsWith("dx.op.createHandle"))
            {
              // CreateHandle(resourceClass,rangeId,index,nonUniformIndex)
              showDxFuncName = false;
              uint32_t dxopCode;
              RDCASSERT(getival<uint32_t>(inst.args[0], dxopCode));
              RDCASSERTEQUAL(dxopCode, (uint32_t)DXOp::createHandle);
              rdcstr handleStr = resultIdStr;
              ResourceClass resClass;
              rdcstr resName;
              uint32_t resIndex;
              bool hasResIndex = getival<uint32_t>(inst.args[2], resIndex);
              if(getival<ResourceClass>(inst.args[1], resClass))
              {
                if(hasResIndex)
                {
                  ResourceHandle resHandle;
                  bool validRes = true;
                  switch(resClass)
                  {
                    case ResourceClass::SRV:
                      resName = entryPoint->srvs[resIndex].name;
                      resHandle.srv = &entryPoint->srvs[resIndex];
                      break;
                    case ResourceClass::UAV:
                      resName = entryPoint->uavs[resIndex].name;
                      resHandle.uav = &entryPoint->uavs[resIndex];
                      break;
                    case ResourceClass::CBuffer:
                      resName = entryPoint->cbuffers[resIndex].name;
                      resHandle.cbuffer = &entryPoint->cbuffers[resIndex];
                      break;
                    case ResourceClass::Sampler:
                      resName = entryPoint->samplers[resIndex].name;
                      resHandle.sampler = &entryPoint->samplers[resIndex];
                      break;
                    default:
                      validRes = false;
                      resName = "INVALID RESOURCE CLASS";
                      break;
                  };

                  if(validRes)
                  {
                    resHandle.name = resName;
                    resHandle.resourceClass = resClass;
                    resHandle.resourceIndex = resIndex;
                    resHandles[handleStr] = resHandle;
                    ssaAliases[handleStr] = resName;
                  }
                  uint32_t index;
                  if(getival<uint32_t>(inst.args[3], index))
                  {
                    if(index != resIndex)
                      commentStr += " index = " + ToStr(index);
                  }
                }
                else
                {
                  switch(resClass)
                  {
                    case ResourceClass::SRV: resName = "SRV"; break;
                    case ResourceClass::UAV: resName = "UAV"; break;
                    case ResourceClass::CBuffer: resName = "CBuffer"; break;
                    case ResourceClass::Sampler: resName = "Sampler"; break;
                    default: resName = "INVALID RESOURCE CLASS"; break;
                  };
                }
              }
              else
              {
                resName = "ResourceClass:" + ArgToString(inst.args[1], false);
              }
              if(!hasResIndex)
              {
                resName += "[" + ArgToString(inst.args[2], false) + "]";
                commentStr += " index = " + ArgToString(inst.args[3], false);
              }
              uint32_t value;
              if(getival<uint32_t>(inst.args[4], value))
              {
                if(value != 0)
                  commentStr += " nonUniformIndex = true";
              }
              lineStr += resName;
            }
            else if(showDxFuncName && funcCallName.beginsWith("dx.op.cbufferLoad"))
            {
              // CBufferLoad(handle,byteOffset,alignment)
              // CBufferLoadLegacy(handle,regIndex)
              showDxFuncName = false;
              uint32_t dxopCode;
              RDCASSERT(getival<uint32_t>(inst.args[0], dxopCode));
              bool loadLegacy = funcCallName.beginsWith("dx.op.cbufferLoadLegacy");
              if(loadLegacy)
                RDCASSERTEQUAL(dxopCode, (uint32_t)DXOp::cbufferLoadLegacy);
              else
                RDCASSERTEQUAL(dxopCode, (uint32_t)DXOp::cbufferLoad);
              rdcstr handleStr = ArgToString(inst.args[1], false);
              if(resHandles.count(handleStr) > 0)
              {
                uint32_t regIndex;
                if(getival<uint32_t>(inst.args[2], regIndex))
                {
                  if(!loadLegacy)
                  {
                    // TODO: handle non 16-byte aligned offsets
                    // Convert byte offset to a register index
                    regIndex = regIndex / 16;
                    // uint32_t alignment = getival<uint32_t>(inst.args[3]);
                  }
                  uint32_t resourceIndex = resHandles[handleStr].resourceIndex;
                  lineStr += MakeCBufferRegisterStr(regIndex, entryPoint->cbuffers[resourceIndex]);
                }
              }
              else
              {
                showDxFuncName = true;
              }
            }
            else if(showDxFuncName && funcCallName.beginsWith("dx.op.bufferLoad"))
            {
              // BufferLoad(srv,index,wot)
              // wot is unused
              showDxFuncName = false;
              uint32_t dxopCode;
              RDCASSERT(getival<uint32_t>(inst.args[0], dxopCode));
              RDCASSERTEQUAL(dxopCode, (uint32_t)DXOp::bufferLoad);
              rdcstr handleStr = ArgToString(inst.args[1], false);
              if(resHandles.count(handleStr) > 0)
              {
                lineStr += resHandles[handleStr].name;
                lineStr += "[" + ArgToString(inst.args[2], false) + "]";
              }
              else
              {
                showDxFuncName = true;
              }
            }
            else if(showDxFuncName && funcCallName.beginsWith("dx.op.rawBufferLoad"))
            {
              // RawBufferLoad(srv,index,elementOffset,mask,alignment)
              showDxFuncName = false;
              uint32_t dxopCode;
              RDCASSERT(getival<uint32_t>(inst.args[0], dxopCode));
              RDCASSERTEQUAL(dxopCode, (uint32_t)DXOp::rawBufferLoad);
              rdcstr handleStr = ArgToString(inst.args[1], false);
              if(resHandles.count(handleStr) > 0)
              {
                lineStr += resHandles[handleStr].name;
                if(!isUndef(inst.args[2]))
                {
                  lineStr += "[" + ArgToString(inst.args[2], false) + "]";
                  if(!isUndef(inst.args[3]))
                  {
                    uint32_t elementOffset;
                    if(getival<uint32_t>(inst.args[3], elementOffset))
                    {
                      if(elementOffset > 0)
                        lineStr += " + " + ToStr(elementOffset) + " bytes";
                    }
                    else
                    {
                      lineStr += " + " + ArgToString(inst.args[3], false) + " bytes";
                    }
                  }
                }
                else
                {
                  lineStr += "[" + ArgToString(inst.args[3], false) + "]";
                }
              }
              else
              {
                showDxFuncName = true;
              }
            }
            else if(showDxFuncName && funcCallName.beginsWith("dx.op.bufferStore") ||
                    funcCallName.beginsWith("dx.op.rawBufferStore"))
            {
              if(funcCallName.beginsWith("dx.op.bufferStore"))
              {
                // BufferStore(uav,coord0,coord1,value0,value1,value2,value3,mask)
                showDxFuncName = false;
                uint32_t dxopCode;
                RDCASSERT(getival<uint32_t>(inst.args[0], dxopCode));
                RDCASSERTEQUAL(dxopCode, (uint32_t)DXOp::bufferStore);
              }
              else
              {
                // RawBufferStore(uav,index,elementOffset,value0,value1,value2,value3,mask,alignment)
                showDxFuncName = false;
                uint32_t dxopCode;
                RDCASSERT(getival<uint32_t>(inst.args[0], dxopCode));
                RDCASSERTEQUAL(dxopCode, (uint32_t)DXOp::rawBufferStore);
              }

              rdcstr handleStr = ArgToString(inst.args[1], false);
              if(resHandles.count(handleStr) > 0)
              {
                uint32_t offset = 0;
                bool validElementOffset = !isUndef(inst.args[3]);
                bool constantElementOffset = validElementOffset && getival(inst.args[3], offset);

                lineStr += resHandles[handleStr].name;
                uint32_t index;
                if(getival(inst.args[2], index))
                {
                  if((offset == 0) || (index > 0))
                    lineStr += "[" + ToStr(index) + "]";
                }
                else
                {
                  lineStr += "[" + ArgToString(inst.args[2], false) + "]";
                }
                if(validElementOffset)
                {
                  if(constantElementOffset)
                  {
                    if(offset > 0)
                      lineStr += " + " + ToStr(offset) + " bytes";
                  }
                  else
                  {
                    lineStr += " + " + ArgToString(inst.args[3], false) + " bytes";
                  }
                }
                lineStr += " = ";
                lineStr += "{";
                bool needComma = false;
                for(uint32_t a = 4; a < 8; ++a)
                {
                  if(!isUndef(inst.args[a]))
                  {
                    if(needComma)
                      lineStr += ", ";
                    lineStr += ArgToString(inst.args[a], false);
                    needComma = true;
                  }
                }
                lineStr += "}";
              }
              else
              {
                showDxFuncName = true;
              }
            }
            else if(showDxFuncName && funcCallName.beginsWith("dx.op.textureLoad"))
            {
              // TextureLoad(srv,mipLevelOrSampleCount,coord0,coord1,coord2,offset0,offset1,offset2)
              showDxFuncName = false;
              uint32_t dxopCode;
              RDCASSERT(getival<uint32_t>(inst.args[0], dxopCode));
              RDCASSERTEQUAL(dxopCode, (uint32_t)DXOp::textureLoad);
              rdcstr handleStr = ArgToString(inst.args[1], false);
              if(resHandles.count(handleStr) > 0)
              {
                lineStr += resHandles[handleStr].name;
                lineStr += ".Load(";
                bool needComma = false;
                const EntryPointInterface::SRV *texture = resHandles[handleStr].srv;
                for(uint32_t a = 3; a < 6; ++a)
                {
                  if(!isUndef(inst.args[a]))
                  {
                    if(needComma)
                      lineStr += ", ";
                    lineStr += ArgToString(inst.args[a], false);
                    needComma = true;
                  }
                }
                bool needText = true;
                if(!isUndef(inst.args[2]))
                {
                  rdcstr prefix;
                  bool showArg = true;
                  if(needText)
                  {
                    if(texture && texture->sampleCount > 1)
                    {
                      prefix = "SampleIndex = ";
                    }
                    else
                    {
                      prefix = "MipSlice = ";
                      uint32_t mipSlice;
                      if(getival<uint32_t>(inst.args[2], mipSlice))
                        showArg = mipSlice > 0;
                    }
                  }
                  if(showArg)
                  {
                    needText = false;
                    lineStr += ", ";
                    lineStr += prefix;
                    lineStr += ArgToString(inst.args[2], false);
                  }
                }
                needText = true;
                for(uint32_t a = 6; a < 9; ++a)
                {
                  if(!isUndef(inst.args[a]))
                  {
                    lineStr += ", ";
                    if(needText)
                    {
                      lineStr += "Offset = ";
                      needText = false;
                    }
                    lineStr += ArgToString(inst.args[a], false);
                  }
                }
                lineStr += ")";
              }
              else
              {
                showDxFuncName = true;
              }
            }
            else if(showDxFuncName && funcCallName.beginsWith("dx.op.textureStore"))
            {
              // TextureStore(srv,coord0,coord1,coord2,value0,value1,value2,value3,mask)
              showDxFuncName = false;
              uint32_t dxopCode;
              RDCASSERT(getival<uint32_t>(inst.args[0], dxopCode));
              RDCASSERTEQUAL(dxopCode, (uint32_t)DXOp::textureStore);
              rdcstr handleStr = ArgToString(inst.args[1], false);
              if(resHandles.count(handleStr) > 0)
              {
                lineStr += resHandles[handleStr].name;
                lineStr += "[";
                bool needComma = false;
                for(uint32_t a = 2; a < 5; ++a)
                {
                  if(!isUndef(inst.args[a]))
                  {
                    if(needComma)
                      lineStr += ", ";
                    lineStr += ArgToString(inst.args[a], false);
                    needComma = true;
                  }
                }
                lineStr += "]";
                lineStr += " = ";
                lineStr += "{";
                needComma = false;
                for(uint32_t a = 5; a < 9; ++a)
                {
                  if(!isUndef(inst.args[a]))
                  {
                    if(needComma)
                      lineStr += ", ";
                    lineStr += ArgToString(inst.args[a], false);
                    needComma = true;
                  }
                }
                lineStr += "}";
              }
              else
              {
                showDxFuncName = true;
              }
            }
            else if(showDxFuncName && funcCallName.beginsWith("dx.op.sample") &&
                    !funcCallName.beginsWith("dx.op.sampleIndex"))
            {
              // Sample(srv,sampler,coord0,coord1,coord2,coord3,offset0,offset1,offset2,clamp)
              // SampleBias(srv,sampler,coord0,coord1,coord2,coord3,offset0,offset1,offset2,bias,clamp)
              // SampleLevel(srv,sampler,coord0,coord1,coord2,coord3,offset0,offset1,offset2,LOD)
              // SampleGrad(srv,sampler,coord0,coord1,coord2,coord3,offset0,offset1,offset2,ddx0,ddx1,ddx2,ddy0,ddy1,ddy2,clamp)
              // SampleCmp(srv,sampler,coord0,coord1,coord2,coord3,offset0,offset1,offset2,compareValue,clamp)
              // SampleCmpLevelZero(srv,sampler,coord0,coord1,coord2,coord3,offset0,offset1,offset2,compareValue)
              // SampleCmpLevel(srv,sampler,coord0,coord1,coord2,coord3,offset0,offset1,offset2,compareValue,lod)
              // SampleCmpGrad(srv,sampler,coord0,coord1,coord2,coord3,offset0,offset1,offset2,compareValue,ddx0,ddx1,ddx2,ddy0,ddy1,ddy2,clamp)
              // SampleCmpBias(srv,sampler,coord0,coord1,coord2,coord3,offset0,offset1,offset2,compareValue,bias,clamp)
              uint32_t dxopCode;
              RDCASSERT(getival<uint32_t>(inst.args[0], dxopCode));
              RDCASSERT((dxopCode == (uint32_t)DXOp::sample) ||
                        (dxopCode == (uint32_t)DXOp::sampleBias) ||
                        (dxopCode == (uint32_t)DXOp::sampleLevel) ||
                        (dxopCode == (uint32_t)DXOp::sampleGrad) ||
                        (dxopCode == (uint32_t)DXOp::sampleCmp) ||
                        (dxopCode == (uint32_t)DXOp::sampleCmpLevelZero) ||
                        (dxopCode == (uint32_t)DXOp::sampleCmpLevel) ||
                        (dxopCode == (uint32_t)DXOp::sampleCmpGrad) ||
                        (dxopCode == (uint32_t)DXOp::sampleCmpBias));
              showDxFuncName = false;
              rdcstr handleStr = ArgToString(inst.args[1], false);
              if(resHandles.count(handleStr) > 0)
              {
                lineStr += resHandles[handleStr].name;
                lineStr += ".";
                rdcstr dxFuncSig = funcNameSigs[dxopCode];
                int paramStart = dxFuncSig.find('(') + 1;
                if(paramStart > 0)
                  lineStr += dxFuncSig.substr(0, paramStart);
                else
                  lineStr += "UNKNOWN DX FUNCTION";

                // sampler is 2
                rdcstr samplerStr = ArgToString(inst.args[2], false);
                if(resHandles.count(samplerStr) > 0)
                  samplerStr = resHandles[samplerStr].name;
                lineStr += samplerStr;

                for(uint32_t a = 3; a < 7; ++a)
                {
                  if(!isUndef(inst.args[a]))
                  {
                    lineStr += ", ";
                    lineStr += ArgToString(inst.args[a], false);
                  }
                }
                bool needText = true;
                for(uint32_t a = 7; a < 10; ++a)
                {
                  if(!isUndef(inst.args[a]))
                  {
                    lineStr += ", ";
                    if(needText)
                    {
                      lineStr += "Offset = {";
                      needText = false;
                    }
                    lineStr += ArgToString(inst.args[a], false);
                  }
                }
                if(!needText)
                  lineStr += "}";

                int paramStrCount = (int)dxFuncSig.size();
                for(size_t a = 1; a < 10; ++a)
                {
                  if(paramStart < paramStrCount)
                  {
                    int paramEnd = dxFuncSig.find(',', paramStart);
                    if(paramEnd == -1)
                      paramEnd = paramStrCount;
                    paramStart = paramEnd + 1;
                  }
                }
                for(uint32_t a = 10; a < inst.args.size(); ++a)
                {
                  rdcstr paramNameStr;
                  if(paramStart < paramStrCount)
                  {
                    int paramEnd = dxFuncSig.find(',', paramStart);
                    if(paramEnd == -1)
                      paramEnd = paramStrCount - 1;
                    if(paramEnd > paramStart)
                    {
                      rdcstr dxParamName = dxFuncSig.substr(paramStart, paramEnd - paramStart);
                      paramStart = paramEnd + 1;
                      paramNameStr = "/*";
                      paramNameStr += dxParamName;
                      paramNameStr += "*/ ";
                    }
                  }
                  if(!isUndef(inst.args[a]))
                  {
                    lineStr += ", ";
                    lineStr += paramNameStr;
                    lineStr += ArgToString(inst.args[a], false);
                  }
                }
                lineStr += ")";
              }
              else
              {
                showDxFuncName = true;
              }
            }
            else if(showDxFuncName && funcCallName.beginsWith("dx.op.atomicBinOp"))
            {
              // AtomicBinOp(handle, atomicOp, offset0, offset1, offset2, newValue)
              showDxFuncName = false;
              uint32_t dxopCode;
              RDCASSERT(getival<uint32_t>(inst.args[0], dxopCode));
              RDCASSERTEQUAL(dxopCode, (uint32_t)DXOp::atomicBinOp);
              rdcstr handleStr = ArgToString(inst.args[1], false);
              AtomicBinOpCode atomicBinOpCode;
              if((resHandles.count(handleStr) > 0) &&
                 getival<AtomicBinOpCode>(inst.args[2], atomicBinOpCode))
              {
                lineStr += resHandles[handleStr].name;
                lineStr += ".";
                lineStr += "Interlocked";
                lineStr += ToStr(atomicBinOpCode);
                lineStr += "(";
                lineStr += "{";
                bool needComma = false;
                for(uint32_t a = 3; a < 6; ++a)
                {
                  if(!isUndef(inst.args[a]))
                  {
                    if(needComma)
                      lineStr += ", ";
                    lineStr += ArgToString(inst.args[a], false);
                    needComma = true;
                  }
                }
                lineStr += "}";
                if(!isUndef(inst.args[6]))
                {
                  lineStr += ", ";
                  lineStr += ArgToString(inst.args[6], false);
                }
                lineStr += ")";
              }
              else
              {
                showDxFuncName = true;
              }
            }
            else if(showDxFuncName && funcCallName.beginsWith("dx.op.dot"))
            {
              // Dot4(ax,ay,az,aw,bx,by,bz,bw)
              // Dot3(ax,ay,az,bx,by,bz)
              // Dot2(ax,ay,bx,by)
              showDxFuncName = false;
              uint32_t dxopCode;
              RDCASSERT(getival<uint32_t>(inst.args[0], dxopCode));
              uint32_t countComponents = 0;
              if(funcCallName.beginsWith("dx.op.dot4"))
              {
                countComponents = 4;
                RDCASSERTEQUAL(dxopCode, (uint32_t)DXOp::dot4);
              }
              else if(funcCallName.beginsWith("dx.op.dot3"))
              {
                countComponents = 3;
                RDCASSERTEQUAL(dxopCode, (uint32_t)DXOp::dot3);
              }
              else if(funcCallName.beginsWith("dx.op.dot2"))
              {
                countComponents = 2;
                RDCASSERTEQUAL(dxopCode, (uint32_t)DXOp::dot2);
              }
              lineStr += "dot(";
              lineStr += "{";
              bool needComma = false;
              uint32_t aVecStart = 1;
              uint32_t aVecEnd = 1 + countComponents;
              for(uint32_t a = aVecStart; a < aVecEnd; ++a)
              {
                if(!isUndef(inst.args[a]))
                {
                  if(needComma)
                    lineStr += ", ";
                  lineStr += ArgToString(inst.args[a], false);
                  needComma = true;
                }
              }
              lineStr += "}";
              needComma = false;
              uint32_t bVecStart = aVecEnd;
              uint32_t bVecEnd = bVecStart + countComponents;
              lineStr += ", {";
              for(uint32_t a = bVecStart; a < bVecEnd; ++a)
              {
                if(!isUndef(inst.args[a]))
                {
                  if(needComma)
                    lineStr += ", ";
                  lineStr += ArgToString(inst.args[a], false);
                  needComma = true;
                }
              }
              lineStr += "}";
              lineStr += ")";
            }
            else if(funcCallName.beginsWith("llvm.dbg."))
            {
            }
            else
            {
              if(showDxFuncName)
              {
                rdcstr dxFuncSig;
                int paramStart = -1;
                if(Constant *op = cast<Constant>(inst.args[0]))
                {
                  uint32_t opcode = op->getU32();
                  if(opcode < ARRAY_COUNT(funcNameSigs))
                  {
                    dxFuncSig = funcNameSigs[opcode];
                    paramStart = dxFuncSig.find('(') + 1;
                    if(paramStart > 0)
                      lineStr += dxFuncSig.substr(0, paramStart);
                    else
                      lineStr += dxFuncSig;
                  }
                  else
                  {
                    lineStr += escapeStringIfNeeded(funcCallName);
                  }
                }
                bool first = true;
                int paramStrCount = (int)dxFuncSig.size();
                for(size_t a = 1; a < inst.args.size(); ++a)
                {
                  rdcstr paramNameStr;
                  if(paramStart < paramStrCount)
                  {
                    int paramEnd = dxFuncSig.find(',', paramStart);
                    if(paramEnd == -1)
                      paramEnd = paramStrCount - 1;
                    if(paramEnd > paramStart)
                    {
                      rdcstr dxParamName = dxFuncSig.substr(paramStart, paramEnd - paramStart);
                      paramStart = paramEnd + 1;
                      paramNameStr = "/*";
                      paramNameStr += dxParamName;
                      paramNameStr += "*/ ";
                    }
                  }
                  // Don't show "undef" parameters
                  if(!isUndef(inst.args[a]))
                  {
                    if(!first)
                      lineStr += ", ";

                    lineStr += paramNameStr;
                    rdcstr ssaStr = ArgToString(inst.args[a], false);
                    if(ssaAliases.count(ssaStr) == 0)
                      lineStr += ssaStr;
                    else
                      lineStr += ssaAliases[ssaStr];
                    first = false;
                  }
                }
                lineStr += ")";
              }
              else
              {
                lineStr += escapeStringIfNeeded(funcCallName);
                lineStr += "(";
                bool first = true;

                const AttributeSet *paramAttrs = inst.getParamAttrs();
                // attribute args start from 1
                size_t argIdx = 1;
                for(const Value *s : inst.args)
                {
                  if(!first)
                    lineStr += ", ";

                  // see if we have param attrs for this param
                  rdcstr attrString;
                  if(paramAttrs && argIdx < paramAttrs->groupSlots.size() &&
                     paramAttrs->groupSlots[argIdx])
                  {
                    attrString = paramAttrs->groupSlots[argIdx]->toString(true) + " ";
                  }

                  lineStr += ArgToString(s, false, attrString);
                  first = false;

                  argIdx++;
                }
                lineStr += ")";

                if(paramAttrs && paramAttrs->functionSlot)
                  lineStr +=
                      StringFormat::Fmt(" #%u", m_FuncAttrGroups.indexOf(paramAttrs->functionSlot));
              }
            }
          }
          break;
          case Operation::Trunc:
          case Operation::ZExt:
          case Operation::SExt:
          case Operation::FToU:
          case Operation::FToS:
          case Operation::UToF:
          case Operation::SToF:
          case Operation::FPTrunc:
          case Operation::FPExt:
          case Operation::PtrToI:
          case Operation::IToPtr:
          case Operation::Bitcast:
          case Operation::AddrSpaceCast:
          {
            switch(inst.op)
            {
              case Operation::Trunc:
              case Operation::ZExt:
              case Operation::SExt:
              case Operation::UToF:
              case Operation::FPTrunc:
              case Operation::FPExt:
              case Operation::Bitcast:
              case Operation::FToU:
              case Operation::FToS:
              case Operation::PtrToI:
              case Operation::SToF: lineStr += "(" + inst.type->toString() + ")"; break;
              case Operation::IToPtr: lineStr += "(void *)"; break;
              case Operation::AddrSpaceCast: lineStr += "addrspacecast"; break;
              default: break;
            }
            switch(inst.op)
            {
              case Operation::Trunc: commentStr = "truncate ";
              case Operation::ZExt: commentStr += "zero extend "; break;
              case Operation::SExt: commentStr += "signed extend "; break;
              case Operation::UToF: commentStr += "unsigned "; break;
              case Operation::FPTrunc: commentStr += "fp truncate "; break;
              case Operation::FPExt: commentStr += "fp extend"; break;
              case Operation::Bitcast: commentStr += "bitcast "; break;
              case Operation::FToU: commentStr += "unsigned "; break;
              case Operation::FToS: commentStr += "signed "; break;
              case Operation::PtrToI: commentStr += "ptrtoi "; break;
              case Operation::IToPtr: commentStr += "itoptr "; break;
              default: break;
            }

            lineStr += "(";
            lineStr += ArgToString(inst.args[0], false);
            lineStr += ")";
            break;
          }
          case Operation::ExtractVal:
          {
            lineStr += "extractvalue ";
            lineStr += ArgToString(inst.args[0], false);
            for(size_t n = 1; n < inst.args.size(); n++)
              lineStr += StringFormat::Fmt(", %llu", cast<Literal>(inst.args[n])->literal);
            break;
          }
          case Operation::FAdd:
          case Operation::FSub:
          case Operation::FMul:
          case Operation::FDiv:
          case Operation::FRem:
          case Operation::Add:
          case Operation::Sub:
          case Operation::Mul:
          case Operation::UDiv:
          case Operation::SDiv:
          case Operation::URem:
          case Operation::SRem:
          case Operation::ShiftLeft:
          case Operation::LogicalShiftRight:
          case Operation::ArithShiftRight:
          case Operation::And:
          case Operation::Or:
          case Operation::Xor:
          {
            rdcstr opStr;
            switch(inst.op)
            {
              case Operation::FAdd: opStr = " + "; break;
              case Operation::FSub: opStr = " - "; break;
              case Operation::FMul: opStr = " * "; break;
              case Operation::FDiv: opStr = " / "; break;
              case Operation::FRem: opStr = " % "; break;
              case Operation::Add: opStr = " + "; break;
              case Operation::Sub: opStr = " - "; break;
              case Operation::Mul: opStr = " * "; break;
              case Operation::UDiv: opStr = " / "; break;
              case Operation::SDiv: opStr = " / "; break;
              case Operation::URem: opStr = " % "; break;
              case Operation::ShiftLeft: opStr = " << "; break;
              case Operation::LogicalShiftRight: opStr = " >> "; break;
              case Operation::ArithShiftRight: opStr = " >> "; break;
              case Operation::And: opStr = " & "; break;
              case Operation::Or: opStr = " | "; break;
              case Operation::Xor: opStr = " ^ "; break;
              default: break;
            }
            switch(inst.op)
            {
              case Operation::FRem: commentStr += "float "; break;
              case Operation::UDiv:
              case Operation::URem: commentStr += "unsigned "; break;
              case Operation::SDiv:
              case Operation::SRem: commentStr += "signed "; break;
              case Operation::LogicalShiftRight: commentStr += "logical "; break;
              case Operation::ArithShiftRight: commentStr += "arithmetic "; break;
              default: break;
            }

            bool first = true;
            for(const Value *s : inst.args)
            {
              lineStr += ArgToString(s, false);
              if(first)
              {
                lineStr += opStr;
                first = false;
              }
            }

            break;
          }
          case Operation::Ret:
          {
            lineStr += "return";
            if(!inst.args.empty())
              lineStr += " " + ArgToString(inst.args[0], false);
            break;
          }
          case Operation::Unreachable: lineStr += "unreachable"; break;
          case Operation::Alloca:
          {
            lineStr += "alloca ";
            lineStr += inst.type->inner->toString();
            if(inst.align > 0)
              lineStr += StringFormat::Fmt(", align %u", (1U << inst.align) >> 1);
            break;
          }
          case Operation::GetElementPtr:
          {
            bool fallbackOutput = true;
            if(!inst.type->isVoid())
            {
              // type "float addrspace(3)*" : addrspace(3) is DXIL specific, see DXIL::Type::PointerAddrSpace
              rdcstr typeStr = inst.type->toString();
              int start = typeStr.find(" addrspace(");
              if(start > 0)
              {
                rdcstr scalarType = typeStr.substr(0, start);
                scalarType.trim();

                start += 11;
                int end = typeStr.find(')', start);
                if(end > start)
                {
                  // Example output:
                  // DXC:
                  // %3 = getelementptr [6 x float], [6 x float] addrspace(3)*
                  // @"\01?s_x@@3@$$A.1dim", i32 0, i32 %9

                  // RD: GroupShared float* _3 = s_x[_9];
                  fallbackOutput = false;

                  rdcstr addrspaceStr(typeStr.substr(start, end - start));
                  int32_t value = atoi(addrspaceStr.c_str());
                  DXIL::Type::PointerAddrSpace addrspace = (DXIL::Type::PointerAddrSpace)value;

                  switch(addrspace)
                  {
                    case DXIL::Type::PointerAddrSpace::Default: lineStr = ""; break;
                    case DXIL::Type::PointerAddrSpace::DeviceMemory:
                      lineStr = "DeviceMemory";
                      break;
                    case DXIL::Type::PointerAddrSpace::CBuffer: lineStr = "CBuffer"; break;
                    case DXIL::Type::PointerAddrSpace::GroupShared: lineStr = "GroupShared"; break;
                    case DXIL::Type::PointerAddrSpace::GenericPointer: lineStr = ""; break;
                    case DXIL::Type::PointerAddrSpace::ImmediateCBuffer:
                      lineStr = "ImmediateCBuffer";
                      break;
                  };

                  lineStr += " ";
                  lineStr += scalarType;
                  lineStr += "* ";

                  if(!inst.getName().empty())
                    lineStr += StringFormat::Fmt("%c%s = ", DXIL::dxilIdentifier,
                                                 escapeStringIfNeeded(inst.getName()).c_str());
                  else if(inst.slot != ~0U)
                    lineStr += StringFormat::Fmt("%c%u = ", DXIL::dxilIdentifier, inst.slot);

                  // arg[0] : ptr
                  rdcstr ptrStr = ArgToString(inst.args[0], false);
                  // Try to de-mangle the pointer name
                  // @"\01?shared_pos@@3PAY0BC@$$CAMA.1dim" -> shared_pos
                  // Take the string between first alphabetical character and last
                  // alphanumeric character or "_"
                  start = 0;
                  int strEnd = (int)ptrStr.size();
                  while(start < strEnd)
                  {
                    if(isalpha(ptrStr[start]))
                      break;
                    ++start;
                  }
                  if(start < strEnd)
                  {
                    end = start + 1;
                    while(end < strEnd)
                    {
                      char c = ptrStr[end];
                      if(!isalnum(c) && c != '_')
                        break;
                      ++end;
                    }
                  }
                  if(end > start)
                    ptrStr = ptrStr.substr(start, end - start);

                  lineStr += ptrStr;
                  // arg[1] : index 0
                  bool first = true;
                  if(inst.args.size() > 1)
                  {
                    uint32_t v = 0;
                    if(!getival<uint32_t>(inst.args[1], v) || (v > 0))
                    {
                      lineStr += "[";
                      lineStr += ArgToString(inst.args[1], false);
                      lineStr += "]";
                      first = false;
                    }
                  }

                  // arg[2..] : index 1...N
                  for(size_t a = 2; a < inst.args.size(); ++a)
                  {
                    if(first)
                      lineStr += "[";
                    else
                      lineStr += " + ";

                    lineStr += ArgToString(inst.args[a], false);

                    if(first)
                    {
                      lineStr += "]";
                      first = false;
                    }
                  }
                }
              }
            }
            if(fallbackOutput)
            {
              lineStr += "getelementptr ";
              bool first = true;
              for(const Value *s : inst.args)
              {
                if(!first)
                  lineStr += ", ";

                lineStr += ArgToString(s, false);
                first = false;
              }
            }
            if(inst.opFlags() & InstructionFlags::InBounds)
              commentStr += "inbounds ";
            break;
          }
          case Operation::LoadAtomic: commentStr += "atomic ";
          case Operation::Load:
          {
            lineStr += "*";
            if(inst.opFlags() & InstructionFlags::Volatile)
              commentStr += "volatile ";
            bool first = true;
            for(const Value *s : inst.args)
            {
              if(!first)
                lineStr += ", ";

              lineStr += ArgToString(s, false);
              first = false;
            }
            if(inst.align > 0)
              commentStr += StringFormat::Fmt("align %u ", (1U << inst.align) >> 1);
            break;
          }
          case Operation::StoreAtomic: commentStr += "atomic ";
          case Operation::Store:
          {
            if(inst.opFlags() & InstructionFlags::Volatile)
              commentStr += "volatile ";
            lineStr = "*";
            lineStr += ArgToString(inst.args[0], false);
            lineStr += " = ";
            lineStr += ArgToString(inst.args[1], false);
            if(inst.align > 0)
              commentStr += StringFormat::Fmt("align %u ", (1U << inst.align) >> 1);
            break;
          }
          case Operation::FOrdEqual:
          case Operation::FOrdGreater:
          case Operation::FOrdGreaterEqual:
          case Operation::FOrdLess:
          case Operation::FOrdLessEqual:
          case Operation::FOrdNotEqual:
          case Operation::FUnordEqual:
          case Operation::FUnordGreater:
          case Operation::FUnordGreaterEqual:
          case Operation::FUnordLess:
          case Operation::FUnordLessEqual:
          case Operation::FUnordNotEqual:
          {
            rdcstr opStr;
            switch(inst.op)
            {
              case Operation::FOrdEqual: opStr = " == "; break;
              case Operation::FOrdGreater: opStr = " > "; break;
              case Operation::FOrdGreaterEqual: opStr = " >= "; break;
              case Operation::FOrdLess: opStr = " < "; break;
              case Operation::FOrdLessEqual: opStr = " <= "; break;
              case Operation::FOrdNotEqual: opStr = " != "; break;
              case Operation::FUnordEqual: opStr = " == ";
              case Operation::FUnordGreater: opStr = " > "; break;
              case Operation::FUnordGreaterEqual: opStr = " >= "; break;
              case Operation::FUnordLess: opStr = " < "; break;
              case Operation::FUnordLessEqual: opStr = " <= "; break;
              case Operation::FUnordNotEqual: opStr = " != "; break;
              default: break;
            }
            switch(inst.op)
            {
              case Operation::FUnord:
              case Operation::FUnordEqual:
              case Operation::FUnordGreater:
              case Operation::FUnordGreaterEqual:
              case Operation::FUnordLess:
              case Operation::FUnordLessEqual: commentStr += "unordered ";
              default: break;
            }
            lineStr += "(";
            lineStr += ArgToString(inst.args[0], false);
            lineStr += opStr;
            lineStr += ArgToString(inst.args[1], false);
            lineStr += ")";
            break;
          }
          case Operation::FOrd:
          {
            // ord: yields true if both operands are not a QNAN.
            lineStr += "!isqnan(";
            lineStr += ArgToString(inst.args[0], false);
            lineStr += ")";
            lineStr += " && ";
            lineStr += "!isqnan(";
            lineStr += ArgToString(inst.args[1], false);
            lineStr += ")";
            break;
          }
          case Operation::FUnord:
          {
            // uno: yields true if either operand is a QNAN.
            lineStr += "isqnan(";
            lineStr += ArgToString(inst.args[0], false);
            lineStr += ")";
            lineStr += " || ";
            lineStr += "isqnan(";
            lineStr += ArgToString(inst.args[1], false);
            lineStr += ")";
            break;
          }
          case Operation::FOrdFalse: lineStr += "false"; break;
          case Operation::FOrdTrue: lineStr += "true"; break;
          case Operation::IEqual:
          case Operation::INotEqual:
          case Operation::UGreater:
          case Operation::UGreaterEqual:
          case Operation::ULess:
          case Operation::ULessEqual:
          case Operation::SGreater:
          case Operation::SGreaterEqual:
          case Operation::SLess:
          case Operation::SLessEqual:
          {
            rdcstr opStr;
            switch(inst.op)
            {
              case Operation::IEqual: opStr += " == "; break;
              case Operation::INotEqual: opStr += " != "; break;
              case Operation::UGreater: opStr += " > "; break;
              case Operation::UGreaterEqual: opStr += " >= "; break;
              case Operation::ULess: opStr += " < "; break;
              case Operation::ULessEqual: opStr += " <= "; break;
              case Operation::SGreater: opStr += " > "; break;
              case Operation::SGreaterEqual: opStr += " >= "; break;
              case Operation::SLess: opStr += " < "; break;
              case Operation::SLessEqual: opStr += " <= "; break;
              default: break;
            }
            switch(inst.op)
            {
              case Operation::SGreater:
              case Operation::SGreaterEqual:
              case Operation::SLess:
              case Operation::SLessEqual: commentStr = "signed ";
              default: break;
            }
            lineStr += "(";
            lineStr += ArgToString(inst.args[0], false);
            lineStr += opStr;
            lineStr += ArgToString(inst.args[1], false);
            lineStr += ")";
            break;
          }
          case Operation::Select:
          {
            lineStr += ArgToString(inst.args[2], false);
            lineStr += " ? ";
            lineStr += ArgToString(inst.args[0], false);
            lineStr += " : ";
            lineStr += ArgToString(inst.args[1], false);
            break;
          }
          case Operation::ExtractElement:
          {
            lineStr += "extractelement ";
            lineStr += ArgToString(inst.args[0], false);
            lineStr += ", ";
            lineStr += ArgToString(inst.args[1], false);
            break;
          }
          case Operation::InsertElement:
          {
            lineStr += "insertelement ";
            lineStr += ArgToString(inst.args[0], false);
            lineStr += ", ";
            lineStr += ArgToString(inst.args[1], false);
            lineStr += ", ";
            lineStr += ArgToString(inst.args[2], false);
            break;
          }
          case Operation::ShuffleVector:
          {
            lineStr += "shufflevector ";
            lineStr += ArgToString(inst.args[0], false);
            lineStr += ", ";
            lineStr += ArgToString(inst.args[1], false);
            lineStr += ", ";
            lineStr += ArgToString(inst.args[2], false);
            break;
          }
          case Operation::InsertValue:
          {
            lineStr += "insertvalue ";
            lineStr += ArgToString(inst.args[0], false);
            lineStr += ", ";
            lineStr += ArgToString(inst.args[1], false);
            for(size_t a = 2; a < inst.args.size(); a++)
            {
              lineStr += ", " + ToStr(cast<Literal>(inst.args[a])->literal);
            }
            break;
          }
          case Operation::Branch:
          {
            if(inst.args.size() > 1)
            {
              lineStr += "if (";
              lineStr += ArgToString(inst.args[2], false);
              lineStr += ") goto ";
              lineStr += StringFormat::Fmt("%s", ArgToString(inst.args[0], false).c_str());
              lineStr += " else goto ";
              lineStr += StringFormat::Fmt("%s", ArgToString(inst.args[1], false).c_str());
            }
            else
            {
              lineStr += "goto ";
              lineStr += ArgToString(inst.args[0], false);
            }
            break;
          }
          case Operation::Phi:
          {
            lineStr += "phi ";
            lineStr += inst.type->toString();
            for(size_t a = 0; a < inst.args.size(); a += 2)
            {
              if(a == 0)
                lineStr += " ";
              else
                lineStr += ", ";
              lineStr += StringFormat::Fmt("[ %s, %s ]", ArgToString(inst.args[a], false).c_str(),
                                           ArgToString(inst.args[a + 1], false).c_str());
            }
            break;
          }
          case Operation::Switch:
          {
            lineStr += "switch ";
            lineStr += ArgToString(inst.args[0], false);
            lineStr += ", ";
            lineStr += ArgToString(inst.args[1], false);
            lineStr += " [";
            lineStr += "\n";
            m_DisassemblyInstructionLine++;
            for(size_t a = 2; a < inst.args.size(); a += 2)
            {
              lineStr += StringFormat::Fmt("    %s, %s", ArgToString(inst.args[a], false).c_str(),
                                           ArgToString(inst.args[a + 1], false).c_str());
              lineStr += "\n";
              m_DisassemblyInstructionLine++;
            }
            lineStr += "  ]";
            break;
          }
          case Operation::Fence:
          {
            lineStr += "fence ";
            if(inst.opFlags() & InstructionFlags::SingleThread)
              lineStr += "singlethread ";
            switch((inst.opFlags() & InstructionFlags::SuccessOrderMask))
            {
              case InstructionFlags::SuccessUnordered: lineStr += "unordered"; break;
              case InstructionFlags::SuccessMonotonic: lineStr += "monotonic"; break;
              case InstructionFlags::SuccessAcquire: lineStr += "acquire"; break;
              case InstructionFlags::SuccessRelease: lineStr += "release"; break;
              case InstructionFlags::SuccessAcquireRelease: lineStr += "acq_rel"; break;
              case InstructionFlags::SuccessSequentiallyConsistent: lineStr += "seq_cst"; break;
              default: break;
            }
            break;
          }
          case Operation::CompareExchange:
          {
            lineStr += "cmpxchg ";
            if(inst.opFlags() & InstructionFlags::Weak)
              lineStr += "weak ";
            if(inst.opFlags() & InstructionFlags::Volatile)
              lineStr += "volatile ";

            bool first = true;
            for(const Value *s : inst.args)
            {
              if(!first)
                lineStr += ", ";

              lineStr += ArgToString(s, false);
              first = false;
            }

            lineStr += " ";
            if(inst.opFlags() & InstructionFlags::SingleThread)
              lineStr += "singlethread ";
            switch((inst.opFlags() & InstructionFlags::SuccessOrderMask))
            {
              case InstructionFlags::SuccessUnordered: lineStr += "unordered"; break;
              case InstructionFlags::SuccessMonotonic: lineStr += "monotonic"; break;
              case InstructionFlags::SuccessAcquire: lineStr += "acquire"; break;
              case InstructionFlags::SuccessRelease: lineStr += "release"; break;
              case InstructionFlags::SuccessAcquireRelease: lineStr += "acq_rel"; break;
              case InstructionFlags::SuccessSequentiallyConsistent: lineStr += "seq_cst"; break;
              default: break;
            }
            lineStr += " ";
            switch((inst.opFlags() & InstructionFlags::FailureOrderMask))
            {
              case InstructionFlags::FailureUnordered: lineStr += "unordered"; break;
              case InstructionFlags::FailureMonotonic: lineStr += "monotonic"; break;
              case InstructionFlags::FailureAcquire: lineStr += "acquire"; break;
              case InstructionFlags::FailureRelease: lineStr += "release"; break;
              case InstructionFlags::FailureAcquireRelease: lineStr += "acq_rel"; break;
              case InstructionFlags::FailureSequentiallyConsistent: lineStr += "seq_cst"; break;
              default: break;
            }
            break;
          }
          case Operation::AtomicExchange:
          case Operation::AtomicAdd:
          case Operation::AtomicSub:
          case Operation::AtomicAnd:
          case Operation::AtomicNand:
          case Operation::AtomicOr:
          case Operation::AtomicXor:
          case Operation::AtomicMax:
          case Operation::AtomicMin:
          case Operation::AtomicUMax:
          case Operation::AtomicUMin:
          {
            lineStr += "atomicrmw ";
            if(inst.opFlags() & InstructionFlags::Volatile)
              lineStr += "volatile ";
            switch(inst.op)
            {
              case Operation::AtomicExchange: lineStr += "xchg "; break;
              case Operation::AtomicAdd: lineStr += "add "; break;
              case Operation::AtomicSub: lineStr += "sub "; break;
              case Operation::AtomicAnd: lineStr += "and "; break;
              case Operation::AtomicNand: lineStr += "nand "; break;
              case Operation::AtomicOr: lineStr += "or "; break;
              case Operation::AtomicXor: lineStr += "xor "; break;
              case Operation::AtomicMax: lineStr += "max "; break;
              case Operation::AtomicMin: lineStr += "min "; break;
              case Operation::AtomicUMax: lineStr += "umax "; break;
              case Operation::AtomicUMin: lineStr += "umin "; break;
              default: break;
            }

            bool first = true;
            for(const Value *s : inst.args)
            {
              if(!first)
                lineStr += ", ";

              lineStr += ArgToString(s, false);
              first = false;
            }

            lineStr += " ";
            if(inst.opFlags() & InstructionFlags::SingleThread)
              lineStr += "singlethread ";
            switch((inst.opFlags() & InstructionFlags::SuccessOrderMask))
            {
              case InstructionFlags::SuccessUnordered: lineStr += "unordered"; break;
              case InstructionFlags::SuccessMonotonic: lineStr += "monotonic"; break;
              case InstructionFlags::SuccessAcquire: lineStr += "acquire"; break;
              case InstructionFlags::SuccessRelease: lineStr += "release"; break;
              case InstructionFlags::SuccessAcquireRelease: lineStr += "acq_rel"; break;
              case InstructionFlags::SuccessSequentiallyConsistent: lineStr += "seq_cst"; break;
              default: break;
            }
            break;
          }
        }

        if(showDxFuncName)
        {
          if(inst.getFuncCall() && inst.getFuncCall()->name.beginsWith("dx.op.annotateHandle"))
          {
            // AnnotateHandle(res,props)
            if(const Constant *props = cast<Constant>(inst.args[2]))
            {
              const Constant *packed[2];
              if(props && !props->isNULL() && props->getMembers().size() == 2 &&
                 (packed[0] = cast<Constant>(props->getMembers()[0])) != NULL &&
                 (packed[1] = cast<Constant>(props->getMembers()[1])) != NULL)
              {
                uint32_t packedProps[2] = {};
                packedProps[0] = packed[0]->getU32();
                packedProps[1] = packed[1]->getU32();

                bool uav = (packedProps[0] & (1 << 12)) != 0;
                bool rov = (packedProps[0] & (1 << 13)) != 0;
                bool globallyCoherent = (packedProps[0] & (1 << 14)) != 0;
                bool sampelCmpOrCounter = (packedProps[0] & (1 << 15)) != 0;
                ResourceKind resKind = (ResourceKind)(packedProps[0] & 0xFF);
                ResourceClass resClass;
                if(sampelCmpOrCounter && resKind == ResourceKind::Sampler)
                  resKind = ResourceKind::SamplerComparison;
                if(resKind == ResourceKind::Sampler || resKind == ResourceKind::SamplerComparison)
                  resClass = ResourceClass::Sampler;
                else if(resKind == ResourceKind::CBuffer)
                  resClass = ResourceClass::CBuffer;
                else if(uav)
                  resClass = ResourceClass::UAV;
                else
                  resClass = ResourceClass::SRV;

                lineStr += "  resource: ";

                bool srv = (resClass == ResourceClass::SRV);

                ComponentType compType = ComponentType(packedProps[1] & 0xFF);
                uint8_t compCount = (packedProps[1] & 0xFF00) >> 8;

                uint8_t feedbackType = packedProps[1] & 0xFF;

                uint32_t structStride = packedProps[1];

                switch(resKind)
                {
                  case ResourceKind::Unknown: lineStr += "Unknown"; break;
                  case ResourceKind::Texture1D:
                  case ResourceKind::Texture2D:
                  case ResourceKind::Texture2DMS:
                  case ResourceKind::Texture3D:
                  case ResourceKind::TextureCube:
                  case ResourceKind::Texture1DArray:
                  case ResourceKind::Texture2DArray:
                  case ResourceKind::Texture2DMSArray:
                  case ResourceKind::TextureCubeArray:
                  case ResourceKind::TypedBuffer:
                    if(globallyCoherent)
                      lineStr += "globallycoherent ";
                    if(!srv && rov)
                      lineStr += "ROV";
                    else if(!srv)
                      lineStr += "RW";
                    switch(resKind)
                    {
                      case ResourceKind::Texture1D: lineStr += "Texture1D"; break;
                      case ResourceKind::Texture2D: lineStr += "Texture2D"; break;
                      case ResourceKind::Texture2DMS: lineStr += "Texture2DMS"; break;
                      case ResourceKind::Texture3D: lineStr += "Texture3D"; break;
                      case ResourceKind::TextureCube: lineStr += "TextureCube"; break;
                      case ResourceKind::Texture1DArray: lineStr += "Texture1DArray"; break;
                      case ResourceKind::Texture2DArray: lineStr += "Texture2DArray"; break;
                      case ResourceKind::Texture2DMSArray: lineStr += "Texture2DMSArray"; break;
                      case ResourceKind::TextureCubeArray: lineStr += "TextureCubeArray"; break;
                      case ResourceKind::TypedBuffer: lineStr += "TypedBuffer"; break;
                      default: break;
                    }
                    break;
                  case ResourceKind::RTAccelerationStructure:
                    lineStr += "RTAccelerationStructure";
                    break;
                  case ResourceKind::FeedbackTexture2D: lineStr += "FeedbackTexture2D"; break;
                  case ResourceKind::FeedbackTexture2DArray:
                    lineStr += "FeedbackTexture2DArray";
                    break;
                  case ResourceKind::StructuredBuffer:
                    if(globallyCoherent)
                      lineStr += "globallycoherent ";
                    lineStr += srv ? "StructuredBuffer" : "RWStructuredBuffer";
                    lineStr += StringFormat::Fmt("<stride=%u", structStride);
                    if(sampelCmpOrCounter)
                      lineStr += ", counter";
                    lineStr += ">";
                    break;
                  case ResourceKind::StructuredBufferWithCounter:
                    if(globallyCoherent)
                      lineStr += "globallycoherent ";
                    lineStr += srv ? "StructuredBufferWithCounter" : "RWStructuredBufferWithCounter";
                    lineStr += StringFormat::Fmt("<stride=%u>", structStride);
                    break;
                  case ResourceKind::RawBuffer:
                    if(globallyCoherent)
                      lineStr += "globallycoherent ";
                    lineStr += srv ? "ByteAddressBuffer" : "RWByteAddressBuffer";
                    break;
                  case ResourceKind::CBuffer:
                    RDCASSERT(resClass == ResourceClass::CBuffer);
                    lineStr += "CBuffer";
                    break;
                  case ResourceKind::Sampler:
                    RDCASSERT(resClass == ResourceClass::Sampler);
                    lineStr += "SamplerState";
                    break;
                  case ResourceKind::TBuffer:
                    RDCASSERT(resClass == ResourceClass::SRV);
                    lineStr += "TBuffer";
                    break;
                  case ResourceKind::SamplerComparison:
                    RDCASSERT(resClass == ResourceClass::Sampler);
                    lineStr += "SamplerComparisonState";
                    break;
                }

                if(resKind == ResourceKind::FeedbackTexture2D ||
                   resKind == ResourceKind::FeedbackTexture2DArray)
                {
                  if(feedbackType == 0)
                    lineStr += "<MinMip>";
                  else if(feedbackType == 1)
                    lineStr += "<MipRegionUsed>";
                  else
                    lineStr += "<Invalid>";
                }
                else if(resKind == ResourceKind::Texture1D || resKind == ResourceKind::Texture2D ||
                        resKind == ResourceKind::Texture3D || resKind == ResourceKind::TextureCube ||
                        resKind == ResourceKind::Texture1DArray ||
                        resKind == ResourceKind::Texture2DArray ||
                        resKind == ResourceKind::TextureCubeArray ||
                        resKind == ResourceKind::TypedBuffer || resKind == ResourceKind::Texture2DMS ||
                        resKind == ResourceKind::Texture2DMSArray)
                {
                  lineStr += "<";
                  if(compCount > 1)
                    lineStr += StringFormat::Fmt("%dx", compCount);
                  lineStr += StringFormat::Fmt("%s>", ToStr(compType).c_str());
                }
              }
            }
          }
        }
        if(!lineStr.empty())
        {
          lineStr += ";";
          if(!commentStr.empty())
            lineStr += " // " + commentStr;

          m_Disassembly += "  " + lineStr;
          DisassemblyAddNewLine();
        }

        // if this is the last instruction don't print the next block's label
        if(funcIdx == func.instructions.size() - 1)
          break;

        if(inst.op == Operation::Branch || inst.op == Operation::Unreachable ||
           inst.op == Operation::Switch || inst.op == Operation::Ret)
        {
          DisassemblyAddNewLine();

          curBlock++;

          rdcstr labelName;

          if(func.blocks[curBlock]->name.empty())
            labelName = StringFormat::Fmt("; <label>:%u", func.blocks[curBlock]->slot);
          else
            labelName =
                StringFormat::Fmt("%s: ", escapeStringIfNeeded(func.blocks[curBlock]->name).c_str());

          labelName.reserve(50);
          while(labelName.size() < 50)
            labelName.push_back(' ');

          labelName += "; preds = ";
          bool first = true;
          for(const Block *pred : func.blocks[curBlock]->preds)
          {
            if(!first)
              labelName += ", ";
            first = false;
            if(pred->name.empty())
              labelName += StringFormat::Fmt("%c%u", DXIL::dxilIdentifier, pred->slot);
            else
              labelName += StringFormat::Fmt("%c%s", DXIL::dxilIdentifier,
                                             escapeStringIfNeeded(pred->name).c_str());
          }

          m_Disassembly += labelName;
          DisassemblyAddNewLine();
        }
      }
      m_Disassembly += "}";
      DisassemblyAddNewLine(2);
    }
    else
    {
      DisassemblyAddNewLine(2);
    }

    m_Accum.exitFunction();
  }

  DisassemblyAddNewLine();

  // TODO: decide how much of this should be output
  // m_Disassembly += DisassembleFuncAttrGroups();
  // m_Disassembly += DisassembleNamedMeta();
  // m_Disassembly += DisassembleMeta();

  m_Disassembly += "\n";
}