SpirvShader: Debug print human readable opcode names

My memory isn't good enough to remember all opcodes by number.

Enum string list stripped in release builds.

Change-Id: Ifa9b9585ee6294a26db5676a34f8ad9c90f527b8
Reviewed-on: https://swiftshader-review.googlesource.com/c/25551
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
Reviewed-by: Chris Forbes <chrisforbes@google.com>
Tested-by: Ben Clayton <bclayton@google.com>
diff --git a/src/Pipeline/SpirvShader.cpp b/src/Pipeline/SpirvShader.cpp
index 42d4d89..87032d9 100644
--- a/src/Pipeline/SpirvShader.cpp
+++ b/src/Pipeline/SpirvShader.cpp
@@ -247,7 +247,7 @@
 				break;
 
 			default:
-				printf("Warning: ignored opcode %u\n", insn.opcode());
+				printf("Warning: ignored opcode %s\n", OpcodeName(insn.opcode()).c_str());
 				break;    // This is OK, these passes are intentionally partial
 			}
 		}
@@ -600,7 +600,7 @@
 			}
 
 			default:
-				UNIMPLEMENTED("Unexpected type in WalkAccessChain");
+				UNIMPLEMENTED("Unexpected type '%s' in WalkAccessChain", OpcodeName(type.definition.opcode()).c_str());
 			}
 		}
 
@@ -1079,7 +1079,7 @@
 				break;
 			}
 			default:
-				printf("emit: ignoring opcode %d\n", insn.opcode());
+				printf("emit: ignoring opcode %s\n", OpcodeName(insn.opcode()).c_str());
 				break;
 			}
 		}
diff --git a/src/Pipeline/SpirvShader.hpp b/src/Pipeline/SpirvShader.hpp
index 64d4fd7..0081149 100644
--- a/src/Pipeline/SpirvShader.hpp
+++ b/src/Pipeline/SpirvShader.hpp
@@ -380,6 +380,10 @@
 
 		SIMD::Int WalkAccessChain(ObjectID id, uint32_t numIndexes, uint32_t const *indexIds, SpirvRoutine *routine) const;
 		uint32_t WalkLiteralAccessChain(TypeID id, uint32_t numIndexes, uint32_t const *indexes) const;
+
+		// OpcodeName returns the name of the opcode op.
+		// If NDEBUG is defined, then OpcodeName will only return the numerical code.
+		static std::string OpcodeName(spv::Op op);
 	};
 
 	class SpirvRoutine
diff --git a/src/Pipeline/SpirvShader_dbg.cpp b/src/Pipeline/SpirvShader_dbg.cpp
new file mode 100644
index 0000000..feacb87
--- /dev/null
+++ b/src/Pipeline/SpirvShader_dbg.cpp
@@ -0,0 +1,408 @@
+// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// This file contains code used to aid debugging.
+
+#include <spirv/unified1/spirv.hpp>
+#include "SpirvShader.hpp"
+
+namespace sw
+{
+	std::string SpirvShader::OpcodeName(spv::Op op)
+	{
+		switch(op){
+#ifndef NDEBUG
+		case spv::OpNop: return "Nop";
+		case spv::OpUndef: return "Undef";
+		case spv::OpSourceContinued: return "SourceContinued";
+		case spv::OpSource: return "Source";
+		case spv::OpSourceExtension: return "SourceExtension";
+		case spv::OpName: return "Name";
+		case spv::OpMemberName: return "MemberName";
+		case spv::OpString: return "String";
+		case spv::OpLine: return "Line";
+		case spv::OpExtension: return "Extension";
+		case spv::OpExtInstImport: return "ExtInstImport";
+		case spv::OpExtInst: return "ExtInst";
+		case spv::OpMemoryModel: return "MemoryModel";
+		case spv::OpEntryPoint: return "EntryPoint";
+		case spv::OpExecutionMode: return "ExecutionMode";
+		case spv::OpCapability: return "Capability";
+		case spv::OpTypeVoid: return "TypeVoid";
+		case spv::OpTypeBool: return "TypeBool";
+		case spv::OpTypeInt: return "TypeInt";
+		case spv::OpTypeFloat: return "TypeFloat";
+		case spv::OpTypeVector: return "TypeVector";
+		case spv::OpTypeMatrix: return "TypeMatrix";
+		case spv::OpTypeImage: return "TypeImage";
+		case spv::OpTypeSampler: return "TypeSampler";
+		case spv::OpTypeSampledImage: return "TypeSampledImage";
+		case spv::OpTypeArray: return "TypeArray";
+		case spv::OpTypeRuntimeArray: return "TypeRuntimeArray";
+		case spv::OpTypeStruct: return "TypeStruct";
+		case spv::OpTypeOpaque: return "TypeOpaque";
+		case spv::OpTypePointer: return "TypePointer";
+		case spv::OpTypeFunction: return "TypeFunction";
+		case spv::OpTypeEvent: return "TypeEvent";
+		case spv::OpTypeDeviceEvent: return "TypeDeviceEvent";
+		case spv::OpTypeReserveId: return "TypeReserveId";
+		case spv::OpTypeQueue: return "TypeQueue";
+		case spv::OpTypePipe: return "TypePipe";
+		case spv::OpTypeForwardPointer: return "TypeForwardPointer";
+		case spv::OpConstantTrue: return "ConstantTrue";
+		case spv::OpConstantFalse: return "ConstantFalse";
+		case spv::OpConstant: return "Constant";
+		case spv::OpConstantComposite: return "ConstantComposite";
+		case spv::OpConstantSampler: return "ConstantSampler";
+		case spv::OpConstantNull: return "ConstantNull";
+		case spv::OpSpecConstantTrue: return "SpecConstantTrue";
+		case spv::OpSpecConstantFalse: return "SpecConstantFalse";
+		case spv::OpSpecConstant: return "SpecConstant";
+		case spv::OpSpecConstantComposite: return "SpecConstantComposite";
+		case spv::OpSpecConstantOp: return "SpecConstantOp";
+		case spv::OpFunction: return "Function";
+		case spv::OpFunctionParameter: return "FunctionParameter";
+		case spv::OpFunctionEnd: return "FunctionEnd";
+		case spv::OpFunctionCall: return "FunctionCall";
+		case spv::OpVariable: return "Variable";
+		case spv::OpImageTexelPointer: return "ImageTexelPointer";
+		case spv::OpLoad: return "Load";
+		case spv::OpStore: return "Store";
+		case spv::OpCopyMemory: return "CopyMemory";
+		case spv::OpCopyMemorySized: return "CopyMemorySized";
+		case spv::OpAccessChain: return "AccessChain";
+		case spv::OpInBoundsAccessChain: return "InBoundsAccessChain";
+		case spv::OpPtrAccessChain: return "PtrAccessChain";
+		case spv::OpArrayLength: return "ArrayLength";
+		case spv::OpGenericPtrMemSemantics: return "GenericPtrMemSemantics";
+		case spv::OpInBoundsPtrAccessChain: return "InBoundsPtrAccessChain";
+		case spv::OpDecorate: return "Decorate";
+		case spv::OpMemberDecorate: return "MemberDecorate";
+		case spv::OpDecorationGroup: return "DecorationGroup";
+		case spv::OpGroupDecorate: return "GroupDecorate";
+		case spv::OpGroupMemberDecorate: return "GroupMemberDecorate";
+		case spv::OpVectorExtractDynamic: return "VectorExtractDynamic";
+		case spv::OpVectorInsertDynamic: return "VectorInsertDynamic";
+		case spv::OpVectorShuffle: return "VectorShuffle";
+		case spv::OpCompositeConstruct: return "CompositeConstruct";
+		case spv::OpCompositeExtract: return "CompositeExtract";
+		case spv::OpCompositeInsert: return "CompositeInsert";
+		case spv::OpCopyObject: return "CopyObject";
+		case spv::OpTranspose: return "Transpose";
+		case spv::OpSampledImage: return "SampledImage";
+		case spv::OpImageSampleImplicitLod: return "ImageSampleImplicitLod";
+		case spv::OpImageSampleExplicitLod: return "ImageSampleExplicitLod";
+		case spv::OpImageSampleDrefImplicitLod: return "ImageSampleDrefImplicitLod";
+		case spv::OpImageSampleDrefExplicitLod: return "ImageSampleDrefExplicitLod";
+		case spv::OpImageSampleProjImplicitLod: return "ImageSampleProjImplicitLod";
+		case spv::OpImageSampleProjExplicitLod: return "ImageSampleProjExplicitLod";
+		case spv::OpImageSampleProjDrefImplicitLod: return "ImageSampleProjDrefImplicitLod";
+		case spv::OpImageSampleProjDrefExplicitLod: return "ImageSampleProjDrefExplicitLod";
+		case spv::OpImageFetch: return "ImageFetch";
+		case spv::OpImageGather: return "ImageGather";
+		case spv::OpImageDrefGather: return "ImageDrefGather";
+		case spv::OpImageRead: return "ImageRead";
+		case spv::OpImageWrite: return "ImageWrite";
+		case spv::OpImage: return "Image";
+		case spv::OpImageQueryFormat: return "ImageQueryFormat";
+		case spv::OpImageQueryOrder: return "ImageQueryOrder";
+		case spv::OpImageQuerySizeLod: return "ImageQuerySizeLod";
+		case spv::OpImageQuerySize: return "ImageQuerySize";
+		case spv::OpImageQueryLod: return "ImageQueryLod";
+		case spv::OpImageQueryLevels: return "ImageQueryLevels";
+		case spv::OpImageQuerySamples: return "ImageQuerySamples";
+		case spv::OpConvertFToU: return "ConvertFToU";
+		case spv::OpConvertFToS: return "ConvertFToS";
+		case spv::OpConvertSToF: return "ConvertSToF";
+		case spv::OpConvertUToF: return "ConvertUToF";
+		case spv::OpUConvert: return "UConvert";
+		case spv::OpSConvert: return "SConvert";
+		case spv::OpFConvert: return "FConvert";
+		case spv::OpQuantizeToF16: return "QuantizeToF16";
+		case spv::OpConvertPtrToU: return "ConvertPtrToU";
+		case spv::OpSatConvertSToU: return "SatConvertSToU";
+		case spv::OpSatConvertUToS: return "SatConvertUToS";
+		case spv::OpConvertUToPtr: return "ConvertUToPtr";
+		case spv::OpPtrCastToGeneric: return "PtrCastToGeneric";
+		case spv::OpGenericCastToPtr: return "GenericCastToPtr";
+		case spv::OpGenericCastToPtrExplicit: return "GenericCastToPtrExplicit";
+		case spv::OpBitcast: return "Bitcast";
+		case spv::OpSNegate: return "SNegate";
+		case spv::OpFNegate: return "FNegate";
+		case spv::OpIAdd: return "IAdd";
+		case spv::OpFAdd: return "FAdd";
+		case spv::OpISub: return "ISub";
+		case spv::OpFSub: return "FSub";
+		case spv::OpIMul: return "IMul";
+		case spv::OpFMul: return "FMul";
+		case spv::OpUDiv: return "UDiv";
+		case spv::OpSDiv: return "SDiv";
+		case spv::OpFDiv: return "FDiv";
+		case spv::OpUMod: return "UMod";
+		case spv::OpSRem: return "SRem";
+		case spv::OpSMod: return "SMod";
+		case spv::OpFRem: return "FRem";
+		case spv::OpFMod: return "FMod";
+		case spv::OpVectorTimesScalar: return "VectorTimesScalar";
+		case spv::OpMatrixTimesScalar: return "MatrixTimesScalar";
+		case spv::OpVectorTimesMatrix: return "VectorTimesMatrix";
+		case spv::OpMatrixTimesVector: return "MatrixTimesVector";
+		case spv::OpMatrixTimesMatrix: return "MatrixTimesMatrix";
+		case spv::OpOuterProduct: return "OuterProduct";
+		case spv::OpDot: return "Dot";
+		case spv::OpIAddCarry: return "IAddCarry";
+		case spv::OpISubBorrow: return "ISubBorrow";
+		case spv::OpUMulExtended: return "UMulExtended";
+		case spv::OpSMulExtended: return "SMulExtended";
+		case spv::OpAny: return "Any";
+		case spv::OpAll: return "All";
+		case spv::OpIsNan: return "IsNan";
+		case spv::OpIsInf: return "IsInf";
+		case spv::OpIsFinite: return "IsFinite";
+		case spv::OpIsNormal: return "IsNormal";
+		case spv::OpSignBitSet: return "SignBitSet";
+		case spv::OpLessOrGreater: return "LessOrGreater";
+		case spv::OpOrdered: return "Ordered";
+		case spv::OpUnordered: return "Unordered";
+		case spv::OpLogicalEqual: return "LogicalEqual";
+		case spv::OpLogicalNotEqual: return "LogicalNotEqual";
+		case spv::OpLogicalOr: return "LogicalOr";
+		case spv::OpLogicalAnd: return "LogicalAnd";
+		case spv::OpLogicalNot: return "LogicalNot";
+		case spv::OpSelect: return "Select";
+		case spv::OpIEqual: return "IEqual";
+		case spv::OpINotEqual: return "INotEqual";
+		case spv::OpUGreaterThan: return "UGreaterThan";
+		case spv::OpSGreaterThan: return "SGreaterThan";
+		case spv::OpUGreaterThanEqual: return "UGreaterThanEqual";
+		case spv::OpSGreaterThanEqual: return "SGreaterThanEqual";
+		case spv::OpULessThan: return "ULessThan";
+		case spv::OpSLessThan: return "SLessThan";
+		case spv::OpULessThanEqual: return "ULessThanEqual";
+		case spv::OpSLessThanEqual: return "SLessThanEqual";
+		case spv::OpFOrdEqual: return "FOrdEqual";
+		case spv::OpFUnordEqual: return "FUnordEqual";
+		case spv::OpFOrdNotEqual: return "FOrdNotEqual";
+		case spv::OpFUnordNotEqual: return "FUnordNotEqual";
+		case spv::OpFOrdLessThan: return "FOrdLessThan";
+		case spv::OpFUnordLessThan: return "FUnordLessThan";
+		case spv::OpFOrdGreaterThan: return "FOrdGreaterThan";
+		case spv::OpFUnordGreaterThan: return "FUnordGreaterThan";
+		case spv::OpFOrdLessThanEqual: return "FOrdLessThanEqual";
+		case spv::OpFUnordLessThanEqual: return "FUnordLessThanEqual";
+		case spv::OpFOrdGreaterThanEqual: return "FOrdGreaterThanEqual";
+		case spv::OpFUnordGreaterThanEqual: return "FUnordGreaterThanEqual";
+		case spv::OpShiftRightLogical: return "ShiftRightLogical";
+		case spv::OpShiftRightArithmetic: return "ShiftRightArithmetic";
+		case spv::OpShiftLeftLogical: return "ShiftLeftLogical";
+		case spv::OpBitwiseOr: return "BitwiseOr";
+		case spv::OpBitwiseXor: return "BitwiseXor";
+		case spv::OpBitwiseAnd: return "BitwiseAnd";
+		case spv::OpNot: return "Not";
+		case spv::OpBitFieldInsert: return "BitFieldInsert";
+		case spv::OpBitFieldSExtract: return "BitFieldSExtract";
+		case spv::OpBitFieldUExtract: return "BitFieldUExtract";
+		case spv::OpBitReverse: return "BitReverse";
+		case spv::OpBitCount: return "BitCount";
+		case spv::OpDPdx: return "DPdx";
+		case spv::OpDPdy: return "DPdy";
+		case spv::OpFwidth: return "Fwidth";
+		case spv::OpDPdxFine: return "DPdxFine";
+		case spv::OpDPdyFine: return "DPdyFine";
+		case spv::OpFwidthFine: return "FwidthFine";
+		case spv::OpDPdxCoarse: return "DPdxCoarse";
+		case spv::OpDPdyCoarse: return "DPdyCoarse";
+		case spv::OpFwidthCoarse: return "FwidthCoarse";
+		case spv::OpEmitVertex: return "EmitVertex";
+		case spv::OpEndPrimitive: return "EndPrimitive";
+		case spv::OpEmitStreamVertex: return "EmitStreamVertex";
+		case spv::OpEndStreamPrimitive: return "EndStreamPrimitive";
+		case spv::OpControlBarrier: return "ControlBarrier";
+		case spv::OpMemoryBarrier: return "MemoryBarrier";
+		case spv::OpAtomicLoad: return "AtomicLoad";
+		case spv::OpAtomicStore: return "AtomicStore";
+		case spv::OpAtomicExchange: return "AtomicExchange";
+		case spv::OpAtomicCompareExchange: return "AtomicCompareExchange";
+		case spv::OpAtomicCompareExchangeWeak: return "AtomicCompareExchangeWeak";
+		case spv::OpAtomicIIncrement: return "AtomicIIncrement";
+		case spv::OpAtomicIDecrement: return "AtomicIDecrement";
+		case spv::OpAtomicIAdd: return "AtomicIAdd";
+		case spv::OpAtomicISub: return "AtomicISub";
+		case spv::OpAtomicSMin: return "AtomicSMin";
+		case spv::OpAtomicUMin: return "AtomicUMin";
+		case spv::OpAtomicSMax: return "AtomicSMax";
+		case spv::OpAtomicUMax: return "AtomicUMax";
+		case spv::OpAtomicAnd: return "AtomicAnd";
+		case spv::OpAtomicOr: return "AtomicOr";
+		case spv::OpAtomicXor: return "AtomicXor";
+		case spv::OpPhi: return "Phi";
+		case spv::OpLoopMerge: return "LoopMerge";
+		case spv::OpSelectionMerge: return "SelectionMerge";
+		case spv::OpLabel: return "Label";
+		case spv::OpBranch: return "Branch";
+		case spv::OpBranchConditional: return "BranchConditional";
+		case spv::OpSwitch: return "Switch";
+		case spv::OpKill: return "Kill";
+		case spv::OpReturn: return "Return";
+		case spv::OpReturnValue: return "ReturnValue";
+		case spv::OpUnreachable: return "Unreachable";
+		case spv::OpLifetimeStart: return "LifetimeStart";
+		case spv::OpLifetimeStop: return "LifetimeStop";
+		case spv::OpGroupAsyncCopy: return "GroupAsyncCopy";
+		case spv::OpGroupWaitEvents: return "GroupWaitEvents";
+		case spv::OpGroupAll: return "GroupAll";
+		case spv::OpGroupAny: return "GroupAny";
+		case spv::OpGroupBroadcast: return "GroupBroadcast";
+		case spv::OpGroupIAdd: return "GroupIAdd";
+		case spv::OpGroupFAdd: return "GroupFAdd";
+		case spv::OpGroupFMin: return "GroupFMin";
+		case spv::OpGroupUMin: return "GroupUMin";
+		case spv::OpGroupSMin: return "GroupSMin";
+		case spv::OpGroupFMax: return "GroupFMax";
+		case spv::OpGroupUMax: return "GroupUMax";
+		case spv::OpGroupSMax: return "GroupSMax";
+		case spv::OpReadPipe: return "ReadPipe";
+		case spv::OpWritePipe: return "WritePipe";
+		case spv::OpReservedReadPipe: return "ReservedReadPipe";
+		case spv::OpReservedWritePipe: return "ReservedWritePipe";
+		case spv::OpReserveReadPipePackets: return "ReserveReadPipePackets";
+		case spv::OpReserveWritePipePackets: return "ReserveWritePipePackets";
+		case spv::OpCommitReadPipe: return "CommitReadPipe";
+		case spv::OpCommitWritePipe: return "CommitWritePipe";
+		case spv::OpIsValidReserveId: return "IsValidReserveId";
+		case spv::OpGetNumPipePackets: return "GetNumPipePackets";
+		case spv::OpGetMaxPipePackets: return "GetMaxPipePackets";
+		case spv::OpGroupReserveReadPipePackets: return "GroupReserveReadPipePackets";
+		case spv::OpGroupReserveWritePipePackets: return "GroupReserveWritePipePackets";
+		case spv::OpGroupCommitReadPipe: return "GroupCommitReadPipe";
+		case spv::OpGroupCommitWritePipe: return "GroupCommitWritePipe";
+		case spv::OpEnqueueMarker: return "EnqueueMarker";
+		case spv::OpEnqueueKernel: return "EnqueueKernel";
+		case spv::OpGetKernelNDrangeSubGroupCount: return "GetKernelNDrangeSubGroupCount";
+		case spv::OpGetKernelNDrangeMaxSubGroupSize: return "GetKernelNDrangeMaxSubGroupSize";
+		case spv::OpGetKernelWorkGroupSize: return "GetKernelWorkGroupSize";
+		case spv::OpGetKernelPreferredWorkGroupSizeMultiple: return "GetKernelPreferredWorkGroupSizeMultiple";
+		case spv::OpRetainEvent: return "RetainEvent";
+		case spv::OpReleaseEvent: return "ReleaseEvent";
+		case spv::OpCreateUserEvent: return "CreateUserEvent";
+		case spv::OpIsValidEvent: return "IsValidEvent";
+		case spv::OpSetUserEventStatus: return "SetUserEventStatus";
+		case spv::OpCaptureEventProfilingInfo: return "CaptureEventProfilingInfo";
+		case spv::OpGetDefaultQueue: return "GetDefaultQueue";
+		case spv::OpBuildNDRange: return "BuildNDRange";
+		case spv::OpImageSparseSampleImplicitLod: return "ImageSparseSampleImplicitLod";
+		case spv::OpImageSparseSampleExplicitLod: return "ImageSparseSampleExplicitLod";
+		case spv::OpImageSparseSampleDrefImplicitLod: return "ImageSparseSampleDrefImplicitLod";
+		case spv::OpImageSparseSampleDrefExplicitLod: return "ImageSparseSampleDrefExplicitLod";
+		case spv::OpImageSparseSampleProjImplicitLod: return "ImageSparseSampleProjImplicitLod";
+		case spv::OpImageSparseSampleProjExplicitLod: return "ImageSparseSampleProjExplicitLod";
+		case spv::OpImageSparseSampleProjDrefImplicitLod: return "ImageSparseSampleProjDrefImplicitLod";
+		case spv::OpImageSparseSampleProjDrefExplicitLod: return "ImageSparseSampleProjDrefExplicitLod";
+		case spv::OpImageSparseFetch: return "ImageSparseFetch";
+		case spv::OpImageSparseGather: return "ImageSparseGather";
+		case spv::OpImageSparseDrefGather: return "ImageSparseDrefGather";
+		case spv::OpImageSparseTexelsResident: return "ImageSparseTexelsResident";
+		case spv::OpNoLine: return "NoLine";
+		case spv::OpAtomicFlagTestAndSet: return "AtomicFlagTestAndSet";
+		case spv::OpAtomicFlagClear: return "AtomicFlagClear";
+		case spv::OpImageSparseRead: return "ImageSparseRead";
+		case spv::OpSizeOf: return "SizeOf";
+		case spv::OpTypePipeStorage: return "TypePipeStorage";
+		case spv::OpConstantPipeStorage: return "ConstantPipeStorage";
+		case spv::OpCreatePipeFromPipeStorage: return "CreatePipeFromPipeStorage";
+		case spv::OpGetKernelLocalSizeForSubgroupCount: return "GetKernelLocalSizeForSubgroupCount";
+		case spv::OpGetKernelMaxNumSubgroups: return "GetKernelMaxNumSubgroups";
+		case spv::OpTypeNamedBarrier: return "TypeNamedBarrier";
+		case spv::OpNamedBarrierInitialize: return "NamedBarrierInitialize";
+		case spv::OpMemoryNamedBarrier: return "MemoryNamedBarrier";
+		case spv::OpModuleProcessed: return "ModuleProcessed";
+		case spv::OpExecutionModeId: return "ExecutionModeId";
+		case spv::OpDecorateId: return "DecorateId";
+		case spv::OpGroupNonUniformElect: return "GroupNonUniformElect";
+		case spv::OpGroupNonUniformAll: return "GroupNonUniformAll";
+		case spv::OpGroupNonUniformAny: return "GroupNonUniformAny";
+		case spv::OpGroupNonUniformAllEqual: return "GroupNonUniformAllEqual";
+		case spv::OpGroupNonUniformBroadcast: return "GroupNonUniformBroadcast";
+		case spv::OpGroupNonUniformBroadcastFirst: return "GroupNonUniformBroadcastFirst";
+		case spv::OpGroupNonUniformBallot: return "GroupNonUniformBallot";
+		case spv::OpGroupNonUniformInverseBallot: return "GroupNonUniformInverseBallot";
+		case spv::OpGroupNonUniformBallotBitExtract: return "GroupNonUniformBallotBitExtract";
+		case spv::OpGroupNonUniformBallotBitCount: return "GroupNonUniformBallotBitCount";
+		case spv::OpGroupNonUniformBallotFindLSB: return "GroupNonUniformBallotFindLSB";
+		case spv::OpGroupNonUniformBallotFindMSB: return "GroupNonUniformBallotFindMSB";
+		case spv::OpGroupNonUniformShuffle: return "GroupNonUniformShuffle";
+		case spv::OpGroupNonUniformShuffleXor: return "GroupNonUniformShuffleXor";
+		case spv::OpGroupNonUniformShuffleUp: return "GroupNonUniformShuffleUp";
+		case spv::OpGroupNonUniformShuffleDown: return "GroupNonUniformShuffleDown";
+		case spv::OpGroupNonUniformIAdd: return "GroupNonUniformIAdd";
+		case spv::OpGroupNonUniformFAdd: return "GroupNonUniformFAdd";
+		case spv::OpGroupNonUniformIMul: return "GroupNonUniformIMul";
+		case spv::OpGroupNonUniformFMul: return "GroupNonUniformFMul";
+		case spv::OpGroupNonUniformSMin: return "GroupNonUniformSMin";
+		case spv::OpGroupNonUniformUMin: return "GroupNonUniformUMin";
+		case spv::OpGroupNonUniformFMin: return "GroupNonUniformFMin";
+		case spv::OpGroupNonUniformSMax: return "GroupNonUniformSMax";
+		case spv::OpGroupNonUniformUMax: return "GroupNonUniformUMax";
+		case spv::OpGroupNonUniformFMax: return "GroupNonUniformFMax";
+		case spv::OpGroupNonUniformBitwiseAnd: return "GroupNonUniformBitwiseAnd";
+		case spv::OpGroupNonUniformBitwiseOr: return "GroupNonUniformBitwiseOr";
+		case spv::OpGroupNonUniformBitwiseXor: return "GroupNonUniformBitwiseXor";
+		case spv::OpGroupNonUniformLogicalAnd: return "GroupNonUniformLogicalAnd";
+		case spv::OpGroupNonUniformLogicalOr: return "GroupNonUniformLogicalOr";
+		case spv::OpGroupNonUniformLogicalXor: return "GroupNonUniformLogicalXor";
+		case spv::OpGroupNonUniformQuadBroadcast: return "GroupNonUniformQuadBroadcast";
+		case spv::OpGroupNonUniformQuadSwap: return "GroupNonUniformQuadSwap";
+		case spv::OpSubgroupBallotKHR: return "SubgroupBallotKHR";
+		case spv::OpSubgroupFirstInvocationKHR: return "SubgroupFirstInvocationKHR";
+		case spv::OpSubgroupAllKHR: return "SubgroupAllKHR";
+		case spv::OpSubgroupAnyKHR: return "SubgroupAnyKHR";
+		case spv::OpSubgroupAllEqualKHR: return "SubgroupAllEqualKHR";
+		case spv::OpSubgroupReadInvocationKHR: return "SubgroupReadInvocationKHR";
+		case spv::OpGroupIAddNonUniformAMD: return "GroupIAddNonUniformAMD";
+		case spv::OpGroupFAddNonUniformAMD: return "GroupFAddNonUniformAMD";
+		case spv::OpGroupFMinNonUniformAMD: return "GroupFMinNonUniformAMD";
+		case spv::OpGroupUMinNonUniformAMD: return "GroupUMinNonUniformAMD";
+		case spv::OpGroupSMinNonUniformAMD: return "GroupSMinNonUniformAMD";
+		case spv::OpGroupFMaxNonUniformAMD: return "GroupFMaxNonUniformAMD";
+		case spv::OpGroupUMaxNonUniformAMD: return "GroupUMaxNonUniformAMD";
+		case spv::OpGroupSMaxNonUniformAMD: return "GroupSMaxNonUniformAMD";
+		case spv::OpFragmentMaskFetchAMD: return "FragmentMaskFetchAMD";
+		case spv::OpFragmentFetchAMD: return "FragmentFetchAMD";
+		case spv::OpImageSampleFootprintNV: return "ImageSampleFootprintNV";
+		case spv::OpGroupNonUniformPartitionNV: return "GroupNonUniformPartitionNV";
+		case spv::OpWritePackedPrimitiveIndices4x8NV: return "WritePackedPrimitiveIndices4x8NV";
+		case spv::OpReportIntersectionNV: return "ReportIntersectionNV";
+		case spv::OpIgnoreIntersectionNV: return "IgnoreIntersectionNV";
+		case spv::OpTerminateRayNV: return "TerminateRayNV";
+		case spv::OpTraceNV: return "TraceNV";
+		case spv::OpTypeAccelerationStructureNV: return "TypeAccelerationStructureNV";
+		case spv::OpExecuteCallableNV: return "ExecuteCallableNV";
+		case spv::OpSubgroupShuffleINTEL: return "SubgroupShuffleINTEL";
+		case spv::OpSubgroupShuffleDownINTEL: return "SubgroupShuffleDownINTEL";
+		case spv::OpSubgroupShuffleUpINTEL: return "SubgroupShuffleUpINTEL";
+		case spv::OpSubgroupShuffleXorINTEL: return "SubgroupShuffleXorINTEL";
+		case spv::OpSubgroupBlockReadINTEL: return "SubgroupBlockReadINTEL";
+		case spv::OpSubgroupBlockWriteINTEL: return "SubgroupBlockWriteINTEL";
+		case spv::OpSubgroupImageBlockReadINTEL: return "SubgroupImageBlockReadINTEL";
+		case spv::OpSubgroupImageBlockWriteINTEL: return "SubgroupImageBlockWriteINTEL";
+		case spv::OpDecorateStringGOOGLE: return "DecorateStringGOOGLE";
+		case spv::OpMemberDecorateStringGOOGLE: return "MemberDecorateStringGOOGLE";
+		case spv::OpMax: return "Max";
+#endif // NDEBUG
+		default:
+			return "Opcode<" + std::to_string(static_cast<int>(op)) + ">";
+		}
+	}
+
+} // namespace sw