Browse Source

Updated spirv-tools.

Бранимир Караџић 5 years ago
parent
commit
d86146c96b
100 changed files with 9932 additions and 485 deletions
  1. 1011 0
      3rdparty/spirv-tools/CHANGES
  2. 685 0
      3rdparty/spirv-tools/README.md
  3. 149 0
      3rdparty/spirv-tools/include/generated/OpenCLDebugInfo100.h
  4. 1 1
      3rdparty/spirv-tools/include/generated/build-version.inc
  5. 2 1
      3rdparty/spirv-tools/include/generated/enum_string_mapping.inc
  6. 1 0
      3rdparty/spirv-tools/include/generated/extension_enum.inc
  7. 39 0
      3rdparty/spirv-tools/include/generated/opencl.debuginfo.100.insts.inc
  8. 69 0
      3rdparty/spirv-tools/include/generated/operand.kinds-unified1.inc
  9. 8 5
      3rdparty/spirv-tools/include/spirv-tools/instrument.hpp
  10. 64 4
      3rdparty/spirv-tools/include/spirv-tools/libspirv.h
  11. 24 1
      3rdparty/spirv-tools/include/spirv-tools/libspirv.hpp
  12. 26 9
      3rdparty/spirv-tools/include/spirv-tools/optimizer.hpp
  13. 20 8
      3rdparty/spirv-tools/source/CMakeLists.txt
  14. 23 4
      3rdparty/spirv-tools/source/binary.cpp
  15. 18 4
      3rdparty/spirv-tools/source/disassemble.cpp
  16. 27 0
      3rdparty/spirv-tools/source/ext_inst.cpp
  17. 6 0
      3rdparty/spirv-tools/source/ext_inst.h
  18. 619 0
      3rdparty/spirv-tools/source/extinst.opencl.debuginfo.100.grammar.json
  19. 36 0
      3rdparty/spirv-tools/source/fuzz/CMakeLists.txt
  20. 131 1
      3rdparty/spirv-tools/source/fuzz/fact_manager.cpp
  21. 53 0
      3rdparty/spirv-tools/source/fuzz/fact_manager.h
  22. 30 3
      3rdparty/spirv-tools/source/fuzz/fuzzer.cpp
  23. 6 2
      3rdparty/spirv-tools/source/fuzz/fuzzer.h
  24. 36 1
      3rdparty/spirv-tools/source/fuzz/fuzzer_context.cpp
  25. 45 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_context.h
  26. 126 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass.cpp
  27. 54 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass.h
  28. 138 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_composite_types.cpp
  29. 61 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_composite_types.h
  30. 64 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_dead_blocks.cpp
  31. 39 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_dead_blocks.h
  32. 0 1
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_construct_composites.cpp
  33. 748 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_donate_modules.cpp
  34. 93 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_donate_modules.h
  35. 65 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_merge_blocks.cpp
  36. 38 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_merge_blocks.h
  37. 99 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_outline_functions.cpp
  38. 40 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_outline_functions.h
  39. 116 37
      3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp
  40. 24 3
      3rdparty/spirv-tools/source/fuzz/fuzzer_util.h
  41. 68 0
      3rdparty/spirv-tools/source/fuzz/instruction_message.cpp
  42. 43 0
      3rdparty/spirv-tools/source/fuzz/instruction_message.h
  43. 385 1
      3rdparty/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto
  44. 55 0
      3rdparty/spirv-tools/source/fuzz/transformation.cpp
  45. 9 0
      3rdparty/spirv-tools/source/fuzz/transformation.h
  46. 130 0
      3rdparty/spirv-tools/source/fuzz/transformation_add_constant_composite.cpp
  47. 58 0
      3rdparty/spirv-tools/source/fuzz/transformation_add_constant_composite.h
  48. 169 0
      3rdparty/spirv-tools/source/fuzz/transformation_add_dead_block.cpp
  49. 63 0
      3rdparty/spirv-tools/source/fuzz/transformation_add_dead_block.h
  50. 2 9
      3rdparty/spirv-tools/source/fuzz/transformation_add_dead_break.cpp
  51. 2 9
      3rdparty/spirv-tools/source/fuzz/transformation_add_dead_continue.cpp
  52. 921 0
      3rdparty/spirv-tools/source/fuzz/transformation_add_function.cpp
  53. 124 0
      3rdparty/spirv-tools/source/fuzz/transformation_add_function.h
  54. 62 0
      3rdparty/spirv-tools/source/fuzz/transformation_add_global_undef.cpp
  55. 51 0
      3rdparty/spirv-tools/source/fuzz/transformation_add_global_undef.h
  56. 137 0
      3rdparty/spirv-tools/source/fuzz/transformation_add_global_variable.cpp
  57. 61 0
      3rdparty/spirv-tools/source/fuzz/transformation_add_global_variable.h
  58. 88 0
      3rdparty/spirv-tools/source/fuzz/transformation_add_type_array.cpp
  59. 55 0
      3rdparty/spirv-tools/source/fuzz/transformation_add_type_array.h
  60. 113 0
      3rdparty/spirv-tools/source/fuzz/transformation_add_type_function.cpp
  61. 59 0
      3rdparty/spirv-tools/source/fuzz/transformation_add_type_function.h
  62. 71 0
      3rdparty/spirv-tools/source/fuzz/transformation_add_type_matrix.cpp
  63. 53 0
      3rdparty/spirv-tools/source/fuzz/transformation_add_type_matrix.h
  64. 73 0
      3rdparty/spirv-tools/source/fuzz/transformation_add_type_struct.cpp
  65. 54 0
      3rdparty/spirv-tools/source/fuzz/transformation_add_type_struct.h
  66. 69 0
      3rdparty/spirv-tools/source/fuzz/transformation_add_type_vector.cpp
  67. 53 0
      3rdparty/spirv-tools/source/fuzz/transformation_add_type_vector.h
  68. 2 0
      3rdparty/spirv-tools/source/fuzz/transformation_copy_object.h
  69. 81 0
      3rdparty/spirv-tools/source/fuzz/transformation_merge_blocks.cpp
  70. 54 0
      3rdparty/spirv-tools/source/fuzz/transformation_merge_blocks.h
  71. 943 0
      3rdparty/spirv-tools/source/fuzz/transformation_outline_function.cpp
  72. 221 0
      3rdparty/spirv-tools/source/fuzz/transformation_outline_function.h
  73. 8 1
      3rdparty/spirv-tools/source/fuzz/transformation_split_block.cpp
  74. 1 0
      3rdparty/spirv-tools/source/fuzz/transformation_split_block.h
  75. 20 25
      3rdparty/spirv-tools/source/link/linker.cpp
  76. 55 0
      3rdparty/spirv-tools/source/operand.cpp
  77. 7 0
      3rdparty/spirv-tools/source/operand.h
  78. 35 10
      3rdparty/spirv-tools/source/opt/amd_ext_to_khr.cpp
  79. 1 1
      3rdparty/spirv-tools/source/opt/basic_block.h
  80. 10 1
      3rdparty/spirv-tools/source/opt/block_merge_util.cpp
  81. 99 3
      3rdparty/spirv-tools/source/opt/const_folding_rules.cpp
  82. 1 1
      3rdparty/spirv-tools/source/opt/constants.cpp
  83. 2 1
      3rdparty/spirv-tools/source/opt/constants.h
  84. 100 89
      3rdparty/spirv-tools/source/opt/convert_to_half_pass.cpp
  85. 25 11
      3rdparty/spirv-tools/source/opt/convert_to_half_pass.h
  86. 13 7
      3rdparty/spirv-tools/source/opt/dead_branch_elim_pass.cpp
  87. 7 6
      3rdparty/spirv-tools/source/opt/dead_branch_elim_pass.h
  88. 49 45
      3rdparty/spirv-tools/source/opt/folding_rules.cpp
  89. 7 6
      3rdparty/spirv-tools/source/opt/inst_bindless_check_pass.h
  90. 4 4
      3rdparty/spirv-tools/source/opt/inst_buff_addr_check_pass.h
  91. 3 0
      3rdparty/spirv-tools/source/opt/instruction.h
  92. 51 56
      3rdparty/spirv-tools/source/opt/instrument_pass.cpp
  93. 10 1
      3rdparty/spirv-tools/source/opt/instrument_pass.h
  94. 31 0
      3rdparty/spirv-tools/source/opt/ir_context.h
  95. 44 2
      3rdparty/spirv-tools/source/opt/ir_loader.cpp
  96. 57 69
      3rdparty/spirv-tools/source/opt/merge_return_pass.cpp
  97. 38 38
      3rdparty/spirv-tools/source/opt/merge_return_pass.h
  98. 2 0
      3rdparty/spirv-tools/source/opt/module.cpp
  99. 33 0
      3rdparty/spirv-tools/source/opt/module.h
  100. 56 4
      3rdparty/spirv-tools/source/opt/strip_debug_info_pass.cpp

+ 1011 - 0
3rdparty/spirv-tools/CHANGES

@@ -0,0 +1,1011 @@
+Revision history for SPIRV-Tools
+
+v2020.2-dev 2020-02-03
+ - Start v2020.2-dev
+
+v2020.1 2020-02-03
+ - General:
+   - Add support for SPV_KHR_non_semantic_info (#3110)
+   - Support OpenCL.DebugInfo.100 extended instruction set (#3080)
+   - Added support for Vulkan 1.2
+   - Add API function to better handle getting the necessary environment (#3142)
+   - Clarify mapping of target env to SPIR-V version (#3150)
+   - Implement constant folding for many transcendentals (#3166)
+ - Optimizer
+   - Change default version for CreatInstBindlessCheckPass to 2 (#3096, #3119)
+   - Better handling of OpLine on merge blocks (#3130)
+   - Use dummy switch instead of dummy loop in MergeReturn pass. (#3151)
+   - Handle TimeAMD in AmdExtensionToKhrPass. (#3168)
+ - Validator
+   - Fix structured exit validation (#3141)
+ - Reduce
+ - Fuzz
+   - Fuzzer pass to merge blocks (#3097)
+   - Transformation to add a new function to a module (#3114)
+   - Add fuzzer pass to perform module donation (#3117)
+   - Fuzzer passes to create and branch to new dead blocks (#3135)
+   - Fuzzer pass to add composite types (#3171)
+ - Linker:
+   - Remove names and decorations of imported symbols (#3081)
+
+v2019.5 2019-12-11
+ - General:
+   - Export SPIRV-Tools targets on installation
+   - SPIRV-Tools support for SPIR-V 1.5 (#2865)
+   - Add WebGPU SPIR-V Assembler in JavaScript. (#2876)
+   - Add Bazel build configuration. (#2891)
+   - Add support for building with emscripten (#2948)
+   - Update SPIR-V binary header test for SPIR-V 1.5 (#2967)
+   - Add fuzzer for spirv-as call path (#2976)
+   - Improved CMake install step. (#2963)
+   - Add fuzzer for spirv-dis call path (#2977)
+   - Ensure timestamp does not vary with timezone. (#2982)
+   - Add a vscode extension for SPIR-V disassembly files (#2987)
+   - Add iOS as a supported platform (#3001)
+   - utils/vscode: Add SPIR-V language server support
+   - Respect CMAKE_INSTALL_LIBDIR in installed CMake files (#3054)
+   - Permit the debug instructions in WebGPU SPIR-V (#3063)
+   - Add support for Fuchsia. (#3062)
+ - Optimizer
+   - Add descriptor array scalar replacement (#2742)
+   - Add pass to wrap OpKill in a function call (#2790)
+   - Fold FMix during constant folding. (#2818)
+   - Add pass to replace AMD shader ballot extension (#2811)
+   - Add pass to make Float32 operation relax precision (#2808)
+   - Add pass to make relax precision operation Float16 (#2808)
+   - Add pass to replace uses of 3 AMD extensions (#2814)
+   - Fold Min, Max, and Clamp instructions. (#2836)
+   - Better handling of OpKill in continues (#2842,#2922,#2933)
+   - Enable OpTypeCooperativeMatrix specialization (#2927)
+   - Support constant-folding UConvert and SConvert (#2960)
+   - Update Offset to ConstOffset bitmask if operand is constant. (#3024)
+   - Improve RegisterSizePasses (#3059)
+   - Folding: perform add and sub on mismatched integer types (#3084)
+   - Graphics robust access: use signed clamp (#3073)
+   Fixes:
+   - Instrument: Fix version 2 output record write for tess eval shaders. (#2782)
+   - Instrument: Add support for Buffer Device Address extension (#2792)
+   - Fix check for changed binary in API call. (#2798)
+   - For WebGPU<->Vulkan optimization, set correct execution environment (#2834)
+   - Handle OpConstantNull in copy-prop-arrays. (#2870)
+   - Use OpReturn* in wrap-opkill (#2886)
+ - Validator
+   - Add generic builtin validation of target (#2843)
+   - Extra resource interface validation (#2864)
+   - Adding valilidation checks for OpEntryPoint duplicate names and execution mode (#2862)
+   - Relaxed bitcast with pointers (#2878)
+   - Validate physical storage buffer restrictions (#2930)
+   - Add SPV_KHR_shader_clock validation (#2879, #3013)
+   - Validate that selections are structured (#2962)
+   - Disallow use of OpCompositeExtract/OpCompositeInsert with no indices (#2980)
+   - Check that derivatives operate on 32-bit values (#2983)
+   - Validate array stride does not cause overlap (#3028)
+   - Validate nested constructs (#3068)
+   Fixes:
+   - Fix validation of constant matrices (#2794)
+   - Update "remquor" validation
+   - Only allow previously declared forward refs in structs (#2920)
+ - Reduce
+   - Remove relaxed precision decorations (#2797)
+   - Reduce/fuzz: improve command line args (#2932)
+   - Improve remove unref instr pass (#2945)
+   Fixes:
+ - Fuzz
+   - Fix add-dead-break and add-dead-continue passes to respect dominance (#2838)
+   - Add fuzzer pass to copy objects (#2853)
+   - Add fuzzer pass to replace ids with synonyms (#2857)
+   - Allow validation during spirv-fuzz replay (#2873)
+   - Employ the "swarm testing" idea in spirv-fuzz (#2890)
+   - reduce/fuzz: improve command line args (#2932)
+   - option to convert shader into a form that renders red (#2934)
+   - Add fuzzer pass to change selection controls (#2944)
+   - add transformation and pass to construct composites (#2941)
+   - Add fuzzer pass to change loop controls (#2949)
+   - Add fuzzer pass to change function controls (#2951)
+   - Add fuzzer pass to add NoContraction decorations (#2950)
+   - Add missing functionality for matrix composites (#2974)
+   - Fuzzer pass to adjust memory access operands (#2968)
+   - Transformation to extract from a composite object (#2991)
+   - Vector shuffle transformation (#3015)
+   - Improve debugging facilities (#3074)
+   - Function outlining fuzzer pass (#3078)
+
+
+v2019.4 2019-08-08
+ - General:
+   - Memory model support for SPIR-V 1.4
+   - Add new spirv-fuzz tool
+   - Add option for base branch in check_code_format.sh
+   - Removed MarkV and Stats code. (#2576)
+   - Instrument: Add version 2 of record formats (#2630)
+   - Linker: Better type comparison for OpTypeArray and OpTypeForwardPointer (#2580)
+ - Optimizer
+   - Bindless Validation: Instrument descriptor-based loads and stores (#2583)
+   - Better folding for OpSpecConstantOp (#2585, #2614)
+   - Add in individual flags for Vulkan <-> WebGPU passes (#2615)
+   - Handle nested breaks from switches. (#2624)
+   - Optimizer: Handle array type with OpSpecConstantOp length (#2652)
+   - Perform merge return with single return in loop. (#2714)
+   - Add --preserve-bindings and --preserve-spec-constants (#2693)
+   - Remove Common Uniform Elimination Pass (#2731)
+   - Allow ray tracing shaders in inst bindle check pass. (#2733)
+   - Add pass to inject code for robust-buffer-access semantics (#2771)
+   - Treat access chain indexes as signed in SROA (#2776)
+   - Handle RelaxedPrecision in SROA (#2788)
+   - Add descriptor array scalar replacement (#2742)
+   Fixes:
+   - Handle decorations better in some optimizations (#2716)
+   - Change the order branches are simplified in dead branch elim (#2728)
+   - Fix bug in merge return (#2734)
+   - SSA rewriter: Don't use trivial phis (#2757)
+   - Record correct dominators in merge return (#2760)
+   - Process OpDecorateId in ADCE (#2761)
+   - Fix check for unreachable blocks in merge-return (#2762)
+   - Handle out-of-bounds scalar replacements. (#2767)
+   - Don't move debug or decorations when folding (#2772)
+   - Protect against out-of-bounds references when folding OpCompositeExtract (#2774)
+ - Validator
+   - Validate loop merge (#2579)
+   - Validate construct exits (#2459)
+   - Validate OpenCL memory and addressing model environment rules (#2589)
+   - Validate OpenCL environment rules for OpTypeImage (#2606)
+   - Allow breaks to switch merge from nested construct (#2604)
+   - Validate OpenCL environment rules for OpImageWrite (#2619)
+   - Allow arrays of out per-primitive builtins for mesh shaders (#2617)
+   - Validate OpenCL rules for ImageRead and OpImageSampleExplicitLod (#2643)
+   - Add validation for SPV_EXT_fragment_shader_interlock (#2650)
+   - Add builtin validation for SPV_NV_shader_sm_builtins (#2656)
+   - Add validation for Subgroup builtins (#2637)
+   - Validate variable initializer type (#2668)
+   - Disallow stores to UBOs (#2651)A
+   - Validate Volatile memory semantics bit (#2672)
+   - Basic validation for Component decorations (#2679)
+   - Validate that in OpenGL env block variables have Binding (#2685)
+   - Validate usage of 8- and 16-bit types with only storage capabilities (#2704)
+   - Add validation for SPV_EXT_demote_to_helper_invocation (#2707)
+   - Extra small storage validation (#2732)
+   - For Vulkan, disallow structures containing opaque types (#2546)
+   - Validate storage class OpenCL environment rules for atomics (#2750)
+   - Update OpControlBarriers rules for WebGPU (#2769)
+   - Update OpMemoryBarriers rules for WebGPU (#2775)
+   - Update WebGPU validation rules of OpAtomic*s (#2777)
+   Fixes:
+   - Disallow merge targeting block with OpLoopMerge (#2610)
+   - Update vloadn and vstoren validation to match the OpenCL Extended
+     Instruction Set Specification (#2599)
+   - Update memory scope rules for WebGPU (#2725)
+   - Allow LOD ops in compute shaders with derivative group execution modes (#2752)
+ - Reduce
+   Fixes:
+
+v2019.3 2019-05-14
+ - General:
+   - Require Python 3 since Python 2 will out of service soon.
+   - Add a continuous test that does memory checks using the address sanitizer.
+   - Fix the build files so the SPIRV_USE_SANITIZER=address build works.
+   - Packaging top of tree build artifacts again.
+   - Added support for SPIR-V 1.4. (#2550)
+ - Optimizer
+   - Remove duplicates from list of interface IDs in OpEntryPoint instruction (#2449)
+   - Bindless Validation: Descriptor Initialization Check (#2419)
+   - Add option to validate after each pass (#2462)
+   - Add legalization pass to fix mismatched pointer (#2430, #2535)
+   - Add error messages when the input contains unknown instructions. (#2487)
+   - Add pass to convert from WebGPU Spir-V to Vulkan Spir-V and back. (#2495)
+   Fixes:
+   - #2412: Dead memeber elimination should not change input and output variables.
+   - #2405: Fix OpDot folding of half float vectors.
+   - #2391: Dead branch elim should not fold away back edges.
+   - #2441: Removing decorations when doing constant propagation.
+   - #2455: Maintain inst to block mapping in merge return.
+   - #2453: Fix merge return in the face of breaks.
+   - #2456: Handle dead infinite loops in DCE.
+   - #2458: Handle variable pointer in some optimizations.
+   - #2452: Fix dead branch elimination to handle unreachable blocks better.
+   - #2528: Fix undefined bit shift in sroa.
+   - #2539: Change implementation of post order CFG traversal.
+ - Validator
+   - Add validation of storage classes for WebGPU (#2446)
+   - Add validation for ExecutionMode in WebGPU (#2443)
+   - Implement WebGPU specific CFG validation (#2386)
+   - Allow NonWritable to target struct members. (#2420)
+   - Allow storage type mismatch for parameter in relaxed addressing mode.
+   - Allow non memory objects as parameter in relaxed addressing mode.
+   - Disallow nested Blocks and buffer blocks (#2410).
+   - Add validation for SPV_NV_cooperative_matrix (#2404)
+   - Add --strip-atomic-counter-memory (#2413)
+   - Check OpSampledImage is only passed into valid instructions (#2467)
+   - Handle function decls in Structured CFG analysis (#2474)
+   - Validate that OpUnreacahble is not statically reachable (#2473)
+   - Add pass to generate needed initializers for WebGPU (#2481)
+   - Allow images without format for OpenCL. (#2470)
+   - Remove unreachable block validation (#2525)
+   - Reduce runtime of array layout checks (#2534)
+   - Add validation specific to OpExecutionModeId (#2536)
+   - Validate sign of int types. (#2549)
+   - VK_KHR_uniform_buffer_standard_layout validation (#2562)
+   Fixes:
+   - #2439: Add missing DepthGreater case to Fragment only check.
+   - #2168: Disallow BufferBlock on StorageBuffer variables for Vulkan.
+   - #2408: Restrict and Aliased decorations cannot be applied to the same id.
+   - #2447: Improve function call parameter check.
+ - Reduce
+   - Add Pass to remove unreferenced blocks. (#2398)
+   - Allows passing options to the validator. (#2401)
+   - Improve reducer algorithm and other changes (#2472)
+   - Add Pass to remove selections (#2485)
+   - Add passes to simplify branches (#2507)
+   Fixes:
+   - #2478: fix loop to selection pass for loops with combined header/continue block
+
+v2019.2 2019-02-20
+ - General:
+   - Support SPV_EXT_physical_storage_buffer
+   - A number of memory leak have been fixed.
+   - Removed use of deprecated Google test macro:
+   - Changed the BUILD.gn to only build tests in Chromium.
+ - Optimizer
+   - Upgrade memory model improvments for modf and frexp.
+   - Add a new pass to move loads closer to their uses: code sinking.
+   - Invalidating the type manager now invalidates the constnat manager.
+   - Expand instrumentation pass for bindless bounds checking to runtime-sized descriptor arrays.
+   - Add a new pass that removes members from structs that are not used: dead member elimination.
+   Fixes:
+   - #2292: Remove undefined behaviour when folding bit shifts.
+   - #2294: Fixes for instrumentation code.
+   - #2293: Fix overflow when folding -INT_MIN.
+   - #2374: Don't merge unreachable blocks when merging blocks.
+ - Validator
+   - Support SPV_KHR_no_integer_wrap and related decorations.
+   - Validate Vulkan rules for OpTypeRuntimeArray.
+   - Validate NonWritable decoration.
+   - Many WebGPU specific validation rules were added.
+   - Validate variable pointer related function call rules.
+   - Better error messages.
+   Fixes:
+   - #2307: Check forwards references in OpTypeArray.
+   - #2315, #2303: Fixed the layout check for relaxed layout.
+   - #1628: Emit an error when an OpSwitch target is not an OpLabel.
+ - Reduce
+   - Added more documentation for spirv-reduce.
+   - Add ability to remove OpPhi instructions.
+   - Add ability to merge two basic blocks.
+   - Add ability to remove unused functions and unused basic blocks.
+   Fixes:
+
+v2019.1 2019-01-07
+ - General:
+   - Created a new tool called spirv-reduce.
+   - Add cmake option to turn off SPIRV_TIMER_ENABLED (#2103)
+   - New optimization pass to update the memory model from GLSL450 to VulkanKHR.
+   - Recognize OpTypeAccelerationStructureNV as a type instruction and ray tracing storage classes.
+   - Fix GCC8 build.
+   - Add --target-env flag to spirv-opt.
+   - Add --webgpu-mode flag to run optimizations for webgpu.
+   - The output disassembled line number stead of byte offset in validation errors. (#2091)
+ - Optimizer
+   - Added the instrumentation passes for bindless validation.
+   - Added passes to help preserve OpLine information (#2027)
+   - Add basic support for EXT_fragment_invocation_density (#2100)
+   - Fix invalid OpPhi generated by merge-return. (#2172)
+   - Constant and type manager have been turned into analysies. (#2251)
+   Fixes:
+   - #2018: Don't inline functions with a return in a structured CFG contstruct.
+   - #2047: Fix bug in folding when volatile stores are present.
+   - #2053: Fix check for when folding floating pointer values is allowed.
+   - #2130: Don't inline recursive functions.
+   - #2202: Handle multiple edges between two basic blocks in SSA-rewriter.
+   - #2205: Don't unswitch a latch condition during loop unswitch.
+   - #2245: Don't fold branch in loop unswitch.  Run dead branch elimination to fold them.
+   - #2204: Fix eliminate common uniform to place OpPhi instructions correctly.
+   - #2247: Fix type mismatches caused by scalar replacement.
+   - #2248: Fix missing OpPhi after merge return.
+   - #2211: After merge return, fix invalid continue target.
+   - #2210: Fix loop invariant code motion to not place code between merge instruction and branch.
+   - #2258: Handle CompositeInsert with no indices in VDCE.
+   - #2261: Have replace load size handle extact with no index.
+ - Validator
+   - Changed the naming convention of outputing ids with names in diagnostic messages.
+   - Added validation rules for UniformConstant variables in Vulkan.
+   - #1949: Validate uniform variable type in Vulkan
+   - Ensure for OpVariable that result type and storage class operand agree (#2052)
+   - Validator: Support VK_EXT_scalar_block_layout
+   - Added Vulkan memory model semantics validation
+   - Added validation checkes spefic to WebGPU environment.
+   - Add support for VK_EXT_Transform_feedback capabilities (#2088)
+   - Add validation for OpArrayLength. (#2117)
+   - Ensure that function parameter's type is not void (#2118)
+   - Validate pointer variables (#2111)
+   - Add check for QueueFamilyKHMR memory scope (#2144)
+   - Validate PushConstants annotation and type (#2140)
+   - Allow Float16/Int8 for Vulkan 1.0 (#2153)
+   - Check binding annotations in resource variables (#2151, #2167)
+   - Validate OpForwardPointer (#2156)
+   - Validate operation for OpSpecConstantOp (#2260)
+   Fixes:
+   - #2049: Allow InstanceId for NV ray tracing
+ - Reduce
+   - Initial commit wit a few passes to reduce test cases.
+   - Validation is run after each reduction step.
+   Fixes:
+
+
+v2018.6 2018-11-07
+ - General:
+   - Added support for the Nvidia Turing and ray tracing extensions.
+   - Make C++11 the CXX standard in CMakeLists.txt.
+   - Enabled a parallel build for MSVC.
+   - Enable pre-compiled headers for MSVC.
+   - Added a code of conduct.
+   - EFFCEE and RE2 are now required when build the tests.
+ - Optimizer
+   - Unrolling loops marked for unrolling in the legalization passes.
+   - Improved the compile time of loop unrolling.
+   - Changee merge-return to create a dummy loop around the function.
+   - Small improvement to merge-blocks to allow it to merge more often.
+   - Enforce an upper bound for the ids, and add option to set it.
+   - #1966: Report error if there are unreachable block before running merge return
+   Fixes:
+   - #1917: Allow 0 (meaning unlimited) as a parameter to --scalar-replacement
+   - #1915: Improve handling of group decorations.
+   - #1942: Fix incorrect uses of the constant manager.  Avoids type mismatches in generated code.
+   - #1997: Fix dead branch elimination when there is a loop in folded selection.
+   - #1991: Fixes legality check in if-conversion.
+   - #1987: Add nullptr check to array copy propagation.
+   - #1984: Better handling of OpUnreachable in ADCE.
+   - #1983: Run merge return on reachable functions only.
+   - #1956: Handled atomic operations in ADCE.
+   - #1963: Fold integer divisions by 0 to 0.
+   - #2019: Handle MemberDecorateStringGOOGLE in ADCE and strip reflect.
+ - Validator
+   - Added validation for OpGroupNonUniformBallotBitCount.
+   - Added validation for the Vulkan memory model.
+   - Added support for VK_KHR_shader_atddomic_int64.
+   - Added validation for execution modes.
+   - Added validation for runtime array layouts.
+   - Added validation for 8-bit storage.
+   - Added validation of OpPhi instructions with pointer result type.
+   - Added checks for the Vulkan memory model.
+   - Validate MakeTexelAvailableKHR and MakeTexelVisibleKHR
+   - Allow atomic function pointer for OpenCL.
+   - FPRounding mode checks were implemented.
+   - Added validation for the id bound with an option to set the max id bound.
+   Fixes:
+   - #1882: Improve the validation of decorations to reduce memory usage.
+   - #1891: Fix an potential infinite loop in dead-branch-elimination.
+   - #1405: Validate the storage class of boolean objects.
+   - #1880: Identify arrays of type void as invalid.
+   - #487: Validate OpImageTexelPointer.
+   - #1922: Validate OpPhi instructions are at the start of a block correctly.
+   - #1923: Validate function scope variable are at the start of the entry block.
+
+v2018.5 2018-09-07
+ - General:
+   - Support SPV_KHR_vulkan_memory_model
+   - Update Dim capabilities, to match SPIR-V 1.3 Rev 4
+   - Automated build bots no run tests for the VS2013 case
+   - Support Chromium GN build
+   - Use Kokoro bots:
+     - Disable Travis-CI bots
+     - Disable AppVeyor VisualStudio Release builds. Keep VS 2017 Debug build
+   - Don't check export symbols on OSX (Darwin): some installations don't have 'objdump'
+   - Reorganize source files and namespaces
+   - Fixes for ClangTidy, and whitespace (passes 'git cl presumit --all -uf')
+   - Fix unused param compile warnings/errors when Effcee not present
+   - Avoid including time headers when timer functionality is disabled
+   - Avoid too-stringent warnings flags for Clang on Windows
+   - Internal refactoring
+   - Add hooks for automated fuzzing
+   - Add testing of command line executables
+   - #1688: Use binary mode on stdin; fixes "spirv-dis <foo.spv" on Windows
+ - Optimizer
+   - The optimizer validates the module before it begins
+   - Add API to register passes by string name
+   - Fold a vector shuffle feeding a vector shuffle
+   - Add -combine-access-chains transform
+   - Refactor how IRContext is handled by passes
+   - Improve bookkeeping for instruction result type and result id
+   - Fix over-duplication of decorations
+   - Fix handling of exits from selections in dead-branch elimination, and dead code
+     elimination.
+   - Fix handling of certain kinds of flow control in merge-return
+   Fixes:
+   - #1721: Fix size bug when folding vector shuffles
+   - #1722: Fix size infinite loop when folding vector shuffles
+   - #1724: Fix finding a constant of a specific type
+   - #1727: Dead branch elim: Reorder blocks if needed to satisfy dominance rule
+   - #1729: Handle VariablePointers cases in various optimizations
+   - #1731: Fix vector shuffle with literal id indicating undef value
+   - #1736: Fix handling of decorations and phis in merge-return
+   - #1787: Fix handling of decorations related to access chains
+   - #1865: Avoid leaking memory for SPIR-V constant values
+ - Validator
+   - Improve error messages
+   - Avoid platform-dependent traversal ordering, to ensure consistent messages
+   - Use libspirv::Instruction where possible
+   - Add option to skip all block layout checks
+   - Validate all type IDs
+   - Validate uses of OpFunction
+   - Validate uses of OpTypeFunction
+   - Disallow a struct containing its own type https://crbug.com/874372
+   - #1685: Vulkan permits non-monotonic offsets for block members
+   - #1697: Enforce block layout rules even when using relaxed block layout option
+   - #1719: Fix line number for vector shuffle valiation error
+   - #1789: Avoid assertion failure when validating some functions
+   - #1800: Fix validation of OpCopyMemorySized
+   - #1822: Stop enforcing struct member offset montonicity
+   - #1831: Disallow void members in structs
+
+v2018.4 2018-07-08
+ - General:
+   - Support SPV_KHR_8bit_storage
+   - Add gclient and presubmit configurations
+   - Enable Kokoro build bots (#1625)
+   - Group tests into fewer executables, reduces load on CI
+   - Port test script to Python 3
+   - Symbol export tests respect SPIRV_SKIP_TESTS
+   - #1596: Operand lookup succeeds if enabled by a capability
+   - #1624: Instruction lookup succeeds if enabled by a capability
+   - Refactoring namespaces:
+     - #1678: Change libspirv to spvtools
+     - Code in source/utils moved into spvtools::utils
+     - Code in source/comp moved into spvtools::comp
+ - Optimizer:
+   - Remove insert-extract-elim pass. Use simplification pass instead.
+   - Preserve instruction-to-block mapping in most passes, to reduce runtime.
+   - Small vector optimization for operands
+   - Add pass to move Private variables to Function. Increase opportunity to optimize.
+   - Fixes:
+     #1120: Check static uses of entry point interfaces
+     #1372: Avoid merging some structs, to preserve names for reflection
+     #1577: Scalar replacement uses only undecorated types.
+     #1578: Fix handling of forward-pointer types, and types embedding pointers
+       to themselves.
+     #1591: Inliner: Callee variable with initializer should have a store at the call site.
+     #1634: Fix crash: Use type id in vector type lookup
+     #1649: Fix assert in compact-ids pass
+     Fix constant folder: ensure it uses the right type
+     #1659: Folding rules added to IRContext. Avoids leak.
+ - Validator
+   - Add work-in-progress WebGPU environment. Disallows OpUndef
+   - #670, #1581: Improve error messages; disassemble instruction
+   - #491: Check structured switches
+   - #937: Check layout rules for Block and BufferBlock in Uniform, StorageBuffer, PushConstant
+   - #1281: Check invalid branches into structured constructs
+   - #1522: Disallow array-of-arrays with DescriptorSets
+   - #1577: Allow duplicate pointer types.
+   - #1581: Better messages: output ID names along with numbers in more cases.
+   - #1597: Check Vulkan 1.1 capabilities
+   - #1618: Check invalid exit from structured case construct
+   - #1622: Run IdPass before DataRulesPass
+   - #1632: Reduce test time by artificially lowering limits in limit test
+   - #1638: Block-decorated structs member order must respect offset order
+   - #1657: Improve CFG validation diagnostics
+   - Khronos SPIR-V #337: GLSL.std.450 Refract instruction Eta param can be any float scalar.
+   - #1606: PushConstant Blocks follow storage-buffer layout rules
+   - #1664: Check layout of StorageBuffer variables with Block decoration, using storage buffer
+     rules
+   - #1666: Layout validation should permit {vec3; float} packing
+   - #1637, #1668: Layout validation uses RowMajor, ArrayStride, MatrixStride properly
+ - Linker
+   - Avoid buffer overrun when creating OpModuleProcessed
+
+v2018.3 2018-05-25
+ - General:
+   - Support SPV_EXT_descriptor_indexing
+   - Support SPV_GOOGLE_decorate_string
+   - Support SPV_GOOGLE_hlsl_functionality1
+   - Support SPV_NV_shader_subgroup_partitioned
+   - Use "unified1" grammar from SPIRV-Headers
+   - Simplify support for new extensions. Assembler, disassembler, and simple validation
+     support is automatic if new tokens are introduced with appropriate extension
+     attributes in the "unified1" SPIR-V core grammar.
+   - Disassembler: Emit more digits on floating point, to reliably reproduce all
+     significand bits.  (Use std::max_digits10 instead of std::digits10)
+   - Fix compilation for old XCode versions: Explicit construction required for std::set.
+ - Optimizer:
+   - Add --strip-reflect
+   - Add --time-report
+   - Add --loop-fission
+   - Add lop fusion.
+   - Add loop peeling pass and internal utility.
+   - Improve optimizer runtime.
+   - Merge-return now works with structured control flow.
+   - New (faster) SSA rewriter to convert local loads and stores to SSA IDs and phis.
+     Can replace load/store elimination passes.
+   - Fix instruction folding case: insertion that feeds and extract, when the extract
+     remains.
+   - Fold OpDot.
+   - Fold OpFNegate.
+   - Fold multply and divide of same value.
+   - Fold FClamp feeding a compare.
+   - Fold OpLoad feeding an extract, to reduce excessive copying. (#1547)
+   - Fold Fmix feeding an extract.
+   - Use simplification pass instead of insert-extract elimination.
+   - Constant fold OpVectorTimesScalar.
+   - Copy propagate arrays, in simple cases.
+   - Aggressive dead code elimination: Can remove more instructions, e.g. derivatives.
+   - Aggressive dead code elimination: Remove Workgroup variables that are written but not read.
+   - Better handling of OpImageTexelPointer
+   - Initial utilities for scalar evolution.
+   - Add Vector dead code elimination.
+   - Each pass can only run once.
+   - Allow code hosting in if-conversion.
+   - Add external interface for adding a PassToken, so external code can make their own
+     passes.
+   - Fixes:
+     #1404: Don't optimize away the compute compute workgroup size constant.
+     #1407: Remove a bad assertion
+     #1456: Fix bug in SSA rewriter related to variables updated in loops.
+     #1487: Fix long runtime in Dead insertion elimination: Don't revist select phi nodes.
+     #1492: Aggressive dead code elimination can remove OpDecorateStringGOOGLE.
+     #1527: Fix inlining of functions having OpKill and OpUnreachable.
+     #1559: Fix assert failure in reduce-load-size pass.
+     #1556: Aggressive dead code elimination: Fix handling of OpCopyMemory.
+ - Validator:
+   - Check Vulkan built-in variables
+   - Check Vulkan-specific atomic result type rule.
+   - Relax control barrier check for SPIR-V 1.3.  Fixes #1427
+   - Check OpPhi.
+   - Check OpMemoryModel.
+   - Stop checking sizes derived from spec-constants.
+   - Re-enable checks for OpUConvert.
+   - Vulkan: Fix check for PrimitiveId: Permit as Input in fragment shader.
+   - Validate binary version for the given target environment.
+   - Add tests for OpBranch checks.
+   - Vulkan 1.1: Check scope for non-uniform subgroup operations.
+   - Fix checks for SPV_AMD_gpu_shader_int16.
+   - Fix logical layout check for OpDecorateId.
+   - Fix checks for ViewportIndex & Layer for Vulkan and SPV_EXT_shader_viewport_index_layer.
+   - Fixes:
+     #1470: Vulkan: Don't restrict WorkgroupSize to Input storage class.
+     #1469: Vulkan: Permit Subgroup memory scope for Vulkan 1.1.
+     #1472: Per-vertex variable validation fixes.
+     #1483: Valdiate barrier execution scopes for Vulkan 1.1.
+ - Fixes:
+   #898: Linker properly removes FuncParamAttr from imported symbols.
+   #924, #1174: Fix handling of decoration groups in optimizer, linker.
+
+v2018.2 2018-03-07
+ - General:
+   - Support SPIR-V 1.3 and Vulkan 1.1.
+     - Default target environment is now SPIR-V 1.3.  For command-line tools,
+       use the --target-env option to override the default.  Examples:
+	  # Generate a SPIR-V 1.0 binary instead of SPIR-V 1.3
+	  spirv-as --target-env spv1.0 a.spvasm -o a.spv
+	  spirv-as --target-env vulkan1.0 a.spvasm -o a.spv
+	  # Validate as Vulkan 1.0
+	  spirv-val --target-env vulkan1.0 a.spv
+   - Support SPV_GOOGLE_decorate_string and SPV_GOOGLE_hlsl_functionality1
+ - Fixes:
+   - Fix Android.mk build. Compilation was failing due to missing definitions of
+     SpvCapabilityFloat16ImageAMD and other enumerated values.
+   - Optimizer: Avoid generating duplicate names when merging types.
+   - #1375: Validator: SPV_AMD_gpu_shaer_half_float implicitly allows declaration
+     of the 16-bit floating point type.
+   - #1376: Optimizer: Avoid folding half-precision float.
+
+v2018.1 2018-03-02
+ - General:
+   - Support Visual Studio 2013 again. (Continue support for VS 2015 and VS 2017.)
+   - Support building SPIRV-Tools as a shared library.
+   - Improve the HLSL legalization optimization recipe. #1311
+ - Optimizer:
+   - General speedups.
+   - Remove generic dead code elimination functionality from transforms:
+       --eliminate-local-single-block
+       --eliminate-local-single-store
+       --eliminate-local-multi-store
+     To recover the previous behaviour, a recipe using those transforms should now
+     also invoke the --eliminate-dead-code-aggressive transform.
+   - Improve folding, including coverage for floating point, OpSelect, and arithmetic
+     with non-trivial constant operands.
+   - Add loop-invariant code motion pass.
+   - Add loop-unrolling pass, for honouring unroll hits.
+   - Add loop-unswitch pass.
+   - Add instruction simplification pass.
+   - Aggressive dead code elimination: Understands capability hierarchy when finding
+     instructions it can eliminate (combinators). (PR #1268)
+   - CCP can now fold floating point arithmetic. #1311
+ - Validator:
+   - Validate barrier instructions.
+   - Check Vulkan-specific rules for atomics.
+   - Check Vulkan prohibition of Location or Component decorations on BuiltIn variables.
+ - Linker:
+   - Add --verify-ids option
+   - Add option to allow a resulting module to be partially linked.
+   - Handle OpModuleProcessed (instructions in SPIR-V layout section 7c)
+ - Fixes:
+   - #1265: Optimizer: Fix use-after free bug in if-conversion. (Fix object lifecycle bug
+     in type manager.)
+   - #1282: Fix new warnings found by GCC 8.0.1.
+   - #1285: Optimizer: Fix random failures during inlining.  (Dangling references in DefUseManager)
+   - #1295: Optimizer: Fix incorrect handling of Phi nodes in CCP.
+   - #1300: Fix CCP: avoid bad CCP transitions and unsettled values.
+   - #1304: Avoid static-duration variables of class type (with constructors).
+   - #1323: Fix folding of an insert composite feeding a composite extract.
+   - #1339: Fix CCP: Handle OpConstantNull boolean values as conditions.
+   - #1341: DCEInst: Keep atomic instructions (and some others with side effects).
+   - #1354: Don't fold integer division.
+   - #1357: Support OpConstantNull in folding.
+   - #1361: CCP: Fix handling of non-constant module-scope values
+
+v2018.0 2018-02-02
+ - General
+   - VisualStudio 2013 is no longer supported.  VisualStudio 2015 is supported.
+   - Use "include/unified1" directory from SPIRV-Headers.  Requires recent SPIRV-Headers source.
+ - Disassembler: spirv-dis adds --color option to force color disassembly.
+ - Optimizer:
+   - Add pass to eliminate dead insertions.
+   - Aggressive dead code elimination now removes OpSwitch constructs.
+   - Block merging occurs in more cases.
+   - Add driver workaround transform: replace OpUnreachable with harmless branch to merge.
+   - Improve instruction folding framework.
+   - Add loop analysis.
+   - Add scalar replacement of aggregates to size-optimization recipe.
+   - Add pass to replace instructions invalid for a shader stage, with a harmless value.
+     This changes the semantics of the program!  Not for general use!
+   - Rearragne and add passes to performance-optimization recipe, to produce better results.
+ - Validator:
+   - Validate OpenCL extended instructions.
+   - Shaders can't perform atomics on floats.
+   - Validate memory semantics values in atomics.
+   - Validate instruction-adjacency constraints, e.g. OpPhi predecessors, merge instructions
+     immediately precede branches.
+ - Fixes:
+   - PR 1198: Optimizer: Fix CCP in presence of matrix constants.
+   - #1199: Optimizer: Fix CCP: don't propagate spec constants.
+   - #1203: Optimizer: Fix common uniform elim bug introduced by refactoring.
+   - #1210: Optimizer: Aggressive dead code elimination: Fix 'break' identification.
+   - #1212: Optimizer: Aggressive dead code elimination: Was skipping too many instructions.
+   - #1214: Optimizer: Aggressive dead code elimination: Fix infinite loop.
+   - #1228: Optimizer: Fix CCP: Handling of varying Phi nodes; was resulting in infinite loop.
+   - #1245: Optimizer: Dead branch elimination: Avoid a null pointer dereference.
+   - #1250: Optimizer: Dead branch elimination: Avoid spuriously reporting a change.
+
+v2017.3 2018-01-12
+ - General:
+   - Support DebugInfo extended instruction set, targeted at OpenCL environments.
+     See the SPIR-V Registry.
+   - Generate a SPIRV-Tools.pc file for pkg-config.
+ - Optimizer:
+   - Progress for legalization of code generated from HLSL (issue #1118):
+     - Add --legalize-hlsl option to run transforms used to transform intermediate
+       code generated by HLSL to SPIR-V for Vulkan compilers.  Those compilers
+       normally run these transforms automatically.  This option is used for developing
+       those transforms.
+     - Add Private-to-Function variable conversion for modules with logical
+       addressing.
+   - Add --ccp: SSA Conditional Constant Propagation (CCP)
+   - Add --print-all to show disassembly for each optimization pass.
+   - Internal: Add loop descriptors and post-order tree iterator.
+   - Generalized dead branch elimination
+   - Aggressive dead code elimination (ADCE) now removes dead functions and
+     module-scope variables.
+   - Vector extract/insert elimination now optimizes through some cases of
+     VectorShuffle, and GLSL.std.450 Mix extended instruction.
+ - Validator:
+   - Add validation for GLSL.std.450 extended instruction set.
+   - Check out of bounds composite accesses, where that's statically computable.
+     Fixes #1112.
+   - Check upper bits of literal numbers that aren't a multiple of 32-bits wide.
+   - More validation of primitive instructions
+   - Add optional "relaxed" checking logical addressing mode to permit some
+     cases of pointer-to-pointer.  Contributes to HLSL legalization (issue #1118).
+ - Fixes:
+   #1100: Validator: Image operand Sample can be used with OpImageSparseFetch,
+     OpImageSparseRead.
+   #1108: Remove duplicates transform was incorrectly removing non-duplicate
+     decorations.
+   #1111: Optimizer's type manager could reference deleted memory.
+   #1112: Fix decoration equality check, e.g. it is now symmetric.
+   #1129: Validator now disallows Dim=SupbassData for OpImageSparseRead.
+   #1143: Fix CCP: Was generating incorrect code for loops.
+   #1153: Fix CCP crash.
+   #1154: Optimizer's internal instruction-to-block mappings were sometimes
+       inconsistent.
+   #1159: Fix CCP infinite loop.
+   #1168: Fix dead branch elimination intermittently generating incorrect code.
+       Fixes https://github.com/KhronosGroup/glslang/issues/1205
+   #1186: Fix validation of PackDouble2x32 and UnpackDouble2x32
+
+v2017.2 2017-12-15
+ - General:
+   - Support OpenCL 1.2, 2.0 target environments, including embedded profiles
+   - Add CONTRIBUTING.md
+   - Fix exit status code for spirv-link
+   - Disassember: Enable emitting ANSI colour codes to a string
+   - Library avoids polluting global namespace.  The libraries can export C and C++
+     symbols starting with "spv", or in a C++ namespace.  Add a test for this.
+   - Linux release builds include debug information, for easier profiling
+   - Build bots no longer test VisualStudio 2013
+     - Testing dependency RE2 requires VisualStudio 2015 or later
+   - Build bots check code formatting
+ - Optimizer:
+   - Add --skip-validation to spirv-opt
+   - Add dominance tree analysis
+   - Add generic value propagation engine
+   - Add global redundancy elimination within a function
+   - Add scalar replacement of function-scope variables of composite type
+   - Aggressive dead code elimination: Remove empty loops
+   - Killing an instruction notifies the IRContext
+   - IRContext::KillInst deletes the instruction
+   - Move CFG analysis to IRContext
+   - Add constant manager
+   - Fix: Don't consider derivative instructions as combinators.
+   - Fix: Don't delete an instruction twice in local dead-code-elimination
+   - Fix: Don't consider derivative instructions as combinators.
+ - Validator:
+   - Finish checking of image instructions (Section 3.32.10)
+     - Check sparse image instructions
+     - Check OpTypeImage, OpTypeSampleImage
+   - Check composite instructions (Section 3.32.12)
+   - Check atomic instructions (Section 3.32.18)
+   - Check OpEmitStreamVertex, OpEndStreamPrimitive instructions
+   - Re-enable validation of OpCopyObject
+   - OpKill, image ImplicitLod and QueryLod instructions can only be used in Fragment
+     shaders.
+   - Fixes for image instruction validation:
+     - Lod image operand only usable with ExplicitLod and OpImageFetch
+     - ExplicitLod Lod image operand must be float scalar
+     - OpImageFectch Lod image operand must be int scalar
+     - OpImageGather component operand must be 32-bits (integer scalar)
+     - OpImageQuerySizeLod Lod must be integer scalar
+ - Fixes:
+   #622: Remove names and decorations when inlining
+   #989: Aggressive dead code elim: Don't optimize away live breaks from a loop
+   #991: Fix validation of SPV_AMD_shader_ballot
+   #1004: Use after free of an instruction, in remove-duplicates transform
+   #1007: OpImageRead not required to return 4-component vector
+   #1009: OpImageRead can return scalar int/float types
+   #1011: OpImageWrite should allow scalar int/float texel types
+   #1012: Fix validat Dref type check
+   #1017: Load-store elimination considers variable initializations
+   #1034: Fix Windows debug build: operator< should be a weak ordering
+   #1083: Inlining: Set parent (function) for each inlined basic block.
+   #1075: Aggressive dead code elimination: Was leaving dangling references to
+     removed blocks.
+
+v2017.1 2017-11-23
+ - Update README with details on the [email protected] mailing list.
+ - General:
+   - Automatically deploy built artifacts to GitHub Releases
+   - Add a Linker (module combiner). Under development.
+   - Add Android.mk for Android NDK builds.
+   - Add the 'effcee' library as an optional dependency for use in tests.
+     Eventually it will be a required dependency, once downstream projects have
+     a chance to adjust.  Requires 're2' library.
+   - Avoid static-duration variables of class type (with constructors).
+   - Hack around bugs in gcc-4.8.1 template handling
+   - Faster opcode lookup
+ - Validator:
+   - Recognize extensions listed on SPIR-V registry,
+     through #25 SPV_AMD_shader_fragment_mask
+   - Validator issues an info message when it sees an unrecognized extension.
+   - Type check basic arithmetic operations
+   - Type check carry/extended arithmetic operations
+   - Type check vector arithmetic operations
+   - Type check Relational and Logical instructions
+   - Type check Bit instructions
+   - Check type uniqueness rules
+   - Check conversion instructions
+   - Check image instructions
+   - Check derivative instructions
+   - Check OpVectorShuffle
+   - Check OpBranchConditional
+   - OpModuleProcessed is only allowed after debug names section and before annotations
+     section.
+   - Checks the right kind of return is called for each function (void or non-void).
+   - Add option to relax type check when storing structs (--relax-store-struct)
+ - Optimizer:
+   - Refactoring internal representation of the module, including:
+     - IRContext: owns a module and manages analyses
+     - Instructions are owned by intrusive lists, and have unique IDs
+     - BasicBlock owns its instruction list.
+     - DefUseManager: change representation of uses, for faster processing
+       on large modules.
+   - Add high level recipes: -O, -Os, and -Oconfig
+     Recipes for -O and -Os are under development.
+   - Add eliminate-dead-function transform
+   - Add strength reduction transform: For now, convert multiply by power of 2
+     to a bit shift.
+   - Add CFG cleanup transform
+   - Add removal of dead module-scope variables
+   - Add merge-return transform for modules without structured control flow
+   - Add redundancy elimination within a basic block (local value numbering)
+   - Extract-insert elimination:
+     - Recognize the case where the first instruction in the sequence is an
+       OpCompositeConstruct or OpConstantComposite
+     - Handle some cases of nested structs
+   - Dead branch elimination now can eliminate entire selection constructs
+     when all arms are dead.
+ - Compressing codec:
+   - Updated algorithm to 1.01, 1.02, 1.03
+   - Not built by default.  Use -DSPIRV_BUILD_COMPRESSION=ON to build.
+   - Codec can be parameterized by a customized model.
+ - Fixes:
+   #728: Fix decoration of inlined functions
+   #798: spirv-as should fail when given unrecognized long option
+   #800: Inliner: Fix inlining function into header of multi-block loop
+   #824: Eliminate-local-multi-store: Fix a crash
+   #826: Elimiante-local-multi-store: Fix a crash
+   #827: Fix crash when compact-ids transform runs before another transform.
+   #834: Add Cmake option to build the compressing codec. Off by default.
+   #911: Fix classification of Line and NoLine instructions
+
+v2017.0 2017-09-01
+ - Update README to describe that assembler, disassembler, and binary parser support
+   are based on grammar files from the SPIRV-Headers repository.
+
+v2016.7 2017-09-01
+ - Add SPIR-V 1.2
+ - OpenCL 2.2 support is now based on SPIR-V 1.2
+ - Support AMD extensions in assembler, disassembler:
+    SPV_AMD_gcn_shader
+    SPV_AMD_shader_ballot
+    SPV_AMD_shader_explicit_vertex_parameter
+    SPV_AMD_shader_trinary_minmax
+    SPV_AMD_gpu_shader_half_float
+    SPV_AMD_texture_gather_bias_lod
+    SPV_AMD_gpu_shader_int16
+ - Optimizer: Add support for:
+   - Inline all function calls in entry points.
+   - Flatten decoration groups.  Fixes #602
+   - Id compaction (minimize Id bound).  Fixes #624
+   - Eliminate redundant composite insert followed by extract
+   - Simplify access chains to local variables
+   - Eliminate local variables with a single store, if possible
+   - Eliminate local variables with a several stores, if possible
+   - Eliminate loads and stores in same block to local variables
+   - Eliminate redundant insert/extract to composite values
+   - Aggressive dead instruction elimination
+   - Eliminate dead branches
+   - Merge blocks when the second can only be preceded by the first
+   - Eliminate ommon uniform loads
+ - Assembler: Add option to preserve numeric ids. Fixes #625
+ - Add build target spirv-tools-vimsyntax to generate spvasm.vim, a SPIR-V
+   assembly syntax file for Vim.
+ - Version string: Allow overriding of wall clock timestamp with contents
+   of environment variable SOURCE_DATE_EPOCH.
+ - Validator implements relaxed rules for SPV_KHR_16bit_storage.
+ - CMake installation rules use GNUInstallDirs.  For example, libraries
+   will be installed into a lib64 directory if that's the norm for the
+   current system.
+ - Fixes:
+   #500: Parameterize validator limit checks
+   #508: Support compilation under CYGWIN
+   #517: Fix validation when continue (or case) contstruct is also the head of a
+     nested control construct.
+   #551: If a merge block is reachable, it must be *strictly* dominated by its
+     header.
+   #548: Validator: Error when the reserved OpImageSparseSampleProj* opcodes
+     are used.
+   #611: spvtools::Optimizer was failing to save the module to the output
+     binary vector when all passes succeded without changes.
+   #629: The inline-entry-points-all optimization could generate invalidly
+     structured code when the inlined function had early returns.
+   #697: Optimizer's Instruction::ForEachInId method was skipping semantics-id
+     and scope-id.
+   #755: Inliner: Fix inlining of callee with single Return appearing before
+     the end of the function.
+   #776: Fix dead branch elimination in presence of complex but dead control
+     flow.
+   #781: SPV_KHR_variable_pointers allows duplicate pointer types
+   #782: Inliner: Fix remapping of non-label forward references in callee
+   #787: Inliner: Fix remapping of inlined entry block when called from
+     single block loop.
+   #790: Inliner: Fix remapping of inlined entry block when callee has
+     multiple returns.
+
+v2016.6 2016-12-13
+ - Published the C++ interface for assembling, disassembling, validation, and
+   optimization.
+ - Support SPV_KHR_shader_draw_parameters in assembler, disassembler, parser.
+ - Validator:
+   - Add validator API accepting raw binary words
+   - Increased coverage:
+     - Checks "Data rules" in Universal Validation Rules, section 2.16.1
+     - WIP: Universal Limits.
+       - The minimum mandated upper bounds are checked.
+       - TODO: Parameterize the validator to allow larger limits accepted by
+         a more than minimally capable implementation.
+   - OpSampledImage checks
+   - OpConstantComposite checks
+   - Id bound check
+ - Disasssembler:
+   - Generates friendly GLSL-based names for more builtin variables
+   - Generates friendly names for numeric OpConstant values
+   - Vendor tool info extracted from SPIR-V XML registry file.
+ - Fixes issues:
+   #429: Validator: Allow OpTypeForwardPointer and OpTypeStruct to reference
+     undefined IDs
+   #482: Validator: OpVariable initializer can be an ID of a module-scope variable
+
+v2016.5 2016-09-16
+ - Support SPV_KHR_shader_ballot in assembler, disassembler, parser.
+ - Disassembler: Generate friendly names for built-in variables.
+ - Partial fixes:
+   #359: Add Emacs helper for automatically diassembling/assembling a SPIR-V
+     binary on file load/save.
+ - Fixes:
+   #414: Validator: Allow OpUndef for composite constants
+   #415: Validator: Phi can use its own value in some cases.
+
+v2016.4 2016-09-01
+ - Relicensed under Apache 2.0
+ - Add optimization passes (in API and spirv-opt command)
+   - Fold spec constants defined with OpSpecConstantOp and
+       OpSpecConstantComposite to normal constants with fixed value(s).
+ - Fixes issues:
+   #318: Relicensed under Apache 2.0
+
+v2016.3 2016-08-24
+ - Add target environment enums for OpenCL 2.1, OpenCL 2.2,
+   OpenGL 4.0, OpenGL 4.1, OpenGL 4.2, OpenGL 4.3, OpenGL 4.5.
+ - Add spirv-cfg, an experimental tool to dump the control flow graph
+   as a GraphiViz "dot" graph
+ - Add optimization pass: Eliminate dead constants.
+ - Add spirv-lesspipe.sh filter utility
+ - Fixes issues:
+   #288: Check def-use dominance rules for OpPhi (variable,parent) operands
+   #339: Allow OpUndef in types-constants-global-vars section, as required
+     by SPIR-V 1.0 Rev7, 1.1 Rev 3.
+   #340: Avoid race on mkdir during build
+   #365: Relax PointSize, ClipDistance, CullDistance capability check in all
+     environments not just Vulkan 1.0.
+
+v2016.2 2016-08-05
+ - Validator is incomplete
+   - Checks ID use block is dominated by definition block
+ - Add optimization passes (in API and spirv-opt command)
+   - Strip debug info instructions
+   - Freeze spec constant to their default values
+ - Allow INotEqual as operation for OpSpecConstantOp
+ - Fixes bugs:
+   #270: validator: crash when continue construct is unreachable
+   #279: validator: infinite loop when analyzing some degenerate control
+     flow graphs
+   #286: validator: don't incorrectly generate def-use error for
+         (variable,parent) parameters to OpPhi
+   #290: disassembler: never generate bare % for an identifier
+   #295: validator: def-use dominance check should ignore unreachable uses
+   #276: validator: allow unreachable continue constructs
+   #297: validator: allow an unreachable block to branch to a reachable
+         merge block
+
+v2016.1 2016-07-19
+ - Fix https://github.com/KhronosGroup/SPIRV-Tools/issues/261
+   Turn off ClipDistance and CullDistance capability checks for Vulkan.
+ - The disassembler can emit friendly names based on debug info (OpName
+   instructions), and will infer somewhat friendly names for most types.
+   This is turned on by default for the spirv-dis command line tool.
+ - Updated to support SPIR-V 1.1 rev 2
+   - Input StorageClass, Sampled1D capability, and SampledBuffer capability
+     do not require Shader capability anymore.
+
+v2016.0 2016-07-04
+
+ - Adds v<year>.<index> versioning, with "-dev" indicating
+   work in progress.  The intent is to more easly report
+   and summarize functionality when SPIRV-Tools is incorporated
+   in downstream projects.
+
+ - Summary of functionality (See the README.md for more):
+   - Supports SPIR-V 1.1 Rev 1
+   - Supports SPIR-V 1.0 Rev 5
+   - Supports GLSL std450 extended instructions 1.0 Rev 3
+   - Supports OpenCL extended instructions 1.0 Rev 2
+   - Assembler, disassembler are complete
+     - Supports floating point widths of 16, 32, 64 bits
+     - Supports integer widths up to 64 bits
+   - Validator is incomplete
+     - Checks capability requirements in most cases
+     - Checks module layout constraints
+     - Checks ID use-definition ordering constraints,
+       ignoring control flow
+     - Checks some control flow graph rules
+   - Optimizer is introduced, with few available transforms.
+   - Supported on Linux, OSX, Android, Windows
+
+ - Fixes bugs:
+   - #143: OpenCL pow and pown arguments

+ 685 - 0
3rdparty/spirv-tools/README.md

@@ -0,0 +1,685 @@
+# SPIR-V Tools
+
+## Overview
+
+The SPIR-V Tools project provides an API and commands for processing SPIR-V
+modules.
+
+The project includes an assembler, binary module parser, disassembler,
+validator, and optimizer for SPIR-V. Except for the optimizer, all are based
+on a common static library.  The library contains all of the implementation
+details, and is used in the standalone tools whilst also enabling integration
+into other code bases directly. The optimizer implementation resides in its
+own library, which depends on the core library.
+
+The interfaces have stabilized:
+We don't anticipate making a breaking change for existing features.
+
+SPIR-V is defined by the Khronos Group Inc.
+See the [SPIR-V Registry][spirv-registry] for the SPIR-V specification,
+headers, and XML registry.
+
+## Downloads
+
+[![Build status](https://ci.appveyor.com/api/projects/status/gpue87cesrx3pi0d/branch/master?svg=true)](https://ci.appveyor.com/project/Khronoswebmaster/spirv-tools/branch/master)
+<img alt="Linux" src="kokoro/img/linux.png" width="20px" height="20px" hspace="2px"/>[![Linux Build Status](https://storage.googleapis.com/spirv-tools/badges/build_status_linux_clang_release.svg)](https://storage.googleapis.com/spirv-tools/badges/build_link_linux_clang_release.html)
+<img alt="MacOS" src="kokoro/img/macos.png" width="20px" height="20px" hspace="2px"/>[![MacOS Build Status](https://storage.googleapis.com/spirv-tools/badges/build_status_macos_clang_release.svg)](https://storage.googleapis.com/spirv-tools/badges/build_link_macos_clang_release.html)
+<img alt="Windows" src="kokoro/img/windows.png" width="20px" height="20px" hspace="2px"/>[![Windows Build Status](https://storage.googleapis.com/spirv-tools/badges/build_status_windows_release.svg)](https://storage.googleapis.com/spirv-tools/badges/build_link_windows_vs2017_release.html)
+
+[More downloads](docs/downloads.md)
+
+## Versioning SPIRV-Tools
+
+See [`CHANGES`](CHANGES) for a high level summary of recent changes, by version.
+
+SPIRV-Tools project version numbers are of the form `v`*year*`.`*index* and with
+an optional `-dev` suffix to indicate work in progress.  For example, the
+following versions are ordered from oldest to newest:
+
+* `v2016.0`
+* `v2016.1-dev`
+* `v2016.1`
+* `v2016.2-dev`
+* `v2016.2`
+
+Use the `--version` option on each command line tool to see the software
+version.  An API call reports the software version as a C-style string.
+
+## Supported features
+
+### Assembler, binary parser, and disassembler
+
+* Support for SPIR-V 1.0, through 1.5
+  * Based on SPIR-V syntax described by JSON grammar files in the
+    [SPIRV-Headers](https://github.com/KhronosGroup/SPIRV-Headers) repository.
+  * Usually, support for a new version of SPIR-V is ready within days after
+    publication.
+* Support for extended instruction sets:
+  * GLSL std450 version 1.0 Rev 3
+  * OpenCL version 1.0 Rev 2
+* Assembler only does basic syntax checking.  No cross validation of
+  IDs or types is performed, except to check literal arguments to
+  `OpConstant`, `OpSpecConstant`, and `OpSwitch`.
+
+See [`docs/syntax.md`](docs/syntax.md) for the assembly language syntax.
+
+### Validator
+
+The validator checks validation rules described by the SPIR-V specification.
+
+Khronos recommends that tools that create or transform SPIR-V modules use the
+validator to ensure their outputs are valid, and that tools that consume SPIR-V
+modules optionally use the validator to protect themselves from bad inputs.
+This is especially encouraged for debug and development scenarios.
+
+The validator has one-sided error: it will only return an error when it has
+implemented a rule check and the module violates that rule.
+
+The validator is incomplete.
+See the [CHANGES](CHANGES) file for reports on completed work, and
+the [Validator
+sub-project](https://github.com/KhronosGroup/SPIRV-Tools/projects/1) for planned
+and in-progress work.
+
+*Note*: The validator checks some Universal Limits, from section 2.17 of the SPIR-V spec.
+The validator will fail on a module that exceeds those minimum upper bound limits.
+It is [future work](https://github.com/KhronosGroup/SPIRV-Tools/projects/1#card-1052403)
+to parameterize the validator to allow larger
+limits accepted by a more than minimally capable SPIR-V consumer.
+
+
+### Optimizer
+
+The optimizer is a collection of code transforms, or "passes".
+Transforms are written for a diverse set of reasons:
+
+* To restructure, simplify, or normalize the code for further processing.
+* To eliminate undesirable code.
+* To improve code quality in some metric such as size or performance.
+  **Note**: These transforms are not guaranteed to actually improve any
+  given metric. Users should always measure results for their own situation.
+
+As of this writing, there are 67 transforms including examples such as:
+* Simplification
+  * Strip debug info
+  * Strip reflection info
+* Specialization Constants
+  * Set spec constant default value
+  * Freeze spec constant to default value
+  * Fold `OpSpecConstantOp` and `OpSpecConstantComposite`
+  * Unify constants
+  * Eliminate dead constant
+* Code Reduction
+  * Inline all function calls exhaustively
+  * Convert local access chains to inserts/extracts
+  * Eliminate local load/store in single block
+  * Eliminate local load/store with single store
+  * Eliminate local load/store with multiple stores
+  * Eliminate local extract from insert
+  * Eliminate dead instructions (aggressive)
+  * Eliminate dead branches
+  * Merge single successor / single predecessor block pairs
+  * Eliminate common uniform loads
+  * Remove duplicates: Capabilities, extended instruction imports, types, and
+    decorations.
+* Normalization
+  * Compact IDs
+  * CFG cleanup
+  * Flatten decorations
+  * Merge returns
+  * Convert AMD-specific instructions to KHR instructions
+* Code improvement
+  * Conditional constant propagation
+  * If-conversion
+  * Loop fission
+  * Loop fusion
+  * Loop-invariant code motion
+  * Loop unroll
+* Other
+  * Generate WebGPU initializers
+  * Graphics robust access
+  * Upgrade memory model to VulkanKHR
+
+Additionally, certain sets of transformations have been packaged into
+higher-level recipes.  These include:
+
+* Optimization for size (`spirv-opt -Os`)
+* Optimization for performance (`spirv-opt -O`)
+
+For the latest list with detailed documentation, please refer to
+[`include/spirv-tools/optimizer.hpp`](include/spirv-tools/optimizer.hpp).
+
+For suggestions on using the code reduction options, please refer to this [white paper](https://www.lunarg.com/shader-compiler-technologies/white-paper-spirv-opt/).
+
+
+### Linker
+
+*Note:* The linker is still under development.
+
+Current features:
+* Combine multiple SPIR-V binary modules together.
+* Combine into a library (exports are retained) or an executable (no symbols
+  are exported).
+
+See the [CHANGES](CHANGES) file for reports on completed work, and the [General
+sub-project](https://github.com/KhronosGroup/SPIRV-Tools/projects/2) for
+planned and in-progress work.
+
+
+### Reducer
+
+*Note:* The reducer is still under development.
+
+The reducer simplifies and shrinks a SPIR-V module with respect to a
+user-supplied *interestingness function*.  For example, given a large
+SPIR-V module that cause some SPIR-V compiler to fail with a given
+fatal error message, the reducer could be used to look for a smaller
+version of the module that causes the compiler to fail with the same
+fatal error message.
+
+To suggest an additional capability for the reducer, [file an
+issue](https://github.com/KhronosGroup/SPIRV-Tools/issues]) with
+"Reducer:" as the start of its title.
+
+
+### Fuzzer
+
+*Note:* The fuzzer is still under development.
+
+The fuzzer applies semantics-preserving transformations to a SPIR-V binary
+module, to produce an equivalent module.  The original and transformed modules
+should produce essentially identical results when executed on identical inputs:
+their results should differ only due to floating-point round-off, if at all.
+Significant differences in results can pinpoint bugs in tools that process
+SPIR-V binaries, such as miscompilations.  This *metamorphic testing* approach
+is similar to the method used by the [GraphicsFuzz
+project](https://github.com/google/graphicsfuzz) for fuzzing of GLSL shaders.
+
+To suggest an additional capability for the fuzzer, [file an
+issue](https://github.com/KhronosGroup/SPIRV-Tools/issues]) with
+"Fuzzer:" as the start of its title.
+
+
+### Extras
+
+* [Utility filters](#utility-filters)
+* Build target `spirv-tools-vimsyntax` generates file `spvasm.vim`.
+  Copy that file into your `$HOME/.vim/syntax` directory to get SPIR-V assembly syntax
+  highlighting in Vim.  This build target is not built by default.
+
+## Contributing
+
+The SPIR-V Tools project is maintained by members of the The Khronos Group Inc.,
+and is hosted at https://github.com/KhronosGroup/SPIRV-Tools.
+
+Consider joining the `[email protected]` mailing list, via
+[https://www.khronos.org/spir/spirv-tools-mailing-list/](https://www.khronos.org/spir/spirv-tools-mailing-list/).
+The mailing list is used to discuss development plans for the SPIRV-Tools as an open source project.
+Once discussion is resolved,
+specific work is tracked via issues and sometimes in one of the
+[projects][spirv-tools-projects].
+
+(To provide feedback on the SPIR-V _specification_, file an issue on the
+[SPIRV-Headers][spirv-headers] GitHub repository.)
+
+See [`docs/projects.md`](docs/projects.md) to see how we use the
+[GitHub Project
+feature](https://help.github.com/articles/tracking-the-progress-of-your-work-with-projects/)
+to organize planned and in-progress work.
+
+Contributions via merge request are welcome. Changes should:
+* Be provided under the [Apache 2.0](#license).
+* You'll be prompted with a one-time "click-through"
+  [Khronos Open Source Contributor License Agreement][spirv-tools-cla]
+  (CLA) dialog as part of submitting your pull request or
+  other contribution to GitHub.
+* Include tests to cover updated functionality.
+* C++ code should follow the [Google C++ Style Guide][cpp-style-guide].
+* Code should be formatted with `clang-format`.
+  [kokoro/check-format/build.sh](kokoro/check-format/build.sh)
+  shows how to download it. Note that we currently use
+  `clang-format version 5.0.0` for SPIRV-Tools. Settings are defined by
+  the included [.clang-format](.clang-format) file.
+
+We intend to maintain a linear history on the GitHub `master` branch.
+
+### Source code organization
+
+* `example`: demo code of using SPIRV-Tools APIs
+* `external/googletest`: Intended location for the
+  [googletest][googletest] sources, not provided
+* `external/effcee`: Location of [Effcee][effcee] sources, if the `effcee` library
+  is not already configured by an enclosing project.
+* `external/re2`: Location of [RE2][re2] sources, if the `re2` library is not already
+  configured by an enclosing project.
+  (The Effcee project already requires RE2.)
+* `include/`: API clients should add this directory to the include search path
+* `external/spirv-headers`: Intended location for
+  [SPIR-V headers][spirv-headers], not provided
+* `include/spirv-tools/libspirv.h`: C API public interface
+* `source/`: API implementation
+* `test/`: Tests, using the [googletest][googletest] framework
+* `tools/`: Command line executables
+
+Example of getting sources, assuming SPIRV-Tools is configured as a standalone project:
+
+    git clone https://github.com/KhronosGroup/SPIRV-Tools.git   spirv-tools
+    git clone https://github.com/KhronosGroup/SPIRV-Headers.git spirv-tools/external/spirv-headers
+    git clone https://github.com/google/googletest.git          spirv-tools/external/googletest
+    git clone https://github.com/google/effcee.git              spirv-tools/external/effcee
+    git clone https://github.com/google/re2.git                 spirv-tools/external/re2
+
+### Tests
+
+The project contains a number of tests, used to drive development
+and ensure correctness.  The tests are written using the
+[googletest][googletest] framework.  The `googletest`
+source is not provided with this project.  There are two ways to enable
+tests:
+* If SPIR-V Tools is configured as part of an enclosing project, then the
+  enclosing project should configure `googletest` before configuring SPIR-V Tools.
+* If SPIR-V Tools is configured as a standalone project, then download the
+  `googletest` source into the `<spirv-dir>/external/googletest` directory before
+  configuring and building the project.
+
+*Note*: You must use a version of googletest that includes
+[a fix][googletest-pull-612] for [googletest issue 610][googletest-issue-610].
+The fix is included on the googletest master branch any time after 2015-11-10.
+In particular, googletest must be newer than version 1.7.0.
+
+### Dependency on Effcee
+
+Some tests depend on the [Effcee][effcee] library for stateful matching.
+Effcee itself depends on [RE2][re2].
+
+* If SPIRV-Tools is configured as part of a larger project that already uses
+  Effcee, then that project should include Effcee before SPIRV-Tools.
+* Otherwise, SPIRV-Tools expects Effcee sources to appear in `external/effcee`
+  and RE2 sources to appear in `external/re2`.
+
+
+## Build
+
+Instead of building manually, you can also download the binaries for your
+platform directly from the [master-tot release][master-tot-release] on GitHub.
+Those binaries are automatically uploaded by the buildbots after successful
+testing and they always reflect the current top of the tree of the master
+branch.
+
+In order to build the code, you first need to sync the external repositories
+that it depends on. Assume that `<spirv-dir>` is the root directory of the
+checked out code:
+
+```sh
+cd <spirv-dir>
+git clone https://github.com/KhronosGroup/SPIRV-Headers.git external/spirv-headers
+git clone https://github.com/google/effcee.git external/effcee
+git clone https://github.com/google/re2.git external/re2
+git clone https://github.com/google/googletest.git external/googletest # optional
+
+```
+
+*Note*:
+The script `utils/git-sync-deps` can be used to checkout and/or update the
+contents of the repos under `external/` instead of manually maintaining them.
+
+### Build using CMake
+You can build The project using [CMake][cmake] to generate platform-specific
+build configurations.
+
+```sh
+cd <spirv-dir>
+mkdir build && cd build
+cmake [-G <platform-generator>] <spirv-dir>
+```
+
+Once the build files have been generated, build using your preferred
+development environment.
+
+### Build using Bazel
+You can also use [Bazel](https://bazel.build/) to build the project.
+```sh
+cd <spirv-dir>
+bazel build :all
+```
+
+### Tools you'll need
+
+For building and testing SPIRV-Tools, the following tools should be
+installed regardless of your OS:
+
+- [CMake](http://www.cmake.org/): if using CMake for generating compilation
+targets, you need to install CMake Version 2.8.12 or later.
+- [Python 3](http://www.python.org/): for utility scripts and running the test
+suite.
+- [Bazel](https://bazel.build/) (optional): if building the source with Bazel,
+you need to install Bazel Version 0.29.1 on your machine. Other versions may
+also work, but are not verified.
+
+SPIRV-Tools is regularly tested with the following compilers:
+
+On Linux
+- GCC version 4.8.5
+- Clang version 3.8
+
+On MacOS
+- AppleClang 10.0
+
+On Windows
+- Visual Studio 2015
+- Visual Studio 2017
+
+Other compilers or later versions may work, but they are not tested.
+
+### CMake options
+
+The following CMake options are supported:
+
+* `SPIRV_BUILD_FUZZER={ON|OFF}`, default `OFF` - Build the spirv-fuzz tool.
+* `SPIRV_COLOR_TERMINAL={ON|OFF}`, default `ON` - Enables color console output.
+* `SPIRV_SKIP_TESTS={ON|OFF}`, default `OFF`- Build only the library and
+  the command line tools.  This will prevent the tests from being built.
+* `SPIRV_SKIP_EXECUTABLES={ON|OFF}`, default `OFF`- Build only the library, not
+  the command line tools and tests.
+* `SPIRV_USE_SANITIZER=<sanitizer>`, default is no sanitizing - On UNIX
+  platforms with an appropriate version of `clang` this option enables the use
+  of the sanitizers documented [here][clang-sanitizers].
+  This should only be used with a debug build.
+* `SPIRV_WARN_EVERYTHING={ON|OFF}`, default `OFF` - On UNIX platforms enable
+  more strict warnings.  The code might not compile with this option enabled.
+  For Clang, enables `-Weverything`.  For GCC, enables `-Wpedantic`.
+  See [`CMakeLists.txt`](CMakeLists.txt) for details.
+* `SPIRV_WERROR={ON|OFF}`, default `ON` - Forces a compilation error on any
+  warnings encountered by enabling the compiler-specific compiler front-end
+  option.  No compiler front-end options are enabled when this option is OFF.
+
+Additionally, you can pass additional C preprocessor definitions to SPIRV-Tools
+via setting `SPIRV_TOOLS_EXTRA_DEFINITIONS`. For example, by setting it to
+`/D_ITERATOR_DEBUG_LEVEL=0` on Windows, you can disable checked iterators and
+iterator debugging.
+
+### Android
+
+SPIR-V Tools supports building static libraries `libSPIRV-Tools.a` and
+`libSPIRV-Tools-opt.a` for Android:
+
+```
+cd <spirv-dir>
+
+export ANDROID_NDK=/path/to/your/ndk
+
+mkdir build && cd build
+mkdir libs
+mkdir app
+
+$ANDROID_NDK/ndk-build -C ../android_test     \
+                      NDK_PROJECT_PATH=.      \
+                      NDK_LIBS_OUT=`pwd`/libs \
+                      NDK_APP_OUT=`pwd`/app
+```
+
+### Updating DEPS
+Occasionally the entries in DEPS will need to be updated. This is done on demand
+when there is a request to do this, often due to downstream breakages. There is
+a script `utils/roll_deps.sh` provided, which will generate a patch with the
+updated DEPS values. This will still need to be tested in your checkout to
+confirm that there are no integration issues that need to be resolved.
+
+## Library
+
+### Usage
+
+The internals of the library use C++11 features, and are exposed via both a C
+and C++ API.
+
+In order to use the library from an application, the include path should point
+to `<spirv-dir>/include`, which will enable the application to include the
+header `<spirv-dir>/include/spirv-tools/libspirv.h{|pp}` then linking against
+the static library in `<spirv-build-dir>/source/libSPIRV-Tools.a` or
+`<spirv-build-dir>/source/SPIRV-Tools.lib`.
+For optimization, the header file is
+`<spirv-dir>/include/spirv-tools/optimizer.hpp`, and the static library is
+`<spirv-build-dir>/source/libSPIRV-Tools-opt.a` or
+`<spirv-build-dir>/source/SPIRV-Tools-opt.lib`.
+
+* `SPIRV-Tools` CMake target: Creates the static library:
+  * `<spirv-build-dir>/source/libSPIRV-Tools.a` on Linux and OS X.
+  * `<spirv-build-dir>/source/libSPIRV-Tools.lib` on Windows.
+* `SPIRV-Tools-opt` CMake target: Creates the static library:
+  * `<spirv-build-dir>/source/libSPIRV-Tools-opt.a` on Linux and OS X.
+  * `<spirv-build-dir>/source/libSPIRV-Tools-opt.lib` on Windows.
+
+#### Entry points
+
+The interfaces are still under development, and are expected to change.
+
+There are five main entry points into the library in the C interface:
+
+* `spvTextToBinary`: An assembler, translating text to a binary SPIR-V module.
+* `spvBinaryToText`: A disassembler, translating a binary SPIR-V module to
+  text.
+* `spvBinaryParse`: The entry point to a binary parser API.  It issues callbacks
+  for the header and each parsed instruction.  The disassembler is implemented
+  as a client of `spvBinaryParse`.
+* `spvValidate` implements the validator functionality. *Incomplete*
+* `spvValidateBinary` implements the validator functionality. *Incomplete*
+
+The C++ interface is comprised of three classes, `SpirvTools`, `Optimizer` and
+`Linker`, all in the `spvtools` namespace.
+* `SpirvTools` provides `Assemble`, `Disassemble`, and `Validate` methods.
+* `Optimizer` provides methods for registering and running optimization passes.
+* `Linker` provides methods for combining together multiple binaries.
+
+## Command line tools
+
+Command line tools, which wrap the above library functions, are provided to
+assemble or disassemble shader files.  It's a convention to name SPIR-V
+assembly and binary files with suffix `.spvasm` and `.spv`, respectively.
+
+### Assembler tool
+
+The assembler reads the assembly language text, and emits the binary form.
+
+The standalone assembler is the exectuable called `spirv-as`, and is located in
+`<spirv-build-dir>/tools/spirv-as`.  The functionality of the assembler is implemented
+by the `spvTextToBinary` library function.
+
+* `spirv-as` - the standalone assembler
+  * `<spirv-dir>/tools/as`
+
+Use option `-h` to print help.
+
+### Disassembler tool
+
+The disassembler reads the binary form, and emits assembly language text.
+
+The standalone disassembler is the executable called `spirv-dis`, and is located in
+`<spirv-build-dir>/tools/spirv-dis`. The functionality of the disassembler is implemented
+by the `spvBinaryToText` library function.
+
+* `spirv-dis` - the standalone disassembler
+  * `<spirv-dir>/tools/dis`
+
+Use option `-h` to print help.
+
+The output includes syntax colouring when printing to the standard output stream,
+on Linux, Windows, and OS X.
+
+### Linker tool
+
+The linker combines multiple SPIR-V binary modules together, resulting in a single
+binary module as output.
+
+This is a work in progress.
+The linker does not support OpenCL program linking options related to math
+flags. (See section 5.6.5.2 in OpenCL 1.2)
+
+* `spirv-link` - the standalone linker
+  * `<spirv-dir>/tools/link`
+
+### Optimizer tool
+
+The optimizer processes a SPIR-V binary module, applying transformations
+in the specified order.
+
+This is a work in progress, with initially only few available transformations.
+
+* `spirv-opt` - the standalone optimizer
+  * `<spirv-dir>/tools/opt`
+
+### Validator tool
+
+*Warning:* This functionality is under development, and is incomplete.
+
+The standalone validator is the executable called `spirv-val`, and is located in
+`<spirv-build-dir>/tools/spirv-val`. The functionality of the validator is implemented
+by the `spvValidate` library function.
+
+The validator operates on the binary form.
+
+* `spirv-val` - the standalone validator
+  * `<spirv-dir>/tools/val`
+
+### Reducer tool
+
+The reducer shrinks a SPIR-V binary module, guided by a user-supplied
+*interestingness test*.
+
+This is a work in progress, with initially only shrinks a module in a few ways.
+
+* `spirv-reduce` - the standalone reducer
+  * `<spirv-dir>/tools/reduce`
+
+Run `spirv-reduce --help` to see how to specify interestingness.
+
+### Fuzzer tool
+
+The fuzzer transforms a SPIR-V binary module into a semantically-equivalent
+SPIR-V binary module by applying transformations in a randomized fashion.
+
+This is a work in progress, with initially only a few semantics-preserving
+transformations.
+
+* `spirv-fuzz` - the standalone fuzzer
+  * `<spirv-dir>/tools/fuzz`
+
+Run `spirv-fuzz --help` for a detailed list of options.
+
+### Control flow dumper tool
+
+The control flow dumper prints the control flow graph for a SPIR-V module as a
+[GraphViz](http://www.graphviz.org/) graph.
+
+This is experimental.
+
+* `spirv-cfg` - the control flow graph dumper
+  * `<spirv-dir>/tools/cfg`
+
+### Utility filters
+
+* `spirv-lesspipe.sh` - Automatically disassembles `.spv` binary files for the
+  `less` program, on compatible systems.  For example, set the `LESSOPEN`
+  environment variable as follows, assuming both `spirv-lesspipe.sh` and
+  `spirv-dis` are on your executable search path:
+  ```
+   export LESSOPEN='| spirv-lesspipe.sh "%s"'
+  ```
+  Then you page through a disassembled module as follows:
+  ```
+  less foo.spv
+  ```
+  * The `spirv-lesspipe.sh` script will pass through any extra arguments to
+    `spirv-dis`.  So, for example, you can turn off colours and friendly ID
+    naming as follows:
+    ```
+    export LESSOPEN='| spirv-lesspipe.sh "%s" --no-color --raw-id'
+    ```
+
+* [vim-spirv](https://github.com/kbenzie/vim-spirv) - A vim plugin which
+  supports automatic disassembly of `.spv` files using the `:edit` command and
+  assembly using the `:write` command. The plugin also provides additional
+  features which include; syntax highlighting; highlighting of all ID's matching
+  the ID under the cursor; and highlighting errors where the `Instruction`
+  operand of `OpExtInst` is used without an appropriate `OpExtInstImport`.
+
+* `50spirv-tools.el` - Automatically disassembles '.spv' binary files when
+  loaded into the emacs text editor, and re-assembles them when saved,
+  provided any modifications to the file are valid.  This functionality
+  must be explicitly requested by defining the symbol
+  SPIRV_TOOLS_INSTALL_EMACS_HELPERS as follows:
+  ```
+  cmake -DSPIRV_TOOLS_INSTALL_EMACS_HELPERS=true ...
+  ```
+
+  In addition, this helper is only installed if the directory /etc/emacs/site-start.d
+  exists, which is typically true if emacs is installed on the system.
+
+  Note that symbol IDs are not currently preserved through a load/edit/save operation.
+  This may change if the ability is added to spirv-as.
+
+
+### Tests
+
+Tests are only built when googletest is found. Use `ctest` to run all the
+tests.
+
+## Future Work
+<a name="future"></a>
+
+_See the [projects pages](https://github.com/KhronosGroup/SPIRV-Tools/projects)
+for more information._
+
+### Assembler and disassembler
+
+* The disassembler could emit helpful annotations in comments.  For example:
+  * Use variable name information from debug instructions to annotate
+    key operations on variables.
+  * Show control flow information by annotating `OpLabel` instructions with
+    that basic block's predecessors.
+* Error messages could be improved.
+
+### Validator
+
+This is a work in progress.
+
+### Linker
+
+* The linker could accept math transformations such as allowing MADs, or other
+  math flags passed at linking-time in OpenCL.
+* Linkage attributes can not be applied through a group.
+* Check decorations of linked functions attributes.
+* Remove dead instructions, such as OpName targeting imported symbols.
+
+## Licence
+<a name="license"></a>
+Full license terms are in [LICENSE](LICENSE)
+```
+Copyright (c) 2015-2016 The Khronos Group Inc.
+
+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.
+```
+
+[spirv-tools-cla]: https://cla-assistant.io/KhronosGroup/SPIRV-Tools
+[spirv-tools-projects]: https://github.com/KhronosGroup/SPIRV-Tools/projects
+[spirv-tools-mailing-list]: https://www.khronos.org/spir/spirv-tools-mailing-list
+[spirv-registry]: https://www.khronos.org/registry/spir-v/
+[spirv-headers]: https://github.com/KhronosGroup/SPIRV-Headers
+[googletest]: https://github.com/google/googletest
+[googletest-pull-612]: https://github.com/google/googletest/pull/612
+[googletest-issue-610]: https://github.com/google/googletest/issues/610
+[effcee]: https://github.com/google/effcee
+[re2]: https://github.com/google/re2
+[CMake]: https://cmake.org/
+[cpp-style-guide]: https://google.github.io/styleguide/cppguide.html
+[clang-sanitizers]: http://clang.llvm.org/docs/UsersManual.html#controlling-code-generation
+[master-tot-release]: https://github.com/KhronosGroup/SPIRV-Tools/releases/tag/master-tot

+ 149 - 0
3rdparty/spirv-tools/include/generated/OpenCLDebugInfo100.h

@@ -0,0 +1,149 @@
+// Copyright (c) 2018 The Khronos Group Inc.
+// 
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and/or associated documentation files (the "Materials"),
+// to deal in the Materials without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Materials, and to permit persons to whom the
+// Materials are furnished to do so, subject to the following conditions:
+// 
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Materials.
+// 
+// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS
+// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND
+// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ 
+// 
+// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS
+// IN THE MATERIALS.
+
+#ifndef SPIRV_EXTINST_OpenCLDebugInfo100_H_
+#define SPIRV_EXTINST_OpenCLDebugInfo100_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum { OpenCLDebugInfo100Version = 200, OpenCLDebugInfo100Version_BitWidthPadding = 0x7fffffff };
+enum { OpenCLDebugInfo100Revision = 2, OpenCLDebugInfo100Revision_BitWidthPadding = 0x7fffffff };
+
+enum OpenCLDebugInfo100Instructions {
+    OpenCLDebugInfo100DebugInfoNone = 0,
+    OpenCLDebugInfo100DebugCompilationUnit = 1,
+    OpenCLDebugInfo100DebugTypeBasic = 2,
+    OpenCLDebugInfo100DebugTypePointer = 3,
+    OpenCLDebugInfo100DebugTypeQualifier = 4,
+    OpenCLDebugInfo100DebugTypeArray = 5,
+    OpenCLDebugInfo100DebugTypeVector = 6,
+    OpenCLDebugInfo100DebugTypedef = 7,
+    OpenCLDebugInfo100DebugTypeFunction = 8,
+    OpenCLDebugInfo100DebugTypeEnum = 9,
+    OpenCLDebugInfo100DebugTypeComposite = 10,
+    OpenCLDebugInfo100DebugTypeMember = 11,
+    OpenCLDebugInfo100DebugTypeInheritance = 12,
+    OpenCLDebugInfo100DebugTypePtrToMember = 13,
+    OpenCLDebugInfo100DebugTypeTemplate = 14,
+    OpenCLDebugInfo100DebugTypeTemplateParameter = 15,
+    OpenCLDebugInfo100DebugTypeTemplateTemplateParameter = 16,
+    OpenCLDebugInfo100DebugTypeTemplateParameterPack = 17,
+    OpenCLDebugInfo100DebugGlobalVariable = 18,
+    OpenCLDebugInfo100DebugFunctionDeclaration = 19,
+    OpenCLDebugInfo100DebugFunction = 20,
+    OpenCLDebugInfo100DebugLexicalBlock = 21,
+    OpenCLDebugInfo100DebugLexicalBlockDiscriminator = 22,
+    OpenCLDebugInfo100DebugScope = 23,
+    OpenCLDebugInfo100DebugNoScope = 24,
+    OpenCLDebugInfo100DebugInlinedAt = 25,
+    OpenCLDebugInfo100DebugLocalVariable = 26,
+    OpenCLDebugInfo100DebugInlinedVariable = 27,
+    OpenCLDebugInfo100DebugDeclare = 28,
+    OpenCLDebugInfo100DebugValue = 29,
+    OpenCLDebugInfo100DebugOperation = 30,
+    OpenCLDebugInfo100DebugExpression = 31,
+    OpenCLDebugInfo100DebugMacroDef = 32,
+    OpenCLDebugInfo100DebugMacroUndef = 33,
+    OpenCLDebugInfo100DebugSource = 35,
+    OpenCLDebugInfo100InstructionsMax = 0x7ffffff
+};
+
+
+enum OpenCLDebugInfo100DebugInfoFlags {
+    OpenCLDebugInfo100FlagIsProtected = 0x01,
+    OpenCLDebugInfo100FlagIsPrivate = 0x02,
+    OpenCLDebugInfo100FlagIsPublic = 0x03,
+    OpenCLDebugInfo100FlagIsLocal = 0x04,
+    OpenCLDebugInfo100FlagIsDefinition = 0x08,
+    OpenCLDebugInfo100FlagFwdDecl = 0x10,
+    OpenCLDebugInfo100FlagArtificial = 0x20,
+    OpenCLDebugInfo100FlagExplicit = 0x40,
+    OpenCLDebugInfo100FlagPrototyped = 0x80,
+    OpenCLDebugInfo100FlagObjectPointer = 0x100,
+    OpenCLDebugInfo100FlagStaticMember = 0x200,
+    OpenCLDebugInfo100FlagIndirectVariable = 0x400,
+    OpenCLDebugInfo100FlagLValueReference = 0x800,
+    OpenCLDebugInfo100FlagRValueReference = 0x1000,
+    OpenCLDebugInfo100FlagIsOptimized = 0x2000,
+    OpenCLDebugInfo100FlagIsEnumClass = 0x4000,
+    OpenCLDebugInfo100FlagTypePassByValue = 0x8000,
+    OpenCLDebugInfo100FlagTypePassByReference = 0x10000,
+    OpenCLDebugInfo100DebugInfoFlagsMax = 0x7ffffff
+};
+
+enum OpenCLDebugInfo100DebugBaseTypeAttributeEncoding {
+    OpenCLDebugInfo100Unspecified = 0,
+    OpenCLDebugInfo100Address = 1,
+    OpenCLDebugInfo100Boolean = 2,
+    OpenCLDebugInfo100Float = 3,
+    OpenCLDebugInfo100Signed = 4,
+    OpenCLDebugInfo100SignedChar = 5,
+    OpenCLDebugInfo100Unsigned = 6,
+    OpenCLDebugInfo100UnsignedChar = 7,
+    OpenCLDebugInfo100DebugBaseTypeAttributeEncodingMax = 0x7ffffff
+};
+
+enum OpenCLDebugInfo100DebugCompositeType {
+    OpenCLDebugInfo100Class = 0,
+    OpenCLDebugInfo100Structure = 1,
+    OpenCLDebugInfo100Union = 2,
+    OpenCLDebugInfo100DebugCompositeTypeMax = 0x7ffffff
+};
+
+enum OpenCLDebugInfo100DebugTypeQualifier {
+    OpenCLDebugInfo100ConstType = 0,
+    OpenCLDebugInfo100VolatileType = 1,
+    OpenCLDebugInfo100RestrictType = 2,
+    OpenCLDebugInfo100AtomicType = 3,
+    OpenCLDebugInfo100DebugTypeQualifierMax = 0x7ffffff
+};
+
+enum OpenCLDebugInfo100DebugOperation {
+    OpenCLDebugInfo100Deref = 0,
+    OpenCLDebugInfo100Plus = 1,
+    OpenCLDebugInfo100Minus = 2,
+    OpenCLDebugInfo100PlusUconst = 3,
+    OpenCLDebugInfo100BitPiece = 4,
+    OpenCLDebugInfo100Swap = 5,
+    OpenCLDebugInfo100Xderef = 6,
+    OpenCLDebugInfo100StackValue = 7,
+    OpenCLDebugInfo100Constu = 8,
+    OpenCLDebugInfo100Fragment = 9,
+    OpenCLDebugInfo100DebugOperationMax = 0x7ffffff
+};
+
+enum OpenCLDebugInfo100DebugImportedEntity {
+    OpenCLDebugInfo100ImportedModule = 0,
+    OpenCLDebugInfo100ImportedDeclaration = 1,
+    OpenCLDebugInfo100DebugImportedEntityMax = 0x7ffffff
+};
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // SPIRV_EXTINST_OpenCLDebugInfo100_H_

+ 1 - 1
3rdparty/spirv-tools/include/generated/build-version.inc

@@ -1 +1 @@
-"v2019.5-dev", "SPIRV-Tools v2019.5-dev v2019.4-192-g983b5b4f"
+"v2020.2-dev", "SPIRV-Tools v2020.2-dev b53f48f92c539b6f8561e36023bbf281d8dfeda8"

File diff suppressed because it is too large
+ 2 - 1
3rdparty/spirv-tools/include/generated/enum_string_mapping.inc


+ 1 - 0
3rdparty/spirv-tools/include/generated/extension_enum.inc

@@ -29,6 +29,7 @@ kSPV_KHR_device_group,
 kSPV_KHR_float_controls,
 kSPV_KHR_multiview,
 kSPV_KHR_no_integer_wrap_decoration,
+kSPV_KHR_non_semantic_info,
 kSPV_KHR_physical_storage_buffer,
 kSPV_KHR_post_depth_coverage,
 kSPV_KHR_shader_atomic_counter_ops,

+ 39 - 0
3rdparty/spirv-tools/include/generated/opencl.debuginfo.100.insts.inc

@@ -0,0 +1,39 @@
+
+
+static const spv_ext_inst_desc_t opencl_debuginfo_100_entries[] = {
+  {"DebugInfoNone", 0, 0, nullptr, {SPV_OPERAND_TYPE_NONE}},
+  {"DebugCompilationUnit", 1, 0, nullptr, {SPV_OPERAND_TYPE_LITERAL_INTEGER, SPV_OPERAND_TYPE_LITERAL_INTEGER, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_SOURCE_LANGUAGE, SPV_OPERAND_TYPE_NONE}},
+  {"DebugTypeBasic", 2, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING, SPV_OPERAND_TYPE_NONE}},
+  {"DebugTypePointer", 3, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_STORAGE_CLASS, SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS, SPV_OPERAND_TYPE_NONE}},
+  {"DebugTypeQualifier", 4, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_DEBUG_TYPE_QUALIFIER, SPV_OPERAND_TYPE_NONE}},
+  {"DebugTypeArray", 5, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_VARIABLE_ID, SPV_OPERAND_TYPE_NONE}},
+  {"DebugTypeVector", 6, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_LITERAL_INTEGER, SPV_OPERAND_TYPE_NONE}},
+  {"DebugTypedef", 7, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_LITERAL_INTEGER, SPV_OPERAND_TYPE_LITERAL_INTEGER, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
+  {"DebugTypeFunction", 8, 0, nullptr, {SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_VARIABLE_ID, SPV_OPERAND_TYPE_NONE}},
+  {"DebugTypeEnum", 9, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_LITERAL_INTEGER, SPV_OPERAND_TYPE_LITERAL_INTEGER, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS, SPV_OPERAND_TYPE_VARIABLE_ID, SPV_OPERAND_TYPE_NONE}},
+  {"DebugTypeComposite", 10, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_DEBUG_COMPOSITE_TYPE, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_LITERAL_INTEGER, SPV_OPERAND_TYPE_LITERAL_INTEGER, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS, SPV_OPERAND_TYPE_VARIABLE_ID, SPV_OPERAND_TYPE_NONE}},
+  {"DebugTypeMember", 11, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_LITERAL_INTEGER, SPV_OPERAND_TYPE_LITERAL_INTEGER, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_NONE}},
+  {"DebugTypeInheritance", 12, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS, SPV_OPERAND_TYPE_NONE}},
+  {"DebugTypePtrToMember", 13, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
+  {"DebugTypeTemplate", 14, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_VARIABLE_ID, SPV_OPERAND_TYPE_NONE}},
+  {"DebugTypeTemplateParameter", 15, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_LITERAL_INTEGER, SPV_OPERAND_TYPE_LITERAL_INTEGER, SPV_OPERAND_TYPE_NONE}},
+  {"DebugTypeTemplateTemplateParameter", 16, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_LITERAL_INTEGER, SPV_OPERAND_TYPE_LITERAL_INTEGER, SPV_OPERAND_TYPE_NONE}},
+  {"DebugTypeTemplateParameterPack", 17, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_LITERAL_INTEGER, SPV_OPERAND_TYPE_LITERAL_INTEGER, SPV_OPERAND_TYPE_VARIABLE_ID, SPV_OPERAND_TYPE_NONE}},
+  {"DebugGlobalVariable", 18, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_LITERAL_INTEGER, SPV_OPERAND_TYPE_LITERAL_INTEGER, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_NONE}},
+  {"DebugFunctionDeclaration", 19, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_LITERAL_INTEGER, SPV_OPERAND_TYPE_LITERAL_INTEGER, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS, SPV_OPERAND_TYPE_NONE}},
+  {"DebugFunction", 20, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_LITERAL_INTEGER, SPV_OPERAND_TYPE_LITERAL_INTEGER, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS, SPV_OPERAND_TYPE_LITERAL_INTEGER, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_NONE}},
+  {"DebugLexicalBlock", 21, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_LITERAL_INTEGER, SPV_OPERAND_TYPE_LITERAL_INTEGER, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_NONE}},
+  {"DebugLexicalBlockDiscriminator", 22, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_LITERAL_INTEGER, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
+  {"DebugScope", 23, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_NONE}},
+  {"DebugNoScope", 24, 0, nullptr, {SPV_OPERAND_TYPE_NONE}},
+  {"DebugInlinedAt", 25, 0, nullptr, {SPV_OPERAND_TYPE_LITERAL_INTEGER, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_NONE}},
+  {"DebugLocalVariable", 26, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_LITERAL_INTEGER, SPV_OPERAND_TYPE_LITERAL_INTEGER, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS, SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER, SPV_OPERAND_TYPE_NONE}},
+  {"DebugInlinedVariable", 27, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
+  {"DebugDeclare", 28, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
+  {"DebugValue", 29, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_VARIABLE_ID, SPV_OPERAND_TYPE_NONE}},
+  {"DebugOperation", 30, 0, nullptr, {SPV_OPERAND_TYPE_DEBUG_OPERATION, SPV_OPERAND_TYPE_VARIABLE_LITERAL_INTEGER, SPV_OPERAND_TYPE_NONE}},
+  {"DebugExpression", 31, 0, nullptr, {SPV_OPERAND_TYPE_VARIABLE_ID, SPV_OPERAND_TYPE_NONE}},
+  {"DebugMacroDef", 32, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_LITERAL_INTEGER, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_NONE}},
+  {"DebugMacroUndef", 33, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_LITERAL_INTEGER, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
+  {"DebugSource", 35, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_NONE}}
+};

+ 69 - 0
3rdparty/spirv-tools/include/generated/operand.kinds-unified1.inc

@@ -918,6 +918,69 @@ static const spv_operand_desc_t pygen_variable_DebugOperationEntries[] = {
   {"Constu", 8, 0, nullptr, 0, nullptr, {SPV_OPERAND_TYPE_LITERAL_INTEGER}, SPV_SPIRV_VERSION_WORD(1, 0), 0xffffffffu}
 };
 
+static const spv_operand_desc_t pygen_variable_CLDEBUG100_DebugInfoFlagsEntries[] = {
+  {"FlagIsProtected", 0x01, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1, 0), 0xffffffffu},
+  {"FlagIsPrivate", 0x02, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1, 0), 0xffffffffu},
+  {"FlagIsPublic", 0x03, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1, 0), 0xffffffffu},
+  {"FlagIsLocal", 0x04, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1, 0), 0xffffffffu},
+  {"FlagIsDefinition", 0x08, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1, 0), 0xffffffffu},
+  {"FlagFwdDecl", 0x10, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1, 0), 0xffffffffu},
+  {"FlagArtificial", 0x20, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1, 0), 0xffffffffu},
+  {"FlagExplicit", 0x40, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1, 0), 0xffffffffu},
+  {"FlagPrototyped", 0x80, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1, 0), 0xffffffffu},
+  {"FlagObjectPointer", 0x100, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1, 0), 0xffffffffu},
+  {"FlagStaticMember", 0x200, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1, 0), 0xffffffffu},
+  {"FlagIndirectVariable", 0x400, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1, 0), 0xffffffffu},
+  {"FlagLValueReference", 0x800, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1, 0), 0xffffffffu},
+  {"FlagRValueReference", 0x1000, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1, 0), 0xffffffffu},
+  {"FlagIsOptimized", 0x2000, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1, 0), 0xffffffffu},
+  {"FlagIsEnumClass", 0x4000, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1, 0), 0xffffffffu},
+  {"FlagTypePassByValue", 0x8000, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1, 0), 0xffffffffu},
+  {"FlagTypePassByReference", 0x10000, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1, 0), 0xffffffffu}
+};
+
+static const spv_operand_desc_t pygen_variable_CLDEBUG100_DebugBaseTypeAttributeEncodingEntries[] = {
+  {"Unspecified", 0, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1, 0), 0xffffffffu},
+  {"Address", 1, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1, 0), 0xffffffffu},
+  {"Boolean", 2, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1, 0), 0xffffffffu},
+  {"Float", 3, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1, 0), 0xffffffffu},
+  {"Signed", 4, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1, 0), 0xffffffffu},
+  {"SignedChar", 5, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1, 0), 0xffffffffu},
+  {"Unsigned", 6, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1, 0), 0xffffffffu},
+  {"UnsignedChar", 7, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1, 0), 0xffffffffu}
+};
+
+static const spv_operand_desc_t pygen_variable_CLDEBUG100_DebugCompositeTypeEntries[] = {
+  {"Class", 0, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1, 0), 0xffffffffu},
+  {"Structure", 1, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1, 0), 0xffffffffu},
+  {"Union", 2, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1, 0), 0xffffffffu}
+};
+
+static const spv_operand_desc_t pygen_variable_CLDEBUG100_DebugTypeQualifierEntries[] = {
+  {"ConstType", 0, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1, 0), 0xffffffffu},
+  {"VolatileType", 1, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1, 0), 0xffffffffu},
+  {"RestrictType", 2, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1, 0), 0xffffffffu},
+  {"AtomicType", 3, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1, 0), 0xffffffffu}
+};
+
+static const spv_operand_desc_t pygen_variable_CLDEBUG100_DebugOperationEntries[] = {
+  {"Deref", 0, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1, 0), 0xffffffffu},
+  {"Plus", 1, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1, 0), 0xffffffffu},
+  {"Minus", 2, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1, 0), 0xffffffffu},
+  {"PlusUconst", 3, 0, nullptr, 0, nullptr, {SPV_OPERAND_TYPE_LITERAL_INTEGER}, SPV_SPIRV_VERSION_WORD(1, 0), 0xffffffffu},
+  {"BitPiece", 4, 0, nullptr, 0, nullptr, {SPV_OPERAND_TYPE_LITERAL_INTEGER, SPV_OPERAND_TYPE_LITERAL_INTEGER}, SPV_SPIRV_VERSION_WORD(1, 0), 0xffffffffu},
+  {"Swap", 5, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1, 0), 0xffffffffu},
+  {"Xderef", 6, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1, 0), 0xffffffffu},
+  {"StackValue", 7, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1, 0), 0xffffffffu},
+  {"Constu", 8, 0, nullptr, 0, nullptr, {SPV_OPERAND_TYPE_LITERAL_INTEGER}, SPV_SPIRV_VERSION_WORD(1, 0), 0xffffffffu},
+  {"Fragment", 9, 0, nullptr, 0, nullptr, {SPV_OPERAND_TYPE_LITERAL_INTEGER, SPV_OPERAND_TYPE_LITERAL_INTEGER}, SPV_SPIRV_VERSION_WORD(1, 0), 0xffffffffu}
+};
+
+static const spv_operand_desc_t pygen_variable_CLDEBUG100_DebugImportedEntityEntries[] = {
+  {"ImportedModule", 0, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1, 0), 0xffffffffu},
+  {"ImportedDeclaration", 1, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1, 0), 0xffffffffu}
+};
+
 static const spv_operand_desc_group_t pygen_variable_OperandInfoTable[] = {
   {SPV_OPERAND_TYPE_IMAGE, ARRAY_SIZE(pygen_variable_ImageOperandsEntries), pygen_variable_ImageOperandsEntries},
   {SPV_OPERAND_TYPE_FP_FAST_MATH_MODE, ARRAY_SIZE(pygen_variable_FPFastMathModeEntries), pygen_variable_FPFastMathModeEntries},
@@ -954,6 +1017,12 @@ static const spv_operand_desc_group_t pygen_variable_OperandInfoTable[] = {
   {SPV_OPERAND_TYPE_DEBUG_COMPOSITE_TYPE, ARRAY_SIZE(pygen_variable_DebugCompositeTypeEntries), pygen_variable_DebugCompositeTypeEntries},
   {SPV_OPERAND_TYPE_DEBUG_TYPE_QUALIFIER, ARRAY_SIZE(pygen_variable_DebugTypeQualifierEntries), pygen_variable_DebugTypeQualifierEntries},
   {SPV_OPERAND_TYPE_DEBUG_OPERATION, ARRAY_SIZE(pygen_variable_DebugOperationEntries), pygen_variable_DebugOperationEntries},
+  {SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS, ARRAY_SIZE(pygen_variable_CLDEBUG100_DebugInfoFlagsEntries), pygen_variable_CLDEBUG100_DebugInfoFlagsEntries},
+  {SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING, ARRAY_SIZE(pygen_variable_CLDEBUG100_DebugBaseTypeAttributeEncodingEntries), pygen_variable_CLDEBUG100_DebugBaseTypeAttributeEncodingEntries},
+  {SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_COMPOSITE_TYPE, ARRAY_SIZE(pygen_variable_CLDEBUG100_DebugCompositeTypeEntries), pygen_variable_CLDEBUG100_DebugCompositeTypeEntries},
+  {SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_TYPE_QUALIFIER, ARRAY_SIZE(pygen_variable_CLDEBUG100_DebugTypeQualifierEntries), pygen_variable_CLDEBUG100_DebugTypeQualifierEntries},
+  {SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION, ARRAY_SIZE(pygen_variable_CLDEBUG100_DebugOperationEntries), pygen_variable_CLDEBUG100_DebugOperationEntries},
+  {SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_IMPORTED_ENTITY, ARRAY_SIZE(pygen_variable_CLDEBUG100_DebugImportedEntityEntries), pygen_variable_CLDEBUG100_DebugImportedEntityEntries},
   {SPV_OPERAND_TYPE_OPTIONAL_IMAGE, ARRAY_SIZE(pygen_variable_ImageOperandsEntries), pygen_variable_ImageOperandsEntries},
   {SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS, ARRAY_SIZE(pygen_variable_MemoryAccessEntries), pygen_variable_MemoryAccessEntries},
   {SPV_OPERAND_TYPE_OPTIONAL_ACCESS_QUALIFIER, ARRAY_SIZE(pygen_variable_AccessQualifierEntries), pygen_variable_AccessQualifierEntries}

+ 8 - 5
3rdparty/spirv-tools/include/spirv-tools/instrument.hpp

@@ -35,10 +35,9 @@ namespace spvtools {
 // generated by InstrumentPass::GenDebugStreamWrite. This method is utilized
 // by InstBindlessCheckPass.
 //
-// kInst2* values support version 2 of the output record format. These should
-// be used if available and version 2 is enabled. Version 1 is DEPRECATED.
-// Specifically, version 1 uses two words for the stage-specific section of
-// the output record; version 2 uses three words.
+// kInst2* values support version 2 of the output record format and were used
+// for the transition to this format. These values have now been transferred
+// to the original kInst* values. The kInst2* values are therefore DEPRECATED.
 //
 // The first member of the debug output buffer contains the next available word
 // in the data stream to be written. Shaders will atomically read and update
@@ -124,7 +123,7 @@ static const int kInstRayTracingOutLaunchIdY = kInstCommonOutCnt + 1;
 static const int kInstRayTracingOutLaunchIdZ = kInstCommonOutCnt + 2;
 
 // Size of Common and Stage-specific Members
-static const int kInstStageOutCnt = kInstCommonOutCnt + 2;
+static const int kInstStageOutCnt = kInstCommonOutCnt + 3;
 static const int kInst2StageOutCnt = kInstCommonOutCnt + 3;
 
 // Validation Error Code Offset
@@ -160,6 +159,10 @@ static const int kInst2BindlessUninitOutCnt = kInst2StageOutCnt + 3;
 
 // A buffer address unalloc error will output the 64-bit pointer in
 // two 32-bit pieces, lower bits first.
+static const int kInstBuffAddrUnallocOutDescPtrLo = kInstStageOutCnt + 1;
+static const int kInstBuffAddrUnallocOutDescPtrHi = kInstStageOutCnt + 2;
+static const int kInstBuffAddrUnallocOutCnt = kInstStageOutCnt + 3;
+
 static const int kInst2BuffAddrUnallocOutDescPtrLo = kInst2StageOutCnt + 1;
 static const int kInst2BuffAddrUnallocOutDescPtrHi = kInst2StageOutCnt + 2;
 static const int kInst2BuffAddrUnallocOutCnt = kInst2StageOutCnt + 3;

+ 64 - 4
3rdparty/spirv-tools/include/spirv-tools/libspirv.h

@@ -225,13 +225,23 @@ typedef enum spv_operand_type_t {
   // A sequence of zero or more pairs of (Id, Literal integer)
   LAST_VARIABLE(SPV_OPERAND_TYPE_VARIABLE_ID_LITERAL_INTEGER),
 
-  // The following are concrete enum types.
+  // The following are concrete enum types from the DebugInfo extended
+  // instruction set.
   SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS,  // DebugInfo Sec 3.2.  A mask.
   SPV_OPERAND_TYPE_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING,  // DebugInfo Sec 3.3
   SPV_OPERAND_TYPE_DEBUG_COMPOSITE_TYPE,                // DebugInfo Sec 3.4
   SPV_OPERAND_TYPE_DEBUG_TYPE_QUALIFIER,                // DebugInfo Sec 3.5
   SPV_OPERAND_TYPE_DEBUG_OPERATION,                     // DebugInfo Sec 3.6
 
+  // The following are concrete enum types from the OpenCL.DebugInfo.100
+  // extended instruction set.
+  SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS,  // Sec 3.2. A Mask
+  SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING,  // Sec 3.3
+  SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_COMPOSITE_TYPE,                // Sec 3.4
+  SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_TYPE_QUALIFIER,                // Sec 3.5
+  SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION,                     // Sec 3.6
+  SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_IMPORTED_ENTITY,               // Sec 3.7
+
   // This is a sentinel value, and does not represent an operand type.
   // It should come last.
   SPV_OPERAND_TYPE_NUM_OPERAND_TYPES,
@@ -248,6 +258,12 @@ typedef enum spv_ext_inst_type_t {
   SPV_EXT_INST_TYPE_SPV_AMD_GCN_SHADER,
   SPV_EXT_INST_TYPE_SPV_AMD_SHADER_BALLOT,
   SPV_EXT_INST_TYPE_DEBUGINFO,
+  SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100,
+
+  // Multiple distinct extended instruction set types could return this
+  // value, if they are prefixed with NonSemantic. and are otherwise
+  // unrecognised
+  SPV_EXT_INST_TYPE_NONSEMANTIC_UNKNOWN,
 
   SPV_FORCE_32_BIT_ENUM(spv_ext_inst_type_t)
 } spv_ext_inst_type_t;
@@ -403,8 +419,17 @@ SPIRV_TOOLS_EXPORT const char* spvSoftwareVersionString(void);
 SPIRV_TOOLS_EXPORT const char* spvSoftwareVersionDetailsString(void);
 
 // Certain target environments impose additional restrictions on SPIR-V, so it's
-// often necessary to specify which one applies.  SPV_ENV_UNIVERSAL means
+// often necessary to specify which one applies.  SPV_ENV_UNIVERSAL_* implies an
 // environment-agnostic SPIR-V.
+//
+// When an API method needs to derive a SPIR-V version from a target environment
+// (from the spv_context object), the method will choose the highest version of
+// SPIR-V supported by the target environment.  Examples:
+//    SPV_ENV_VULKAN_1_0           ->  SPIR-V 1.0
+//    SPV_ENV_VULKAN_1_1           ->  SPIR-V 1.3
+//    SPV_ENV_VULKAN_1_1_SPIRV_1_4 ->  SPIR-V 1.4
+//    SPV_ENV_VULKAN_1_2           ->  SPIR-V 1.5
+// Consult the description of API entry points for specific rules.
 typedef enum {
   SPV_ENV_UNIVERSAL_1_0,  // SPIR-V 1.0 latest revision, no other restrictions.
   SPV_ENV_VULKAN_1_0,     // Vulkan 1.0 latest revision.
@@ -432,8 +457,12 @@ typedef enum {
   SPV_ENV_VULKAN_1_1,     // Vulkan 1.1 latest revision.
   SPV_ENV_WEBGPU_0,       // Work in progress WebGPU 1.0.
   SPV_ENV_UNIVERSAL_1_4,  // SPIR-V 1.4 latest revision, no other restrictions.
-  SPV_ENV_VULKAN_1_1_SPIRV_1_4,  // Vulkan 1.1 with SPIR-V 1.4 binary.
+
+  // Vulkan 1.1 with VK_KHR_spirv_1_4, i.e. SPIR-V 1.4 binary.
+  SPV_ENV_VULKAN_1_1_SPIRV_1_4,
+
   SPV_ENV_UNIVERSAL_1_5,  // SPIR-V 1.5 latest revision, no other restrictions.
+  SPV_ENV_VULKAN_1_2,     // Vulkan 1.2 latest revision.
 } spv_target_env;
 
 // SPIR-V Validator can be parameterized with the following Universal Limits.
@@ -456,7 +485,24 @@ SPIRV_TOOLS_EXPORT const char* spvTargetEnvDescription(spv_target_env env);
 // false and sets *env to SPV_ENV_UNIVERSAL_1_0.
 SPIRV_TOOLS_EXPORT bool spvParseTargetEnv(const char* s, spv_target_env* env);
 
-// Creates a context object.  Returns null if env is invalid.
+// Determines the target env value with the least features but which enables
+// the given Vulkan and SPIR-V versions. If such a target is supported, returns
+// true and writes the value to |env|, otherwise returns false.
+//
+// The Vulkan version is given as an unsigned 32-bit number as specified in
+// Vulkan section "29.2.1 Version Numbers": the major version number appears
+// in bits 22 to 21, and the minor version is in bits 12 to 21.  The SPIR-V
+// version is given in the SPIR-V version header word: major version in bits
+// 16 to 23, and minor version in bits 8 to 15.
+SPIRV_TOOLS_EXPORT bool spvParseVulkanEnv(uint32_t vulkan_ver,
+                                          uint32_t spirv_ver,
+                                          spv_target_env* env);
+
+// Creates a context object for most of the SPIRV-Tools API.
+// Returns null if env is invalid.
+//
+// See specific API calls for how the target environment is interpeted
+// (particularly assembly and validation).
 SPIRV_TOOLS_EXPORT spv_context spvContextCreate(spv_target_env env);
 
 // Destroys the given context object.
@@ -637,6 +683,8 @@ SPIRV_TOOLS_EXPORT void spvFuzzerOptionsEnableFuzzerPassValidation(
 // be stored into *binary. Any error will be written into *diagnostic if
 // diagnostic is non-null, otherwise the context's message consumer will be
 // used. The generated binary is independent of the context and may outlive it.
+// The SPIR-V binary version is set to the highest version of SPIR-V supported
+// by the context's target environment.
 SPIRV_TOOLS_EXPORT spv_result_t spvTextToBinary(const spv_const_context context,
                                                 const char* text,
                                                 const size_t length,
@@ -674,6 +722,12 @@ SPIRV_TOOLS_EXPORT void spvBinaryDestroy(spv_binary binary);
 // Validates a SPIR-V binary for correctness. Any errors will be written into
 // *diagnostic if diagnostic is non-null, otherwise the context's message
 // consumer will be used.
+//
+// Validate for SPIR-V spec rules for the SPIR-V version named in the
+// binary's header (at word offset 1).  Additionally, if the context target
+// environment is a client API (such as Vulkan 1.1), then validate for that
+// client API version, to the extent that it is verifiable from data in the
+// binary itself.
 SPIRV_TOOLS_EXPORT spv_result_t spvValidate(const spv_const_context context,
                                             const spv_const_binary binary,
                                             spv_diagnostic* diagnostic);
@@ -681,6 +735,12 @@ SPIRV_TOOLS_EXPORT spv_result_t spvValidate(const spv_const_context context,
 // Validates a SPIR-V binary for correctness. Uses the provided Validator
 // options. Any errors will be written into *diagnostic if diagnostic is
 // non-null, otherwise the context's message consumer will be used.
+//
+// Validate for SPIR-V spec rules for the SPIR-V version named in the
+// binary's header (at word offset 1).  Additionally, if the context target
+// environment is a client API (such as Vulkan 1.1), then validate for that
+// client API version, to the extent that it is verifiable from data in the
+// binary itself, or in the validator options.
 SPIRV_TOOLS_EXPORT spv_result_t spvValidateWithOptions(
     const spv_const_context context, const spv_const_validator_options options,
     const spv_const_binary binary, spv_diagnostic* diagnostic);

+ 24 - 1
3rdparty/spirv-tools/include/spirv-tools/libspirv.hpp

@@ -36,6 +36,9 @@ class Context {
  public:
   // Constructs a context targeting the given environment |env|.
   //
+  // See specific API calls for how the target environment is interpeted
+  // (particularly assembly and validation).
+  //
   // The constructed instance will have an empty message consumer, which just
   // ignores all messages from the library. Use SetMessageConsumer() to supply
   // one if messages are of concern.
@@ -279,16 +282,20 @@ class SpirvTools {
   // Assembles the given assembly |text| and writes the result to |binary|.
   // Returns true on successful assembling. |binary| will be kept untouched if
   // assembling is unsuccessful.
+  // The SPIR-V binary version is set to the highest version of SPIR-V supported
+  // by the target environment with which this SpirvTools object was created.
   bool Assemble(const std::string& text, std::vector<uint32_t>* binary,
                 uint32_t options = kDefaultAssembleOption) const;
   // |text_size| specifies the number of bytes in |text|. A terminating null
   // character is not required to present in |text| as long as |text| is valid.
+  // The SPIR-V binary version is set to the highest version of SPIR-V supported
+  // by the target environment with which this SpirvTools object was created.
   bool Assemble(const char* text, size_t text_size,
                 std::vector<uint32_t>* binary,
                 uint32_t options = kDefaultAssembleOption) const;
 
   // Disassembles the given SPIR-V |binary| with the given |options| and writes
-  // the assembly to |text|. Returns ture on successful disassembling. |text|
+  // the assembly to |text|. Returns true on successful disassembling. |text|
   // will be kept untouched if diassembling is unsuccessful.
   bool Disassemble(const std::vector<uint32_t>& binary, std::string* text,
                    uint32_t options = kDefaultDisassembleOption) const;
@@ -300,10 +307,26 @@ class SpirvTools {
   // Validates the given SPIR-V |binary|. Returns true if no issues are found.
   // Otherwise, returns false and communicates issues via the message consumer
   // registered.
+  // Validates for SPIR-V spec rules for the SPIR-V version named in the
+  // binary's header (at word offset 1).  Additionally, if the target
+  // environment is a client API (such as Vulkan 1.1), then validate for that
+  // client API version, to the extent that it is verifiable from data in the
+  // binary itself.
   bool Validate(const std::vector<uint32_t>& binary) const;
+  // Like the previous overload, but provides the binary as a pointer and size:
   // |binary_size| specifies the number of words in |binary|.
+  // Validates for SPIR-V spec rules for the SPIR-V version named in the
+  // binary's header (at word offset 1).  Additionally, if the target
+  // environment is a client API (such as Vulkan 1.1), then validate for that
+  // client API version, to the extent that it is verifiable from data in the
+  // binary itself.
   bool Validate(const uint32_t* binary, size_t binary_size) const;
   // Like the previous overload, but takes an options object.
+  // Validates for SPIR-V spec rules for the SPIR-V version named in the
+  // binary's header (at word offset 1).  Additionally, if the target
+  // environment is a client API (such as Vulkan 1.1), then validate for that
+  // client API version, to the extent that it is verifiable from data in the
+  // binary itself, or in the validator options.
   bool Validate(const uint32_t* binary, size_t binary_size,
                 spv_validator_options options) const;
 

+ 26 - 9
3rdparty/spirv-tools/include/spirv-tools/optimizer.hpp

@@ -65,9 +65,9 @@ class Optimizer {
   // Constructs an instance with the given target |env|, which is used to decode
   // the binaries to be optimized later.
   //
-  // The constructed instance will have an empty message consumer, which just
-  // ignores all messages from the library. Use SetMessageConsumer() to supply
-  // one if messages are of concern.
+  // The instance will have an empty message consumer, which ignores all
+  // messages from the library. Use SetMessageConsumer() to supply a consumer
+  // if messages are of concern.
   //
   // For collections of passes that are meant to transform the input into
   // another execution environment, then the source environment should be
@@ -164,17 +164,26 @@ class Optimizer {
   bool FlagHasValidForm(const std::string& flag) const;
 
   // Allows changing, after creation time, the target environment to be
-  // optimized for.  Should be called before calling Run().
+  // optimized for and validated.  Should be called before calling Run().
   void SetTargetEnv(const spv_target_env env);
 
   // Optimizes the given SPIR-V module |original_binary| and writes the
-  // optimized binary into |optimized_binary|.
+  // optimized binary into |optimized_binary|. The optimized binary uses
+  // the same SPIR-V version as the original binary.
+  //
   // Returns true on successful optimization, whether or not the module is
   // modified. Returns false if |original_binary| fails to validate or if errors
   // occur when processing |original_binary| using any of the registered passes.
   // In that case, no further passes are executed and the contents in
   // |optimized_binary| may be invalid.
   //
+  // By default, the binary is validated before any transforms are performed,
+  // and optionally after each transform.  Validation uses SPIR-V spec rules
+  // for the SPIR-V version named in the binary's header (at word offset 1).
+  // Additionally, if the target environment is a client API (such as
+  // Vulkan 1.1), then validate for that client API version, to the extent
+  // that it is verifiable from data in the binary itself.
+  //
   // It's allowed to alias |original_binary| to the start of |optimized_binary|.
   bool Run(const uint32_t* original_binary, size_t original_binary_size,
            std::vector<uint32_t>* optimized_binary) const;
@@ -190,6 +199,14 @@ class Optimizer {
 
   // Same as above, except it takes an options object.  See the documentation
   // for |OptimizerOptions| to see which options can be set.
+  //
+  // By default, the binary is validated before any transforms are performed,
+  // and optionally after each transform.  Validation uses SPIR-V spec rules
+  // for the SPIR-V version named in the binary's header (at word offset 1).
+  // Additionally, if the target environment is a client API (such as
+  // Vulkan 1.1), then validate for that client API version, to the extent
+  // that it is verifiable from data in the binary itself, or from the
+  // validator options set on the optimizer options.
   bool Run(const uint32_t* original_binary, const size_t original_binary_size,
            std::vector<uint32_t>* optimized_binary,
            const spv_optimizer_options opt_options) const;
@@ -685,9 +702,9 @@ Optimizer::PassToken CreateSSARewritePass();
 // any resulting half precision values back to float32 as needed. No variables
 // are changed. No image operations are changed.
 //
-// Best if run late since it will generate better code with unneeded function
-// scope loads and stores and composite inserts and extracts removed. Also best
-// if followed by instruction simplification, redundancy elimination and DCE.
+// Best if run after function scope store/load and composite operation
+// eliminations are run. Also best if followed by instruction simplification,
+// redundancy elimination and DCE.
 Optimizer::PassToken CreateConvertRelaxedToHalfPass();
 
 // Create relax float ops pass.
@@ -748,7 +765,7 @@ Optimizer::PassToken CreateCombineAccessChainsPass();
 // |version| specifies the buffer record format.
 Optimizer::PassToken CreateInstBindlessCheckPass(
     uint32_t desc_set, uint32_t shader_id, bool input_length_enable = false,
-    bool input_init_enable = false, uint32_t version = 1);
+    bool input_init_enable = false, uint32_t version = 2);
 
 // Create a pass to instrument physical buffer address checking
 // This pass instruments all physical buffer address references to check that

+ 20 - 8
3rdparty/spirv-tools/source/CMakeLists.txt

@@ -20,6 +20,7 @@ set(LANG_HEADER_PROCESSING_SCRIPT "${spirv-tools_SOURCE_DIR}/utils/generate_lang
 # For now, assume the DebugInfo grammar file is in the current directory.
 # It might migrate to SPIRV-Headers.
 set(DEBUGINFO_GRAMMAR_JSON_FILE "${CMAKE_CURRENT_SOURCE_DIR}/extinst.debuginfo.grammar.json")
+set(CLDEBUGINFO100_GRAMMAR_JSON_FILE "${CMAKE_CURRENT_SOURCE_DIR}/extinst.opencl.debuginfo.100.grammar.json")
 
 # macro() definitions are used in the following because we need to append .inc
 # file paths into some global lists (*_CPP_DEPENDS). And those global lists are
@@ -33,9 +34,13 @@ macro(spvtools_core_tables CONFIG_VERSION)
     COMMAND ${PYTHON_EXECUTABLE} ${GRAMMAR_PROCESSING_SCRIPT}
       --spirv-core-grammar=${GRAMMAR_JSON_FILE}
       --extinst-debuginfo-grammar=${DEBUGINFO_GRAMMAR_JSON_FILE}
+      --extinst-cldebuginfo100-grammar=${CLDEBUGINFO100_GRAMMAR_JSON_FILE}
       --core-insts-output=${GRAMMAR_INSTS_INC_FILE}
       --operand-kinds-output=${GRAMMAR_KINDS_INC_FILE}
-    DEPENDS ${GRAMMAR_PROCESSING_SCRIPT} ${GRAMMAR_JSON_FILE} ${DEBUGINFO_GRAMMAR_JSON_FILE}
+    DEPENDS ${GRAMMAR_PROCESSING_SCRIPT}
+            ${GRAMMAR_JSON_FILE}
+            ${DEBUGINFO_GRAMMAR_JSON_FILE}
+            ${CLDEBUGINFO100_GRAMMAR_JSON_FILE}
     COMMENT "Generate info tables for SPIR-V v${CONFIG_VERSION} core instructions and operands.")
   list(APPEND OPCODE_CPP_DEPENDS ${GRAMMAR_INSTS_INC_FILE})
   list(APPEND OPERAND_CPP_DEPENDS ${GRAMMAR_KINDS_INC_FILE})
@@ -50,9 +55,13 @@ macro(spvtools_enum_string_mapping CONFIG_VERSION)
     COMMAND ${PYTHON_EXECUTABLE} ${GRAMMAR_PROCESSING_SCRIPT}
       --spirv-core-grammar=${GRAMMAR_JSON_FILE}
       --extinst-debuginfo-grammar=${DEBUGINFO_GRAMMAR_JSON_FILE}
+      --extinst-cldebuginfo100-grammar=${CLDEBUGINFO100_GRAMMAR_JSON_FILE}
       --extension-enum-output=${GRAMMAR_EXTENSION_ENUM_INC_FILE}
       --enum-string-mapping-output=${GRAMMAR_ENUM_STRING_MAPPING_INC_FILE}
-    DEPENDS ${GRAMMAR_PROCESSING_SCRIPT} ${GRAMMAR_JSON_FILE} ${DEBUGINFO_GRAMMAR_JSON_FILE}
+    DEPENDS ${GRAMMAR_PROCESSING_SCRIPT}
+            ${GRAMMAR_JSON_FILE}
+            ${DEBUGINFO_GRAMMAR_JSON_FILE}
+            ${CLDEBUGINFO100_GRAMMAR_JSON_FILE}
     COMMENT "Generate enum-string mapping for SPIR-V v${CONFIG_VERSION}.")
   list(APPEND EXTENSION_H_DEPENDS ${GRAMMAR_EXTENSION_ENUM_INC_FILE})
   list(APPEND ENUM_STRING_MAPPING_CPP_DEPENDS ${GRAMMAR_ENUM_STRING_MAPPING_INC_FILE})
@@ -101,13 +110,14 @@ macro(spvtools_opencl_tables CONFIG_VERSION)
   list(APPEND EXTINST_CPP_DEPENDS ${GRAMMAR_INC_FILE})
 endmacro(spvtools_opencl_tables)
 
-macro(spvtools_vendor_tables VENDOR_TABLE SHORT_NAME)
+macro(spvtools_vendor_tables VENDOR_TABLE SHORT_NAME OPERAND_KIND_PREFIX)
   set(INSTS_FILE "${spirv-tools_BINARY_DIR}/${VENDOR_TABLE}.insts.inc")
   set(GRAMMAR_FILE "${spirv-tools_SOURCE_DIR}/source/extinst.${VENDOR_TABLE}.grammar.json")
   add_custom_command(OUTPUT ${INSTS_FILE}
     COMMAND ${PYTHON_EXECUTABLE} ${GRAMMAR_PROCESSING_SCRIPT}
       --extinst-vendor-grammar=${GRAMMAR_FILE}
       --vendor-insts-output=${INSTS_FILE}
+      --vendor-operand-kind-prefix=${OPERAND_KIND_PREFIX}
     DEPENDS ${GRAMMAR_PROCESSING_SCRIPT} ${GRAMMAR_FILE}
     COMMENT "Generate extended instruction tables for ${VENDOR_TABLE}.")
   add_custom_target(spv-tools-${SHORT_NAME} DEPENDS ${INSTS_FILE})
@@ -134,12 +144,14 @@ spvtools_core_tables("unified1")
 spvtools_enum_string_mapping("unified1")
 spvtools_opencl_tables("unified1")
 spvtools_glsl_tables("unified1")
-spvtools_vendor_tables("spv-amd-shader-explicit-vertex-parameter" "spv-amd-sevp")
-spvtools_vendor_tables("spv-amd-shader-trinary-minmax" "spv-amd-stm")
-spvtools_vendor_tables("spv-amd-gcn-shader" "spv-amd-gs")
-spvtools_vendor_tables("spv-amd-shader-ballot" "spv-amd-sb")
-spvtools_vendor_tables("debuginfo" "debuginfo")
+spvtools_vendor_tables("spv-amd-shader-explicit-vertex-parameter" "spv-amd-sevp" "")
+spvtools_vendor_tables("spv-amd-shader-trinary-minmax" "spv-amd-stm" "")
+spvtools_vendor_tables("spv-amd-gcn-shader" "spv-amd-gs" "")
+spvtools_vendor_tables("spv-amd-shader-ballot" "spv-amd-sb" "")
+spvtools_vendor_tables("debuginfo" "debuginfo" "")
+spvtools_vendor_tables("opencl.debuginfo.100" "cldi100" "CLDEBUG100_")
 spvtools_extinst_lang_headers("DebugInfo" ${DEBUGINFO_GRAMMAR_JSON_FILE})
+spvtools_extinst_lang_headers("OpenCLDebugInfo100" ${CLDEBUGINFO100_GRAMMAR_JSON_FILE})
 
 spvtools_vimsyntax("unified1" "1.0")
 add_custom_target(spirv-tools-vimsyntax DEPENDS ${VIMSYNTAX_FILE})

+ 23 - 4
3rdparty/spirv-tools/source/binary.cpp

@@ -477,9 +477,22 @@ spv_result_t Parser::parseOperand(size_t inst_offset,
       assert(SpvOpExtInst == opcode);
       assert(inst->ext_inst_type != SPV_EXT_INST_TYPE_NONE);
       spv_ext_inst_desc ext_inst;
-      if (grammar_.lookupExtInst(inst->ext_inst_type, word, &ext_inst))
-        return diagnostic() << "Invalid extended instruction number: " << word;
-      spvPushOperandTypes(ext_inst->operandTypes, expected_operands);
+      if (grammar_.lookupExtInst(inst->ext_inst_type, word, &ext_inst) ==
+          SPV_SUCCESS) {
+        // if we know about this ext inst, push the expected operands
+        spvPushOperandTypes(ext_inst->operandTypes, expected_operands);
+      } else {
+        // if we don't know this extended instruction and the set isn't
+        // non-semantic, we cannot process further
+        if (!spvExtInstIsNonSemantic(inst->ext_inst_type)) {
+          return diagnostic()
+                 << "Invalid extended instruction number: " << word;
+        } else {
+          // for non-semantic instruction sets, we know the form of all such
+          // extended instructions contains a series of IDs as parameters
+          expected_operands->push_back(SPV_OPERAND_TYPE_VARIABLE_ID);
+        }
+      }
     } break;
 
     case SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER: {
@@ -623,7 +636,12 @@ spv_result_t Parser::parseOperand(size_t inst_offset,
     case SPV_OPERAND_TYPE_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING:
     case SPV_OPERAND_TYPE_DEBUG_COMPOSITE_TYPE:
     case SPV_OPERAND_TYPE_DEBUG_TYPE_QUALIFIER:
-    case SPV_OPERAND_TYPE_DEBUG_OPERATION: {
+    case SPV_OPERAND_TYPE_DEBUG_OPERATION:
+    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING:
+    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_COMPOSITE_TYPE:
+    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_TYPE_QUALIFIER:
+    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION:
+    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_IMPORTED_ENTITY: {
       // A single word that is a plain enum value.
 
       // Map an optional operand type to its corresponding concrete type.
@@ -647,6 +665,7 @@ spv_result_t Parser::parseOperand(size_t inst_offset,
     case SPV_OPERAND_TYPE_OPTIONAL_IMAGE:
     case SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS:
     case SPV_OPERAND_TYPE_SELECTION_CONTROL:
+    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS:
     case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS: {
       // This operand is a mask.
 

+ 18 - 4
3rdparty/spirv-tools/source/disassemble.cpp

@@ -217,10 +217,18 @@ void Disassembler::EmitOperand(const spv_parsed_instruction_t& inst,
       break;
     case SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER: {
       spv_ext_inst_desc ext_inst;
-      if (grammar_.lookupExtInst(inst.ext_inst_type, word, &ext_inst))
-        assert(false && "should have caught this earlier");
       SetRed();
-      stream_ << ext_inst->name;
+      if (grammar_.lookupExtInst(inst.ext_inst_type, word, &ext_inst) ==
+          SPV_SUCCESS) {
+        stream_ << ext_inst->name;
+      } else {
+        if (!spvExtInstIsNonSemantic(inst.ext_inst_type)) {
+          assert(false && "should have caught this earlier");
+        } else {
+          // for non-semantic instruction sets we can just print the number
+          stream_ << word;
+        }
+      }
     } break;
     case SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER: {
       spv_opcode_desc opcode_desc;
@@ -272,7 +280,12 @@ void Disassembler::EmitOperand(const spv_parsed_instruction_t& inst,
     case SPV_OPERAND_TYPE_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING:
     case SPV_OPERAND_TYPE_DEBUG_COMPOSITE_TYPE:
     case SPV_OPERAND_TYPE_DEBUG_TYPE_QUALIFIER:
-    case SPV_OPERAND_TYPE_DEBUG_OPERATION: {
+    case SPV_OPERAND_TYPE_DEBUG_OPERATION:
+    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING:
+    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_COMPOSITE_TYPE:
+    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_TYPE_QUALIFIER:
+    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION:
+    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_IMPORTED_ENTITY: {
       spv_operand_desc entry;
       if (grammar_.lookupOperand(operand.type, word, &entry))
         assert(false && "should have caught this earlier");
@@ -285,6 +298,7 @@ void Disassembler::EmitOperand(const spv_parsed_instruction_t& inst,
     case SPV_OPERAND_TYPE_MEMORY_ACCESS:
     case SPV_OPERAND_TYPE_SELECTION_CONTROL:
     case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS:
+    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS:
       EmitMaskOperand(operand.type, word);
       break;
     default:

+ 27 - 0
3rdparty/spirv-tools/source/ext_inst.cpp

@@ -28,6 +28,7 @@
 
 #include "debuginfo.insts.inc"
 #include "glsl.std.450.insts.inc"
+#include "opencl.debuginfo.100.insts.inc"
 #include "opencl.std.insts.inc"
 
 #include "spirv-tools/libspirv.h"
@@ -51,6 +52,8 @@ static const spv_ext_inst_group_t kGroups_1_0[] = {
      ARRAY_SIZE(spv_amd_shader_ballot_entries), spv_amd_shader_ballot_entries},
     {SPV_EXT_INST_TYPE_DEBUGINFO, ARRAY_SIZE(debuginfo_entries),
      debuginfo_entries},
+    {SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100,
+     ARRAY_SIZE(opencl_debuginfo_100_entries), opencl_debuginfo_100_entries},
 };
 
 static const spv_ext_inst_table_t kTable_1_0 = {ARRAY_SIZE(kGroups_1_0),
@@ -85,6 +88,7 @@ spv_result_t spvExtInstTableGet(spv_ext_inst_table* pExtInstTable,
     case SPV_ENV_WEBGPU_0:
     case SPV_ENV_UNIVERSAL_1_4:
     case SPV_ENV_UNIVERSAL_1_5:
+    case SPV_ENV_VULKAN_1_2:
       *pExtInstTable = &kTable_1_0;
       return SPV_SUCCESS;
     default:
@@ -116,9 +120,32 @@ spv_ext_inst_type_t spvExtInstImportTypeGet(const char* name) {
   if (!strcmp("DebugInfo", name)) {
     return SPV_EXT_INST_TYPE_DEBUGINFO;
   }
+  if (!strcmp("OpenCL.DebugInfo.100", name)) {
+    return SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100;
+  }
+  // ensure to add any known non-semantic extended instruction sets
+  // above this point, and update spvExtInstIsNonSemantic()
+  if (!strncmp("NonSemantic.", name, 12)) {
+    return SPV_EXT_INST_TYPE_NONSEMANTIC_UNKNOWN;
+  }
   return SPV_EXT_INST_TYPE_NONE;
 }
 
+bool spvExtInstIsNonSemantic(const spv_ext_inst_type_t type) {
+  if (type == SPV_EXT_INST_TYPE_NONSEMANTIC_UNKNOWN) {
+    return true;
+  }
+  return false;
+}
+
+bool spvExtInstIsDebugInfo(const spv_ext_inst_type_t type) {
+  if (type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 ||
+      type == SPV_EXT_INST_TYPE_DEBUGINFO) {
+    return true;
+  }
+  return false;
+}
+
 spv_result_t spvExtInstTableNameLookup(const spv_ext_inst_table table,
                                        const spv_ext_inst_type_t type,
                                        const char* name,

+ 6 - 0
3rdparty/spirv-tools/source/ext_inst.h

@@ -21,6 +21,12 @@
 // Gets the type of the extended instruction set with the specified name.
 spv_ext_inst_type_t spvExtInstImportTypeGet(const char* name);
 
+// Returns true if the extended instruction set is non-semantic
+bool spvExtInstIsNonSemantic(const spv_ext_inst_type_t type);
+
+// Returns true if the extended instruction set is debug info
+bool spvExtInstIsDebugInfo(const spv_ext_inst_type_t type);
+
 // Finds the named extented instruction of the given type in the given extended
 // instruction table. On success, returns SPV_SUCCESS and writes a handle of
 // the instruction entry into *entry.

+ 619 - 0
3rdparty/spirv-tools/source/extinst.opencl.debuginfo.100.grammar.json

@@ -0,0 +1,619 @@
+{
+  "copyright" : [
+    "Copyright (c) 2018 The Khronos Group Inc.",
+    "",
+    "Permission is hereby granted, free of charge, to any person obtaining a copy",
+    "of this software and/or associated documentation files (the \"Materials\"),",
+    "to deal in the Materials without restriction, including without limitation",
+    "the rights to use, copy, modify, merge, publish, distribute, sublicense,",
+    "and/or sell copies of the Materials, and to permit persons to whom the",
+    "Materials are furnished to do so, subject to the following conditions:",
+    "",
+    "The above copyright notice and this permission notice shall be included in",
+    "all copies or substantial portions of the Materials.",
+    "",
+    "MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS",
+    "STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND",
+    "HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ ",
+    "",
+    "THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS",
+    "OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,",
+    "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL",
+    "THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER",
+    "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING",
+    "FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS",
+    "IN THE MATERIALS."
+  ],
+  "version" : 200,
+  "revision" : 2,
+  "instructions" : [
+    {
+      "opname" : "DebugInfoNone",
+      "opcode" : 0
+    },
+    {
+      "opname" : "DebugCompilationUnit",
+      "opcode" : 1,
+      "operands" : [
+        { "kind" : "LiteralInteger", "name" : "'Version'" },
+        { "kind" : "LiteralInteger", "name" : "'DWARF Version'" },
+        { "kind" : "IdRef", "name" : "'Source'" },
+        { "kind" : "SourceLanguage", "name" : "'Language'" }
+      ]
+    },
+    {
+      "opname" : "DebugTypeBasic",
+      "opcode" : 2,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Name'" },
+        { "kind" : "IdRef", "name" : "'Size'" },
+        { "kind" : "DebugBaseTypeAttributeEncoding", "name" : "'Encoding'" }
+      ]
+    },
+    {
+      "opname" : "DebugTypePointer",
+      "opcode" : 3,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Base Type'" },
+        { "kind" : "StorageClass", "name" : "'Storage Class'" },
+        { "kind" : "DebugInfoFlags", "name" : "'Flags'" }
+      ]
+    },
+    {
+      "opname" : "DebugTypeQualifier",
+      "opcode" : 4,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Base Type'" },
+        { "kind" : "DebugTypeQualifier", "name" : "'Type Qualifier'" }
+      ]
+    },
+    {
+      "opname" : "DebugTypeArray",
+      "opcode" : 5,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Base Type'" },
+        { "kind" : "IdRef", "name" : "'Component Counts'", "quantifier" : "*" }
+      ]
+    },
+    {
+      "opname" : "DebugTypeVector",
+      "opcode" : 6,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Base Type'" },
+        { "kind" : "LiteralInteger", "name" : "'Component Count'" }
+      ]
+    },
+    {
+      "opname" : "DebugTypedef",
+      "opcode" : 7,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Name'" },
+        { "kind" : "IdRef", "name" : "'Base Type'" },
+        { "kind" : "IdRef", "name" : "'Source'" },
+        { "kind" : "LiteralInteger", "name" : "'Line'" },
+        { "kind" : "LiteralInteger", "name" : "'Column'" },
+        { "kind" : "IdRef", "name" : "'Parent'" }
+      ]
+    },
+    {
+      "opname" : "DebugTypeFunction",
+      "opcode" : 8,
+      "operands" : [
+        { "kind" : "DebugInfoFlags", "name" : "'Flags'" },
+        { "kind" : "IdRef", "name" : "'Return Type'" },
+        { "kind" : "IdRef", "name" : "'Parameter Types'", "quantifier" : "*" }
+      ]
+    },
+    {
+      "opname" : "DebugTypeEnum",
+      "opcode" : 9,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Name'" },
+        { "kind" : "IdRef", "name" : "'Underlying Type'" },
+        { "kind" : "IdRef", "name" : "'Source'" },
+        { "kind" : "LiteralInteger", "name" : "'Line'" },
+        { "kind" : "LiteralInteger", "name" : "'Column'" },
+        { "kind" : "IdRef", "name" : "'Parent'" },
+        { "kind" : "IdRef", "name" : "'Size'" },
+        { "kind" : "DebugInfoFlags", "name" : "'Flags'" },
+        { "kind" : "PairIdRefIdRef", "name" : "'Value, Name, Value, Name, ...'", "quantifier" : "*" }
+      ]
+    },
+    {
+      "opname" : "DebugTypeComposite",
+      "opcode" : 10,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Name'" },
+        { "kind" : "DebugCompositeType", "name" : "'Tag'" },
+        { "kind" : "IdRef", "name" : "'Source'" },
+        { "kind" : "LiteralInteger", "name" : "'Line'" },
+        { "kind" : "LiteralInteger", "name" : "'Column'" },
+        { "kind" : "IdRef", "name" : "'Parent'" },
+        { "kind" : "IdRef", "name" : "'Linkage Name'" },
+        { "kind" : "IdRef", "name" : "'Size'" },
+        { "kind" : "DebugInfoFlags", "name" : "'Flags'" },
+        { "kind" : "IdRef", "name" : "'Members'", "quantifier" : "*" }
+      ]
+    },
+    {
+      "opname" : "DebugTypeMember",
+      "opcode" : 11,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Name'" },
+        { "kind" : "IdRef", "name" : "'Type'" },
+        { "kind" : "IdRef", "name" : "'Source'" },
+        { "kind" : "LiteralInteger", "name" : "'Line'" },
+        { "kind" : "LiteralInteger", "name" : "'Column'" },
+        { "kind" : "IdRef", "name" : "'Parent'" },
+        { "kind" : "IdRef", "name" : "'Offset'" },
+        { "kind" : "IdRef", "name" : "'Size'" },
+        { "kind" : "DebugInfoFlags", "name" : "'Flags'" },
+        { "kind" : "IdRef", "name" : "'Value'", "quantifier" : "?" }
+      ]
+    },
+    {
+      "opname" : "DebugTypeInheritance",
+      "opcode" : 12,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Child'" },
+        { "kind" : "IdRef", "name" : "'Parent'" },
+        { "kind" : "IdRef", "name" : "'Offset'" },
+        { "kind" : "IdRef", "name" : "'Size'" },
+        { "kind" : "DebugInfoFlags", "name" : "'Flags'" }
+      ]
+    },
+    {
+      "opname" : "DebugTypePtrToMember",
+      "opcode" : 13,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Member Type'" },
+        { "kind" : "IdRef", "name" : "'Parent'" }
+      ]
+    },
+    {
+      "opname" : "DebugTypeTemplate",
+      "opcode" : 14,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Target'" },
+        { "kind" : "IdRef", "name" : "'Parameters'", "quantifier" : "*" }
+      ]
+    },
+    {
+      "opname" : "DebugTypeTemplateParameter",
+      "opcode" : 15,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Name'" },
+        { "kind" : "IdRef", "name" : "'Actual Type'" },
+        { "kind" : "IdRef", "name" : "'Value'" },
+        { "kind" : "IdRef", "name" : "'Source'" },
+        { "kind" : "LiteralInteger", "name" : "'Line'" },
+        { "kind" : "LiteralInteger", "name" : "'Column'" }
+      ]
+    },
+    {
+      "opname" : "DebugTypeTemplateTemplateParameter",
+      "opcode" : 16,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Name'" },
+        { "kind" : "IdRef", "name" : "'Template Name'" },
+        { "kind" : "IdRef", "name" : "'Source'" },
+        { "kind" : "LiteralInteger", "name" : "'Line'" },
+        { "kind" : "LiteralInteger", "name" : "'Column'" }
+      ]
+    },
+    {
+      "opname" : "DebugTypeTemplateParameterPack",
+      "opcode" : 17,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Name'" },
+        { "kind" : "IdRef", "name" : "'Source'" },
+        { "kind" : "LiteralInteger", "name" : "'Line'" },
+        { "kind" : "LiteralInteger", "name" : "'Column'" },
+        { "kind" : "IdRef", "name" : "'Template Parameters'", "quantifier" : "*" }
+      ]
+    },
+    {
+      "opname" : "DebugGlobalVariable",
+      "opcode" : 18,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Name'" },
+        { "kind" : "IdRef", "name" : "'Type'" },
+        { "kind" : "IdRef", "name" : "'Source'" },
+        { "kind" : "LiteralInteger", "name" : "'Line'" },
+        { "kind" : "LiteralInteger", "name" : "'Column'" },
+        { "kind" : "IdRef", "name" : "'Parent'" },
+        { "kind" : "IdRef", "name" : "'Linkage Name'" },
+        { "kind" : "IdRef", "name" : "'Variable'" },
+        { "kind" : "DebugInfoFlags", "name" : "'Flags'" },
+        { "kind" : "IdRef", "name" : "'Static Member Declaration'", "quantifier" : "?" }
+      ]
+    },
+    {
+      "opname" : "DebugFunctionDeclaration",
+      "opcode" : 19,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Name'" },
+        { "kind" : "IdRef", "name" : "'Type'" },
+        { "kind" : "IdRef", "name" : "'Source'" },
+        { "kind" : "LiteralInteger", "name" : "'Line'" },
+        { "kind" : "LiteralInteger", "name" : "'Column'" },
+        { "kind" : "IdRef", "name" : "'Parent'" },
+        { "kind" : "IdRef", "name" : "'Linkage Name'" },
+        { "kind" : "DebugInfoFlags", "name" : "'Flags'" }
+      ]
+    },
+    {
+      "opname" : "DebugFunction",
+      "opcode" : 20,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Name'" },
+        { "kind" : "IdRef", "name" : "'Type'" },
+        { "kind" : "IdRef", "name" : "'Source'" },
+        { "kind" : "LiteralInteger", "name" : "'Line'" },
+        { "kind" : "LiteralInteger", "name" : "'Column'" },
+        { "kind" : "IdRef", "name" : "'Parent'" },
+        { "kind" : "IdRef", "name" : "'Linkage Name'" },
+        { "kind" : "DebugInfoFlags", "name" : "'Flags'" },
+        { "kind" : "LiteralInteger", "name" : "'Scope Line'" },
+        { "kind" : "IdRef", "name" : "'Function'" },
+        { "kind" : "IdRef", "name" : "'Declaration'", "quantifier" : "?" }
+      ]
+    },
+    {
+      "opname" : "DebugLexicalBlock",
+      "opcode" : 21,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Source'" },
+        { "kind" : "LiteralInteger", "name" : "'Line'" },
+        { "kind" : "LiteralInteger", "name" : "'Column'" },
+        { "kind" : "IdRef", "name" : "'Parent'" },
+        { "kind" : "IdRef", "name" : "'Name'", "quantifier" : "?" }
+      ]
+    },
+    {
+      "opname" : "DebugLexicalBlockDiscriminator",
+      "opcode" : 22,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Source'" },
+        { "kind" : "LiteralInteger", "name" : "'Discriminator'" },
+        { "kind" : "IdRef", "name" : "'Parent'" }
+      ]
+    },
+    {
+      "opname" : "DebugScope",
+      "opcode" : 23,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Scope'" },
+        { "kind" : "IdRef", "name" : "'Inlined At'", "quantifier" : "?" }
+      ]
+    },
+    {
+      "opname" : "DebugNoScope",
+      "opcode" : 24
+    },
+    {
+      "opname" : "DebugInlinedAt",
+      "opcode" : 25,
+      "operands" : [
+        { "kind" : "LiteralInteger", "name" : "'Line'" },
+        { "kind" : "IdRef", "name" : "'Scope'" },
+        { "kind" : "IdRef", "name" : "'Inlined'", "quantifier" : "?" }
+      ]
+    },
+    {
+      "opname" : "DebugLocalVariable",
+      "opcode" : 26,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Name'" },
+        { "kind" : "IdRef", "name" : "'Type'" },
+        { "kind" : "IdRef", "name" : "'Source'" },
+        { "kind" : "LiteralInteger", "name" : "'Line'" },
+        { "kind" : "LiteralInteger", "name" : "'Column'" },
+        { "kind" : "IdRef", "name" : "'Parent'" },
+        { "kind" : "DebugInfoFlags", "name" : "'Flags'" },
+        { "kind" : "LiteralInteger", "name" : "'Arg Number'", "quantifier" : "?" }
+      ]
+    },
+    {
+      "opname" : "DebugInlinedVariable",
+      "opcode" : 27,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Variable'" },
+        { "kind" : "IdRef", "name" : "'Inlined'" }
+      ]
+    },
+    {
+      "opname" : "DebugDeclare",
+      "opcode" : 28,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Local Variable'" },
+        { "kind" : "IdRef", "name" : "'Variable'" },
+        { "kind" : "IdRef", "name" : "'Expression'" }
+      ]
+    },
+    {
+      "opname" : "DebugValue",
+      "opcode" : 29,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Local Variable'" },
+        { "kind" : "IdRef", "name" : "'Value'" },
+        { "kind" : "IdRef", "name" : "'Expression'" },
+        { "kind" : "IdRef", "name" : "'Indexes'", "quantifier" : "*" }
+      ]
+    },
+    {
+      "opname" : "DebugOperation",
+      "opcode" : 30,
+      "operands" : [
+        { "kind" : "DebugOperation", "name" : "'OpCode'" },
+        { "kind" : "LiteralInteger", "name" : "'Operands ...'", "quantifier" : "*" }
+      ]
+    },
+    {
+      "opname" : "DebugExpression",
+      "opcode" : 31,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Operands ...'", "quantifier" : "*" }
+      ]
+    },
+    {
+      "opname" : "DebugMacroDef",
+      "opcode" : 32,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Source'" },
+        { "kind" : "LiteralInteger", "name" : "'Line'" },
+        { "kind" : "IdRef", "name" : "'Name'" },
+        { "kind" : "IdRef", "name" : "'Value'", "quantifier" : "?" }
+      ]
+    },
+    {
+      "opname" : "DebugMacroUndef",
+      "opcode" : 33,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Source'" },
+        { "kind" : "LiteralInteger", "name" : "'Line'" },
+        { "kind" : "IdRef", "name" : "'Macro'" }
+      ]
+    },
+    {
+      "opname" : "DebugSource",
+      "opcode" : 35,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'File'" },
+        { "kind" : "IdRef", "name" : "'Text'", "quantifier" : "?" }
+      ]
+    }
+  ],
+  "operand_kinds" : [
+    {
+      "category" : "BitEnum",
+      "kind" : "DebugInfoFlags",
+      "enumerants" : [
+        {
+          "enumerant" : "FlagIsProtected",
+          "value" : "0x01"
+        },
+        {
+          "enumerant" : "FlagIsPrivate",
+          "value" : "0x02"
+        },
+        {
+          "enumerant" : "FlagIsPublic",
+          "value" : "0x03"
+        },
+        {
+          "enumerant" : "FlagIsLocal",
+          "value" : "0x04"
+        },
+        {
+          "enumerant" : "FlagIsDefinition",
+          "value" : "0x08"
+        },
+        {
+          "enumerant" : "FlagFwdDecl",
+          "value" : "0x10"
+        },
+        {
+          "enumerant" : "FlagArtificial",
+          "value" : "0x20"
+        },
+        {
+          "enumerant" : "FlagExplicit",
+          "value" : "0x40"
+        },
+        {
+          "enumerant" : "FlagPrototyped",
+          "value" : "0x80"
+        },
+        {
+          "enumerant" : "FlagObjectPointer",
+          "value" : "0x100"
+        },
+        {
+          "enumerant" : "FlagStaticMember",
+          "value" : "0x200"
+        },
+        {
+          "enumerant" : "FlagIndirectVariable",
+          "value" : "0x400"
+        },
+        {
+          "enumerant" : "FlagLValueReference",
+          "value" : "0x800"
+        },
+        {
+          "enumerant" : "FlagRValueReference",
+          "value" : "0x1000"
+        },
+        {
+          "enumerant" : "FlagIsOptimized",
+          "value" : "0x2000"
+        },
+        {
+          "enumerant" : "FlagIsEnumClass",
+          "value" : "0x4000"
+        },
+        {
+          "enumerant" : "FlagTypePassByValue",
+          "value" : "0x8000"
+        },
+        {
+          "enumerant" : "FlagTypePassByReference",
+          "value" : "0x10000"
+        }
+      ]
+    },
+    {
+      "category" : "ValueEnum",
+      "kind" : "DebugBaseTypeAttributeEncoding",
+      "enumerants" : [
+        {
+          "enumerant" : "Unspecified",
+          "value" : "0"
+        },
+        {
+          "enumerant" : "Address",
+          "value" : "1"
+        },
+        {
+          "enumerant" : "Boolean",
+          "value" : "2"
+        },
+        {
+          "enumerant" : "Float",
+          "value" : "3"
+        },
+        {
+          "enumerant" : "Signed",
+          "value" : "4"
+        },
+        {
+          "enumerant" : "SignedChar",
+          "value" : "5"
+        },
+        {
+          "enumerant" : "Unsigned",
+          "value" : "6"
+        },
+        {
+          "enumerant" : "UnsignedChar",
+          "value" : "7"
+        }
+      ]
+    },
+    {
+      "category" : "ValueEnum",
+      "kind" : "DebugCompositeType",
+      "enumerants" : [
+        {
+          "enumerant" : "Class",
+          "value" : "0"
+        },
+        {
+          "enumerant" : "Structure",
+          "value" : "1"
+        },
+        {
+          "enumerant" : "Union",
+          "value" : "2"
+        }
+      ]
+    },
+    {
+      "category" : "ValueEnum",
+      "kind" : "DebugTypeQualifier",
+      "enumerants" : [
+        {
+          "enumerant" : "ConstType",
+          "value" : "0"
+        },
+        {
+          "enumerant" : "VolatileType",
+          "value" : "1"
+        },
+        {
+          "enumerant" : "RestrictType",
+          "value" : "2"
+        },
+        {
+          "enumerant" : "AtomicType",
+          "value" : "3"
+        }
+      ]
+    },
+    {
+      "category" : "ValueEnum",
+      "kind" : "DebugOperation",
+      "enumerants" : [
+        {
+          "enumerant" : "Deref",
+          "value" : "0"
+        },
+        {
+          "enumerant" : "Plus",
+          "value" : "1"
+        },
+        {
+          "enumerant" : "Minus",
+          "value" : "2"
+        },
+        {
+          "enumerant" : "PlusUconst",
+          "value" : "3",
+          "parameters" : [
+             { "kind" : "LiteralInteger" }
+          ]
+        },
+        {
+          "enumerant" : "BitPiece",
+          "value" : "4",
+          "parameters" : [
+             { "kind" : "LiteralInteger" },
+             { "kind" : "LiteralInteger" }
+          ]
+        },
+        {
+          "enumerant" : "Swap",
+          "value" : "5"
+        },
+        {
+          "enumerant" : "Xderef",
+          "value" : "6"
+        },
+        {
+          "enumerant" : "StackValue",
+          "value" : "7"
+        },
+        {
+          "enumerant" : "Constu",
+          "value" : "8",
+          "parameters" : [
+             { "kind" : "LiteralInteger" }
+          ]
+        },
+        {
+          "enumerant" : "Fragment",
+          "value" : "9",
+          "parameters" : [
+             { "kind" : "LiteralInteger" },
+             { "kind" : "LiteralInteger" }
+          ]
+        }
+      ]
+    },
+    {
+      "category" : "ValueEnum",
+      "kind" : "DebugImportedEntity",
+      "enumerants" : [
+        {
+          "enumerant" : "ImportedModule",
+          "value" : "0"
+        },
+        {
+          "enumerant" : "ImportedDeclaration",
+          "value" : "1"
+	}
+      ]
+    }
+  ]
+}

+ 36 - 0
3rdparty/spirv-tools/source/fuzz/CMakeLists.txt

@@ -36,6 +36,8 @@ if(SPIRV_BUILD_FUZZER)
         fuzzer.h
         fuzzer_context.h
         fuzzer_pass.h
+        fuzzer_pass_add_composite_types.h
+        fuzzer_pass_add_dead_blocks.h
         fuzzer_pass_add_dead_breaks.h
         fuzzer_pass_add_dead_continues.h
         fuzzer_pass_add_no_contraction_decorations.h
@@ -47,12 +49,16 @@ if(SPIRV_BUILD_FUZZER)
         fuzzer_pass_apply_id_synonyms.h
         fuzzer_pass_construct_composites.h
         fuzzer_pass_copy_objects.h
+        fuzzer_pass_donate_modules.h
+        fuzzer_pass_merge_blocks.h
         fuzzer_pass_obfuscate_constants.h
+        fuzzer_pass_outline_functions.h
         fuzzer_pass_permute_blocks.h
         fuzzer_pass_split_blocks.h
         fuzzer_util.h
         id_use_descriptor.h
         instruction_descriptor.h
+        instruction_message.h
         protobufs/spirvfuzz_protobufs.h
         pseudo_random_generator.h
         random_generator.h
@@ -60,18 +66,30 @@ if(SPIRV_BUILD_FUZZER)
         shrinker.h
         transformation.h
         transformation_add_constant_boolean.h
+        transformation_add_constant_composite.h
         transformation_add_constant_scalar.h
+        transformation_add_dead_block.h
         transformation_add_dead_break.h
         transformation_add_dead_continue.h
+        transformation_add_function.h
+        transformation_add_global_undef.h
+        transformation_add_global_variable.h
         transformation_add_no_contraction_decoration.h
+        transformation_add_type_array.h
         transformation_add_type_boolean.h
         transformation_add_type_float.h
+        transformation_add_type_function.h
         transformation_add_type_int.h
+        transformation_add_type_matrix.h
         transformation_add_type_pointer.h
+        transformation_add_type_struct.h
+        transformation_add_type_vector.h
         transformation_composite_construct.h
         transformation_composite_extract.h
         transformation_copy_object.h
+        transformation_merge_blocks.h
         transformation_move_block_down.h
+        transformation_outline_function.h
         transformation_replace_boolean_constant_with_constant_binary.h
         transformation_replace_constant_with_uniform.h
         transformation_replace_id_with_synonym.h
@@ -90,6 +108,8 @@ if(SPIRV_BUILD_FUZZER)
         fuzzer.cpp
         fuzzer_context.cpp
         fuzzer_pass.cpp
+        fuzzer_pass_add_composite_types.cpp
+        fuzzer_pass_add_dead_blocks.cpp
         fuzzer_pass_add_dead_breaks.cpp
         fuzzer_pass_add_dead_continues.cpp
         fuzzer_pass_add_no_contraction_decorations.cpp
@@ -101,30 +121,46 @@ if(SPIRV_BUILD_FUZZER)
         fuzzer_pass_apply_id_synonyms.cpp
         fuzzer_pass_construct_composites.cpp
         fuzzer_pass_copy_objects.cpp
+        fuzzer_pass_donate_modules.cpp
+        fuzzer_pass_merge_blocks.cpp
         fuzzer_pass_obfuscate_constants.cpp
+        fuzzer_pass_outline_functions.cpp
         fuzzer_pass_permute_blocks.cpp
         fuzzer_pass_split_blocks.cpp
         fuzzer_util.cpp
         id_use_descriptor.cpp
         instruction_descriptor.cpp
+        instruction_message.cpp
         pseudo_random_generator.cpp
         random_generator.cpp
         replayer.cpp
         shrinker.cpp
         transformation.cpp
         transformation_add_constant_boolean.cpp
+        transformation_add_constant_composite.cpp
         transformation_add_constant_scalar.cpp
+        transformation_add_dead_block.cpp
         transformation_add_dead_break.cpp
         transformation_add_dead_continue.cpp
+        transformation_add_function.cpp
+        transformation_add_global_undef.cpp
+        transformation_add_global_variable.cpp
         transformation_add_no_contraction_decoration.cpp
+        transformation_add_type_array.cpp
         transformation_add_type_boolean.cpp
         transformation_add_type_float.cpp
+        transformation_add_type_function.cpp
         transformation_add_type_int.cpp
+        transformation_add_type_matrix.cpp
         transformation_add_type_pointer.cpp
+        transformation_add_type_struct.cpp
+        transformation_add_type_vector.cpp
         transformation_composite_construct.cpp
         transformation_composite_extract.cpp
         transformation_copy_object.cpp
+        transformation_merge_blocks.cpp
         transformation_move_block_down.cpp
+        transformation_outline_function.cpp
         transformation_replace_boolean_constant_with_constant_binary.cpp
         transformation_replace_constant_with_uniform.cpp
         transformation_replace_id_with_synonym.cpp

+ 131 - 1
3rdparty/spirv-tools/source/fuzz/fact_manager.cpp

@@ -800,9 +800,102 @@ bool FactManager::DataSynonymFacts::IsSynonymous(
 // End of data synonym facts
 //==============================
 
+//==============================
+// Dead block facts
+
+// The purpose of this class is to group the fields and data used to represent
+// facts about data blocks.
+class FactManager::DeadBlockFacts {
+ public:
+  // See method in FactManager which delegates to this method.
+  void AddFact(const protobufs::FactBlockIsDead& fact);
+
+  // See method in FactManager which delegates to this method.
+  bool BlockIsDead(uint32_t block_id) const;
+
+ private:
+  std::set<uint32_t> dead_block_ids_;
+};
+
+void FactManager::DeadBlockFacts::AddFact(
+    const protobufs::FactBlockIsDead& fact) {
+  dead_block_ids_.insert(fact.block_id());
+}
+
+bool FactManager::DeadBlockFacts::BlockIsDead(uint32_t block_id) const {
+  return dead_block_ids_.count(block_id) != 0;
+}
+
+// End of dead block facts
+//==============================
+
+//==============================
+// Livesafe function facts
+
+// The purpose of this class is to group the fields and data used to represent
+// facts about livesafe functions.
+class FactManager::LivesafeFunctionFacts {
+ public:
+  // See method in FactManager which delegates to this method.
+  void AddFact(const protobufs::FactFunctionIsLivesafe& fact);
+
+  // See method in FactManager which delegates to this method.
+  bool FunctionIsLivesafe(uint32_t function_id) const;
+
+ private:
+  std::set<uint32_t> livesafe_function_ids_;
+};
+
+void FactManager::LivesafeFunctionFacts::AddFact(
+    const protobufs::FactFunctionIsLivesafe& fact) {
+  livesafe_function_ids_.insert(fact.function_id());
+}
+
+bool FactManager::LivesafeFunctionFacts::FunctionIsLivesafe(
+    uint32_t function_id) const {
+  return livesafe_function_ids_.count(function_id) != 0;
+}
+
+// End of livesafe function facts
+//==============================
+
+//==============================
+// Arbitrarily-valued variable facts
+
+// The purpose of this class is to group the fields and data used to represent
+// facts about livesafe functions.
+class FactManager::ArbitrarilyValuedVaribleFacts {
+ public:
+  // See method in FactManager which delegates to this method.
+  void AddFact(const protobufs::FactValueOfVariableIsArbitrary& fact);
+
+  // See method in FactManager which delegates to this method.
+  bool VariableValueIsArbitrary(uint32_t variable_id) const;
+
+ private:
+  std::set<uint32_t> arbitrary_valued_varible_ids_;
+};
+
+void FactManager::ArbitrarilyValuedVaribleFacts::AddFact(
+    const protobufs::FactValueOfVariableIsArbitrary& fact) {
+  arbitrary_valued_varible_ids_.insert(fact.variable_id());
+}
+
+bool FactManager::ArbitrarilyValuedVaribleFacts::VariableValueIsArbitrary(
+    uint32_t variable_id) const {
+  return arbitrary_valued_varible_ids_.count(variable_id) != 0;
+}
+
+// End of arbitrarily-valued variable facts
+//==============================
+
 FactManager::FactManager()
     : uniform_constant_facts_(MakeUnique<ConstantUniformFacts>()),
-      data_synonym_facts_(MakeUnique<DataSynonymFacts>()) {}
+      data_synonym_facts_(MakeUnique<DataSynonymFacts>()),
+      dead_block_facts_(MakeUnique<DeadBlockFacts>()),
+      livesafe_function_facts_(MakeUnique<LivesafeFunctionFacts>()),
+      arbitrarily_valued_variable_facts_(
+          MakeUnique<ArbitrarilyValuedVaribleFacts>()) {}
 
 FactManager::~FactManager() = default;
 
@@ -827,6 +920,12 @@ bool FactManager::AddFact(const fuzz::protobufs::Fact& fact,
     case protobufs::Fact::kDataSynonymFact:
       data_synonym_facts_->AddFact(fact.data_synonym_fact(), context);
       return true;
+    case protobufs::Fact::kBlockIsDeadFact:
+      dead_block_facts_->AddFact(fact.block_is_dead_fact());
+      return true;
+    case protobufs::Fact::kFunctionIsLivesafeFact:
+      livesafe_function_facts_->AddFact(fact.function_is_livesafe_fact());
+      return true;
     default:
       assert(false && "Unknown fact type.");
       return false;
@@ -898,5 +997,36 @@ bool FactManager::IsSynonymous(
                                            context);
 }
 
+bool FactManager::BlockIsDead(uint32_t block_id) const {
+  return dead_block_facts_->BlockIsDead(block_id);
+}
+
+void FactManager::AddFactBlockIsDead(uint32_t block_id) {
+  protobufs::FactBlockIsDead fact;
+  fact.set_block_id(block_id);
+  dead_block_facts_->AddFact(fact);
+}
+
+bool FactManager::FunctionIsLivesafe(uint32_t function_id) const {
+  return livesafe_function_facts_->FunctionIsLivesafe(function_id);
+}
+
+void FactManager::AddFactFunctionIsLivesafe(uint32_t function_id) {
+  protobufs::FactFunctionIsLivesafe fact;
+  fact.set_function_id(function_id);
+  livesafe_function_facts_->AddFact(fact);
+}
+
+bool FactManager::VariableValueIsArbitrary(uint32_t variable_id) const {
+  return arbitrarily_valued_variable_facts_->VariableValueIsArbitrary(
+      variable_id);
+}
+
+void FactManager::AddFactValueOfVariableIsArbitrary(uint32_t variable_id) {
+  protobufs::FactValueOfVariableIsArbitrary fact;
+  fact.set_variable_id(variable_id);
+  arbitrarily_valued_variable_facts_->AddFact(fact);
+}
+
 }  // namespace fuzz
 }  // namespace spvtools

+ 53 - 0
3rdparty/spirv-tools/source/fuzz/fact_manager.h

@@ -58,6 +58,16 @@ class FactManager {
                           const protobufs::DataDescriptor& data2,
                           opt::IRContext* context);
 
+  // Records the fact that |block_id| is dead.
+  void AddFactBlockIsDead(uint32_t block_id);
+
+  // Records the fact that |function_id| is livesafe.
+  void AddFactFunctionIsLivesafe(uint32_t function_id);
+
+  // Records the fact that |variable_id| has an arbitrary value and can thus be
+  // stored to without affecting the module's behaviour.
+  void AddFactValueOfVariableIsArbitrary(uint32_t variable_id);
+
   // The fact manager is responsible for managing a few distinct categories of
   // facts. In principle there could be different fact managers for each kind
   // of fact, but in practice providing one 'go to' place for facts is
@@ -130,6 +140,35 @@ class FactManager {
   // End of id synonym facts
   //==============================
 
+  //==============================
+  // Querying facts about dead blocks
+
+  // Returns true if and ony if |block_id| is the id of a block known to be
+  // dynamically unreachable.
+  bool BlockIsDead(uint32_t block_id) const;
+
+  // End of dead block facts
+  //==============================
+
+  //==============================
+  // Querying facts about livesafe function
+
+  // Returns true if and ony if |function_id| is the id of a function known
+  // to be livesafe.
+  bool FunctionIsLivesafe(uint32_t function_id) const;
+
+  // End of dead livesafe function facts
+  //==============================
+
+  //==============================
+  // Querying facts about arbitrarily-valued variables
+
+  // Returns true if and ony if |variable_id| is arbitrarily-valued.
+  bool VariableValueIsArbitrary(uint32_t variable_id) const;
+
+  // End of arbitrarily-valued variable facts
+  //==============================
+
  private:
   // For each distinct kind of fact to be managed, we use a separate opaque
   // class type.
@@ -142,6 +181,20 @@ class FactManager {
   class DataSynonymFacts;  // Opaque class for management of data synonym facts.
   std::unique_ptr<DataSynonymFacts>
       data_synonym_facts_;  // Unique pointer to internal data.
+
+  class DeadBlockFacts;  // Opaque class for management of dead block facts.
+  std::unique_ptr<DeadBlockFacts>
+      dead_block_facts_;  // Unique pointer to internal data.
+
+  class LivesafeFunctionFacts;  // Opaque class for management of livesafe
+                                // function facts.
+  std::unique_ptr<LivesafeFunctionFacts>
+      livesafe_function_facts_;  // Unique pointer to internal data.
+
+  class ArbitrarilyValuedVaribleFacts;  // Opaque class for management of
+  // facts about variables whose values should be expected to be arbitrary.
+  std::unique_ptr<ArbitrarilyValuedVaribleFacts>
+      arbitrarily_valued_variable_facts_;  // Unique pointer to internal data.
 };
 
 }  // namespace fuzz

+ 30 - 3
3rdparty/spirv-tools/source/fuzz/fuzzer.cpp

@@ -21,6 +21,8 @@
 #include "fuzzer_pass_adjust_memory_operands_masks.h"
 #include "source/fuzz/fact_manager.h"
 #include "source/fuzz/fuzzer_context.h"
+#include "source/fuzz/fuzzer_pass_add_composite_types.h"
+#include "source/fuzz/fuzzer_pass_add_dead_blocks.h"
 #include "source/fuzz/fuzzer_pass_add_dead_breaks.h"
 #include "source/fuzz/fuzzer_pass_add_dead_continues.h"
 #include "source/fuzz/fuzzer_pass_add_no_contraction_decorations.h"
@@ -31,7 +33,10 @@
 #include "source/fuzz/fuzzer_pass_apply_id_synonyms.h"
 #include "source/fuzz/fuzzer_pass_construct_composites.h"
 #include "source/fuzz/fuzzer_pass_copy_objects.h"
+#include "source/fuzz/fuzzer_pass_donate_modules.h"
+#include "source/fuzz/fuzzer_pass_merge_blocks.h"
 #include "source/fuzz/fuzzer_pass_obfuscate_constants.h"
+#include "source/fuzz/fuzzer_pass_outline_functions.h"
 #include "source/fuzz/fuzzer_pass_permute_blocks.h"
 #include "source/fuzz/fuzzer_pass_split_blocks.h"
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
@@ -50,15 +55,21 @@ const uint32_t kTransformationLimit = 500;
 
 const uint32_t kChanceOfApplyingAnotherPass = 85;
 
-template <typename T>
+// A convenience method to add a fuzzer pass to |passes| with probability 0.5.
+// All fuzzer passes take |ir_context|, |fact_manager|, |fuzzer_context| and
+// |transformation_sequence_out| as parameters.  Extra arguments can be provided
+// via |extra_args|.
+template <typename T, typename... Args>
 void MaybeAddPass(
     std::vector<std::unique_ptr<FuzzerPass>>* passes,
     opt::IRContext* ir_context, FactManager* fact_manager,
     FuzzerContext* fuzzer_context,
-    protobufs::TransformationSequence* transformation_sequence_out) {
+    protobufs::TransformationSequence* transformation_sequence_out,
+    Args&&... extra_args) {
   if (fuzzer_context->ChooseEven()) {
     passes->push_back(MakeUnique<T>(ir_context, fact_manager, fuzzer_context,
-                                    transformation_sequence_out));
+                                    transformation_sequence_out,
+                                    std::forward<Args>(extra_args)...));
   }
 }
 
@@ -112,6 +123,7 @@ bool Fuzzer::Impl::ApplyPassAndCheckValidity(
 Fuzzer::FuzzerResultStatus Fuzzer::Run(
     const std::vector<uint32_t>& binary_in,
     const protobufs::FactSequence& initial_facts,
+    const std::vector<fuzzerutil::ModuleSupplier>& donor_suppliers,
     std::vector<uint32_t>* binary_out,
     protobufs::TransformationSequence* transformation_sequence_out) const {
   // Check compatibility between the library version being linked with and the
@@ -167,6 +179,12 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
   // Apply some semantics-preserving passes.
   std::vector<std::unique_ptr<FuzzerPass>> passes;
   while (passes.empty()) {
+    MaybeAddPass<FuzzerPassAddCompositeTypes>(&passes, ir_context.get(),
+                                              &fact_manager, &fuzzer_context,
+                                              transformation_sequence_out);
+    MaybeAddPass<FuzzerPassAddDeadBlocks>(&passes, ir_context.get(),
+                                          &fact_manager, &fuzzer_context,
+                                          transformation_sequence_out);
     MaybeAddPass<FuzzerPassAddDeadBreaks>(&passes, ir_context.get(),
                                           &fact_manager, &fuzzer_context,
                                           transformation_sequence_out);
@@ -182,9 +200,18 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
     MaybeAddPass<FuzzerPassCopyObjects>(&passes, ir_context.get(),
                                         &fact_manager, &fuzzer_context,
                                         transformation_sequence_out);
+    MaybeAddPass<FuzzerPassDonateModules>(
+        &passes, ir_context.get(), &fact_manager, &fuzzer_context,
+        transformation_sequence_out, donor_suppliers);
+    MaybeAddPass<FuzzerPassMergeBlocks>(&passes, ir_context.get(),
+                                        &fact_manager, &fuzzer_context,
+                                        transformation_sequence_out);
     MaybeAddPass<FuzzerPassObfuscateConstants>(&passes, ir_context.get(),
                                                &fact_manager, &fuzzer_context,
                                                transformation_sequence_out);
+    MaybeAddPass<FuzzerPassOutlineFunctions>(&passes, ir_context.get(),
+                                             &fact_manager, &fuzzer_context,
+                                             transformation_sequence_out);
     MaybeAddPass<FuzzerPassPermuteBlocks>(&passes, ir_context.get(),
                                           &fact_manager, &fuzzer_context,
                                           transformation_sequence_out);

+ 6 - 2
3rdparty/spirv-tools/source/fuzz/fuzzer.h

@@ -18,6 +18,7 @@
 #include <memory>
 #include <vector>
 
+#include "source/fuzz/fuzzer_util.h"
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
 #include "spirv-tools/libspirv.hpp"
 
@@ -57,11 +58,14 @@ class Fuzzer {
 
   // Transforms |binary_in| to |binary_out| by running a number of randomized
   // fuzzer passes.  Initial facts about the input binary and the context in
-  // which it will execute are provided via |initial_facts|.  The transformation
-  // sequence that was applied is returned via |transformation_sequence_out|.
+  // which it will execute are provided via |initial_facts|.  A source of donor
+  // modules to be used by transformations is provided via |donor_suppliers|.
+  // The transformation sequence that was applied is returned via
+  // |transformation_sequence_out|.
   FuzzerResultStatus Run(
       const std::vector<uint32_t>& binary_in,
       const protobufs::FactSequence& initial_facts,
+      const std::vector<fuzzerutil::ModuleSupplier>& donor_suppliers,
       std::vector<uint32_t>* binary_out,
       protobufs::TransformationSequence* transformation_sequence_out) const;
 

+ 36 - 1
3rdparty/spirv-tools/source/fuzz/fuzzer_context.cpp

@@ -23,10 +23,16 @@ namespace {
 // Default <minimum, maximum> pairs of probabilities for applying various
 // transformations. All values are percentages. Keep them in alphabetical order.
 
+const std::pair<uint32_t, uint32_t> kChanceOfAddingAnotherStructField = {20,
+                                                                         90};
+const std::pair<uint32_t, uint32_t> kChanceOfAddingArrayOrStructType = {20, 90};
+const std::pair<uint32_t, uint32_t> kChanceOfAddingDeadBlock = {20, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfAddingDeadBreak = {5, 80};
 const std::pair<uint32_t, uint32_t> kChanceOfAddingDeadContinue = {5, 80};
+const std::pair<uint32_t, uint32_t> kChanceOfAddingMatrixType = {20, 70};
 const std::pair<uint32_t, uint32_t> kChanceOfAddingNoContractionDecoration = {
     5, 70};
+const std::pair<uint32_t, uint32_t> kChanceOfAddingVectorType = {20, 70};
 const std::pair<uint32_t, uint32_t> kChanceOfAdjustingFunctionControl = {20,
                                                                          70};
 const std::pair<uint32_t, uint32_t> kChanceOfAdjustingLoopControl = {20, 90};
@@ -34,10 +40,16 @@ const std::pair<uint32_t, uint32_t> kChanceOfAdjustingMemoryOperandsMask = {20,
                                                                             90};
 const std::pair<uint32_t, uint32_t> kChanceOfAdjustingSelectionControl = {20,
                                                                           90};
-const std::pair<uint32_t, uint32_t> kChanceOfCopyingObject = {20, 50};
+const std::pair<uint32_t, uint32_t> kChanceOfChoosingStructTypeVsArrayType = {
+    20, 80};
 const std::pair<uint32_t, uint32_t> kChanceOfConstructingComposite = {20, 50};
+const std::pair<uint32_t, uint32_t> kChanceOfCopyingObject = {20, 50};
+const std::pair<uint32_t, uint32_t> kChanceOfDonatingAdditionalModule = {5, 50};
+const std::pair<uint32_t, uint32_t> kChanceOfMakingDonorLivesafe = {40, 60};
+const std::pair<uint32_t, uint32_t> kChanceOfMergingBlocks = {20, 95};
 const std::pair<uint32_t, uint32_t> kChanceOfMovingBlockDown = {20, 50};
 const std::pair<uint32_t, uint32_t> kChanceOfObfuscatingConstant = {10, 90};
+const std::pair<uint32_t, uint32_t> kChanceOfOutliningFunction = {10, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfReplacingIdWithSynonym = {10, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfSplittingBlock = {40, 95};
 
@@ -45,6 +57,8 @@ const std::pair<uint32_t, uint32_t> kChanceOfSplittingBlock = {40, 95};
 // Keep them in alphabetical order.
 const uint32_t kDefaultMaxLoopControlPartialCount = 100;
 const uint32_t kDefaultMaxLoopControlPeelCount = 100;
+const uint32_t kDefaultMaxLoopLimit = 20;
+const uint32_t kDefaultMaxNewArraySizeLimit = 100;
 
 // Default functions for controlling how deep to go during recursive
 // generation/transformation. Keep them in alphabetical order.
@@ -64,12 +78,22 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
       next_fresh_id_(min_fresh_id),
       go_deeper_in_constant_obfuscation_(
           kDefaultGoDeeperInConstantObfuscation) {
+  chance_of_adding_another_struct_field_ =
+      ChooseBetweenMinAndMax(kChanceOfAddingAnotherStructField);
+  chance_of_adding_array_or_struct_type_ =
+      ChooseBetweenMinAndMax(kChanceOfAddingArrayOrStructType);
+  chance_of_adding_dead_block_ =
+      ChooseBetweenMinAndMax(kChanceOfAddingDeadBlock);
   chance_of_adding_dead_break_ =
       ChooseBetweenMinAndMax(kChanceOfAddingDeadBreak);
   chance_of_adding_dead_continue_ =
       ChooseBetweenMinAndMax(kChanceOfAddingDeadContinue);
+  chance_of_adding_matrix_type_ =
+      ChooseBetweenMinAndMax(kChanceOfAddingMatrixType);
   chance_of_adding_no_contraction_decoration_ =
       ChooseBetweenMinAndMax(kChanceOfAddingNoContractionDecoration);
+  chance_of_adding_vector_type_ =
+      ChooseBetweenMinAndMax(kChanceOfAddingVectorType);
   chance_of_adjusting_function_control_ =
       ChooseBetweenMinAndMax(kChanceOfAdjustingFunctionControl);
   chance_of_adjusting_loop_control_ =
@@ -78,18 +102,29 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
       ChooseBetweenMinAndMax(kChanceOfAdjustingMemoryOperandsMask);
   chance_of_adjusting_selection_control_ =
       ChooseBetweenMinAndMax(kChanceOfAdjustingSelectionControl);
+  chance_of_choosing_struct_type_vs_array_type_ =
+      ChooseBetweenMinAndMax(kChanceOfChoosingStructTypeVsArrayType);
   chance_of_constructing_composite_ =
       ChooseBetweenMinAndMax(kChanceOfConstructingComposite);
   chance_of_copying_object_ = ChooseBetweenMinAndMax(kChanceOfCopyingObject);
+  chance_of_donating_additional_module_ =
+      ChooseBetweenMinAndMax(kChanceOfDonatingAdditionalModule);
+  chance_of_making_donor_livesafe_ =
+      ChooseBetweenMinAndMax(kChanceOfMakingDonorLivesafe);
+  chance_of_merging_blocks_ = ChooseBetweenMinAndMax(kChanceOfMergingBlocks);
   chance_of_moving_block_down_ =
       ChooseBetweenMinAndMax(kChanceOfMovingBlockDown);
   chance_of_obfuscating_constant_ =
       ChooseBetweenMinAndMax(kChanceOfObfuscatingConstant);
+  chance_of_outlining_function_ =
+      ChooseBetweenMinAndMax(kChanceOfOutliningFunction);
   chance_of_replacing_id_with_synonym_ =
       ChooseBetweenMinAndMax(kChanceOfReplacingIdWithSynonym);
   chance_of_splitting_block_ = ChooseBetweenMinAndMax(kChanceOfSplittingBlock);
   max_loop_control_partial_count_ = kDefaultMaxLoopControlPartialCount;
   max_loop_control_peel_count_ = kDefaultMaxLoopControlPeelCount;
+  max_loop_limit_ = kDefaultMaxLoopLimit;
+  max_new_array_size_limit_ = kDefaultMaxNewArraySizeLimit;
 }
 
 FuzzerContext::~FuzzerContext() = default;

+ 45 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_context.h

@@ -58,13 +58,26 @@ class FuzzerContext {
 
   // Probabilities associated with applying various transformations.
   // Keep them in alphabetical order.
+  uint32_t GetChanceOfAddingAnotherStructField() {
+    return chance_of_adding_another_struct_field_;
+  }
+  uint32_t GetChanceOfAddingArrayOrStructType() {
+    return chance_of_adding_array_or_struct_type_;
+  }
+  uint32_t GetChanceOfAddingDeadBlock() { return chance_of_adding_dead_block_; }
   uint32_t GetChanceOfAddingDeadBreak() { return chance_of_adding_dead_break_; }
   uint32_t GetChanceOfAddingDeadContinue() {
     return chance_of_adding_dead_continue_;
   }
+  uint32_t GetChanceOfAddingMatrixType() {
+    return chance_of_adding_matrix_type_;
+  }
   uint32_t GetChanceOfAddingNoContractionDecoration() {
     return chance_of_adding_no_contraction_decoration_;
   }
+  uint32_t GetChanceOfAddingVectorType() {
+    return chance_of_adding_vector_type_;
+  }
   uint32_t GetChanceOfAdjustingFunctionControl() {
     return chance_of_adjusting_function_control_;
   }
@@ -77,14 +90,27 @@ class FuzzerContext {
   uint32_t GetChanceOfAdjustingSelectionControl() {
     return chance_of_adjusting_selection_control_;
   }
+  uint32_t GetChanceOfChoosingStructTypeVsArrayType() {
+    return chance_of_choosing_struct_type_vs_array_type_;
+  }
   uint32_t GetChanceOfConstructingComposite() {
     return chance_of_constructing_composite_;
   }
   uint32_t GetChanceOfCopyingObject() { return chance_of_copying_object_; }
+  uint32_t GetChanceOfDonatingAdditionalModule() {
+    return chance_of_donating_additional_module_;
+  }
+  uint32_t ChanceOfMakingDonorLivesafe() {
+    return chance_of_making_donor_livesafe_;
+  }
+  uint32_t GetChanceOfMergingBlocks() { return chance_of_merging_blocks_; }
   uint32_t GetChanceOfMovingBlockDown() { return chance_of_moving_block_down_; }
   uint32_t GetChanceOfObfuscatingConstant() {
     return chance_of_obfuscating_constant_;
   }
+  uint32_t GetChanceOfOutliningFunction() {
+    return chance_of_outlining_function_;
+  }
   uint32_t GetChanceOfReplacingIdWithSynonym() {
     return chance_of_replacing_id_with_synonym_;
   }
@@ -95,6 +121,13 @@ class FuzzerContext {
   uint32_t GetRandomLoopControlPartialCount() {
     return random_generator_->RandomUint32(max_loop_control_partial_count_);
   }
+  uint32_t GetRandomLoopLimit() {
+    return random_generator_->RandomUint32(max_loop_limit_);
+  }
+  uint32_t GetRandomSizeForNewArray() {
+    // Ensure that the array size is non-zero.
+    return random_generator_->RandomUint32(max_new_array_size_limit_ - 1) + 1;
+  }
 
   // Functions to control how deeply to recurse.
   // Keep them in alphabetical order.
@@ -110,17 +143,27 @@ class FuzzerContext {
 
   // Probabilities associated with applying various transformations.
   // Keep them in alphabetical order.
+  uint32_t chance_of_adding_another_struct_field_;
+  uint32_t chance_of_adding_array_or_struct_type_;
+  uint32_t chance_of_adding_dead_block_;
   uint32_t chance_of_adding_dead_break_;
   uint32_t chance_of_adding_dead_continue_;
+  uint32_t chance_of_adding_matrix_type_;
   uint32_t chance_of_adding_no_contraction_decoration_;
+  uint32_t chance_of_adding_vector_type_;
   uint32_t chance_of_adjusting_function_control_;
   uint32_t chance_of_adjusting_loop_control_;
   uint32_t chance_of_adjusting_memory_operands_mask_;
   uint32_t chance_of_adjusting_selection_control_;
+  uint32_t chance_of_choosing_struct_type_vs_array_type_;
   uint32_t chance_of_constructing_composite_;
   uint32_t chance_of_copying_object_;
+  uint32_t chance_of_donating_additional_module_;
+  uint32_t chance_of_making_donor_livesafe_;
+  uint32_t chance_of_merging_blocks_;
   uint32_t chance_of_moving_block_down_;
   uint32_t chance_of_obfuscating_constant_;
+  uint32_t chance_of_outlining_function_;
   uint32_t chance_of_replacing_id_with_synonym_;
   uint32_t chance_of_splitting_block_;
 
@@ -129,6 +172,8 @@ class FuzzerContext {
   // Keep them in alphabetical order.
   uint32_t max_loop_control_partial_count_;
   uint32_t max_loop_control_peel_count_;
+  uint32_t max_loop_limit_;
+  uint32_t max_new_array_size_limit_;
 
   // Functions to determine with what probability to go deeper when generating
   // or mutating constructs recursively.

+ 126 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_pass.cpp

@@ -15,6 +15,14 @@
 #include "source/fuzz/fuzzer_pass.h"
 
 #include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_add_constant_scalar.h"
+#include "source/fuzz/transformation_add_global_undef.h"
+#include "source/fuzz/transformation_add_type_boolean.h"
+#include "source/fuzz/transformation_add_type_float.h"
+#include "source/fuzz/transformation_add_type_int.h"
+#include "source/fuzz/transformation_add_type_matrix.h"
+#include "source/fuzz/transformation_add_type_pointer.h"
+#include "source/fuzz/transformation_add_type_vector.h"
 
 namespace spvtools {
 namespace fuzz {
@@ -128,5 +136,123 @@ void FuzzerPass::MaybeAddTransformationBeforeEachInstruction(
   }
 }
 
+uint32_t FuzzerPass::FindOrCreateBoolType() {
+  opt::analysis::Bool bool_type;
+  auto existing_id = GetIRContext()->get_type_mgr()->GetId(&bool_type);
+  if (existing_id) {
+    return existing_id;
+  }
+  auto result = GetFuzzerContext()->GetFreshId();
+  ApplyTransformation(TransformationAddTypeBoolean(result));
+  return result;
+}
+
+uint32_t FuzzerPass::FindOrCreate32BitIntegerType(bool is_signed) {
+  opt::analysis::Integer int_type(32, is_signed);
+  auto existing_id = GetIRContext()->get_type_mgr()->GetId(&int_type);
+  if (existing_id) {
+    return existing_id;
+  }
+  auto result = GetFuzzerContext()->GetFreshId();
+  ApplyTransformation(TransformationAddTypeInt(result, 32, is_signed));
+  return result;
+}
+
+uint32_t FuzzerPass::FindOrCreate32BitFloatType() {
+  opt::analysis::Float float_type(32);
+  auto existing_id = GetIRContext()->get_type_mgr()->GetId(&float_type);
+  if (existing_id) {
+    return existing_id;
+  }
+  auto result = GetFuzzerContext()->GetFreshId();
+  ApplyTransformation(TransformationAddTypeFloat(result, 32));
+  return result;
+}
+
+uint32_t FuzzerPass::FindOrCreateVectorType(uint32_t component_type_id,
+                                            uint32_t component_count) {
+  assert(component_count >= 2 && component_count <= 4 &&
+         "Precondition: component count must be in range [2, 4].");
+  opt::analysis::Type* component_type =
+      GetIRContext()->get_type_mgr()->GetType(component_type_id);
+  assert(component_type && "Precondition: the component type must exist.");
+  opt::analysis::Vector vector_type(component_type, component_count);
+  auto existing_id = GetIRContext()->get_type_mgr()->GetId(&vector_type);
+  if (existing_id) {
+    return existing_id;
+  }
+  auto result = GetFuzzerContext()->GetFreshId();
+  ApplyTransformation(
+      TransformationAddTypeVector(result, component_type_id, component_count));
+  return result;
+}
+
+uint32_t FuzzerPass::FindOrCreateMatrixType(uint32_t column_count,
+                                            uint32_t row_count) {
+  assert(column_count >= 2 && column_count <= 4 &&
+         "Precondition: column count must be in range [2, 4].");
+  assert(row_count >= 2 && row_count <= 4 &&
+         "Precondition: row count must be in range [2, 4].");
+  uint32_t column_type_id =
+      FindOrCreateVectorType(FindOrCreate32BitFloatType(), row_count);
+  opt::analysis::Type* column_type =
+      GetIRContext()->get_type_mgr()->GetType(column_type_id);
+  opt::analysis::Matrix matrix_type(column_type, column_count);
+  auto existing_id = GetIRContext()->get_type_mgr()->GetId(&matrix_type);
+  if (existing_id) {
+    return existing_id;
+  }
+  auto result = GetFuzzerContext()->GetFreshId();
+  ApplyTransformation(
+      TransformationAddTypeMatrix(result, column_type_id, column_count));
+  return result;
+}
+
+uint32_t FuzzerPass::FindOrCreatePointerTo32BitIntegerType(
+    bool is_signed, SpvStorageClass storage_class) {
+  auto uint32_type_id = FindOrCreate32BitIntegerType(is_signed);
+  opt::analysis::Pointer pointer_type(
+      GetIRContext()->get_type_mgr()->GetType(uint32_type_id), storage_class);
+  auto existing_id = GetIRContext()->get_type_mgr()->GetId(&pointer_type);
+  if (existing_id) {
+    return existing_id;
+  }
+  auto result = GetFuzzerContext()->GetFreshId();
+  ApplyTransformation(
+      TransformationAddTypePointer(result, storage_class, uint32_type_id));
+  return result;
+}
+
+uint32_t FuzzerPass::FindOrCreate32BitIntegerConstant(uint32_t word,
+                                                      bool is_signed) {
+  auto uint32_type_id = FindOrCreate32BitIntegerType(is_signed);
+  opt::analysis::IntConstant int_constant(
+      GetIRContext()->get_type_mgr()->GetType(uint32_type_id)->AsInteger(),
+      {word});
+  auto existing_constant =
+      GetIRContext()->get_constant_mgr()->FindConstant(&int_constant);
+  if (existing_constant) {
+    return GetIRContext()
+        ->get_constant_mgr()
+        ->GetDefiningInstruction(existing_constant)
+        ->result_id();
+  }
+  auto result = GetFuzzerContext()->GetFreshId();
+  ApplyTransformation(
+      TransformationAddConstantScalar(result, uint32_type_id, {word}));
+  return result;
+}
+
+uint32_t FuzzerPass::FindOrCreateGlobalUndef(uint32_t type_id) {
+  for (auto& inst : GetIRContext()->types_values()) {
+    if (inst.opcode() == SpvOpUndef && inst.type_id() == type_id) {
+      return inst.result_id();
+    }
+  }
+  auto result = GetFuzzerContext()->GetFreshId();
+  ApplyTransformation(TransformationAddGlobalUndef(result, type_id));
+  return result;
+}
+
 }  // namespace fuzz
 }  // namespace spvtools

+ 54 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_pass.h

@@ -89,6 +89,60 @@ class FuzzerPass {
                const protobufs::InstructionDescriptor& instruction_descriptor)>
           maybe_apply_transformation);
 
+  // A generic helper for applying a transformation that should be applicable
+  // by construction, and adding it to the sequence of applied transformations.
+  template <typename TransformationType>
+  void ApplyTransformation(const TransformationType& transformation) {
+    assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()) &&
+           "Transformation should be applicable by construction.");
+    transformation.Apply(GetIRContext(), GetFactManager());
+    *GetTransformations()->add_transformation() = transformation.ToMessage();
+  }
+
+  // Returns the id of an OpTypeBool instruction.  If such an instruction does
+  // not exist, a transformation is applied to add it.
+  uint32_t FindOrCreateBoolType();
+
+  // Returns the id of an OpTypeInt instruction, with width 32 and signedness
+  // specified by |is_signed|.  If such an instruction does not exist, a
+  // transformation is applied to add it.
+  uint32_t FindOrCreate32BitIntegerType(bool is_signed);
+
+  // Returns the id of an OpTypeFloat instruction, with width 32.  If such an
+  // instruction does not exist, a transformation is applied to add it.
+  uint32_t FindOrCreate32BitFloatType();
+
+  // Returns the id of an OpTypeVector instruction, with |component_type_id|
+  // (which must already exist) as its base type, and |component_count|
+  // elements (which must be in the range [2, 4]).  If such an instruction does
+  // not exist, a transformation is applied to add it.
+  uint32_t FindOrCreateVectorType(uint32_t component_type_id,
+                                  uint32_t component_count);
+
+  // Returns the id of an OpTypeMatrix instruction, with |column_count| columns
+  // and |row_count| rows (each of which must be in the range [2, 4]).  If the
+  // float and vector types required to build this matrix type or the matrix
+  // type itself do not exist, transformations are applied to add them.
+  uint32_t FindOrCreateMatrixType(uint32_t column_count, uint32_t row_count);
+
+  // Returns the id of an OpTypePointer instruction, with a 32-bit integer base
+  // type of signedness specified by |is_signed|.  If the pointer type or
+  // required integer base type do not exist, transformations are applied to add
+  // them.
+  uint32_t FindOrCreatePointerTo32BitIntegerType(bool is_signed,
+                                                 SpvStorageClass storage_class);
+
+  // Returns the id of an OpConstant instruction, with 32-bit integer type of
+  // signedness specified by |is_signed|, with |word| as its value.  If either
+  // the required integer type or the constant do not exist, transformations are
+  // applied to add them.
+  uint32_t FindOrCreate32BitIntegerConstant(uint32_t word, bool is_signed);
+
+  // Returns the result id of an instruction of the form:
+  //   %id = OpUndef %|type_id|
+  // If no such instruction exists, a transformation is applied to add it.
+  uint32_t FindOrCreateGlobalUndef(uint32_t type_id);
+
  private:
   opt::IRContext* ir_context_;
   FactManager* fact_manager_;

+ 138 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_composite_types.cpp

@@ -0,0 +1,138 @@
+// Copyright (c) 2020 Google LLC
+//
+// 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.
+
+#include "source/fuzz/fuzzer_pass_add_composite_types.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/transformation_add_type_array.h"
+#include "source/fuzz/transformation_add_type_struct.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassAddCompositeTypes::FuzzerPassAddCompositeTypes(
+    opt::IRContext* ir_context, FactManager* fact_manager,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+
+FuzzerPassAddCompositeTypes::~FuzzerPassAddCompositeTypes() = default;
+
+void FuzzerPassAddCompositeTypes::Apply() {
+  MaybeAddMissingVectorTypes();
+  MaybeAddMissingMatrixTypes();
+
+  // Randomly interleave between adding struct and array composite types
+  while (GetFuzzerContext()->ChoosePercentage(
+      GetFuzzerContext()->GetChanceOfAddingArrayOrStructType())) {
+    if (GetFuzzerContext()->ChoosePercentage(
+            GetFuzzerContext()->GetChanceOfChoosingStructTypeVsArrayType())) {
+      AddNewStructType();
+    } else {
+      AddNewArrayType();
+    }
+  }
+}
+
+void FuzzerPassAddCompositeTypes::MaybeAddMissingVectorTypes() {
+  // Functions to lazily supply scalar base types on demand if we decide to
+  // create vectors with the relevant base types.
+  std::function<uint32_t()> bool_type_supplier = [this]() -> uint32_t {
+    return FindOrCreateBoolType();
+  };
+  std::function<uint32_t()> float_type_supplier = [this]() -> uint32_t {
+    return FindOrCreate32BitFloatType();
+  };
+  std::function<uint32_t()> int_type_supplier = [this]() -> uint32_t {
+    return FindOrCreate32BitIntegerType(true);
+  };
+  std::function<uint32_t()> uint_type_supplier = [this]() -> uint32_t {
+    return FindOrCreate32BitIntegerType(false);
+  };
+
+  // Consider each of the base types with which we can make vectors.
+  for (auto& base_type_supplier : {bool_type_supplier, float_type_supplier,
+                                   int_type_supplier, uint_type_supplier}) {
+    // Consider each valid vector size.
+    for (uint32_t size = 2; size <= 4; size++) {
+      // Randomly decide whether to create (if it does not already exist) a
+      // vector with this size and base type.
+      if (GetFuzzerContext()->ChoosePercentage(
+              GetFuzzerContext()->GetChanceOfAddingVectorType())) {
+        FindOrCreateVectorType(base_type_supplier(), size);
+      }
+    }
+  }
+}
+
+void FuzzerPassAddCompositeTypes::MaybeAddMissingMatrixTypes() {
+  // Consider every valid matrix dimension.
+  for (uint32_t columns = 2; columns <= 4; columns++) {
+    for (uint32_t rows = 2; rows <= 4; rows++) {
+      // Randomly decide whether to create (if it does not already exist) a
+      // matrix with these dimensions.  As matrices can only have floating-point
+      // base type, we do not need to consider multiple base types as in the
+      // case for vectors.
+      if (GetFuzzerContext()->ChoosePercentage(
+              GetFuzzerContext()->GetChanceOfAddingMatrixType())) {
+        FindOrCreateMatrixType(columns, rows);
+      }
+    }
+  }
+}
+
+void FuzzerPassAddCompositeTypes::AddNewArrayType() {
+  ApplyTransformation(TransformationAddTypeArray(
+      GetFuzzerContext()->GetFreshId(), ChooseScalarOrCompositeType(),
+      FindOrCreate32BitIntegerConstant(
+          GetFuzzerContext()->GetRandomSizeForNewArray(), false)));
+}
+
+void FuzzerPassAddCompositeTypes::AddNewStructType() {
+  std::vector<uint32_t> field_type_ids;
+  do {
+    field_type_ids.push_back(ChooseScalarOrCompositeType());
+  } while (GetFuzzerContext()->ChoosePercentage(
+      GetFuzzerContext()->GetChanceOfAddingAnotherStructField()));
+  ApplyTransformation(TransformationAddTypeStruct(
+      GetFuzzerContext()->GetFreshId(), field_type_ids));
+}
+
+uint32_t FuzzerPassAddCompositeTypes::ChooseScalarOrCompositeType() {
+  // Gather up all the possibly-relevant types.
+  std::vector<uint32_t> candidates;
+  for (auto& inst : GetIRContext()->types_values()) {
+    switch (inst.opcode()) {
+      case SpvOpTypeArray:
+      case SpvOpTypeBool:
+      case SpvOpTypeFloat:
+      case SpvOpTypeInt:
+      case SpvOpTypeMatrix:
+      case SpvOpTypeStruct:
+      case SpvOpTypeVector:
+        candidates.push_back(inst.result_id());
+        break;
+      default:
+        break;
+    }
+  }
+  assert(!candidates.empty() &&
+         "This function should only be called if there is at least one scalar "
+         "or composite type available.");
+  // Return one of these types at random.
+  return candidates[GetFuzzerContext()->RandomIndex(candidates)];
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 61 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_composite_types.h

@@ -0,0 +1,61 @@
+// Copyright (c) 2020 Google LLC
+//
+// 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.
+
+#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_COMPOSITE_TYPES_H_
+#define SOURCE_FUZZ_FUZZER_PASS_ADD_COMPOSITE_TYPES_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Fuzzer pass that randomly adds missing vector and matrix types, and new
+// array and struct types, to the module.
+class FuzzerPassAddCompositeTypes : public FuzzerPass {
+ public:
+  FuzzerPassAddCompositeTypes(
+      opt::IRContext* ir_context, FactManager* fact_manager,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassAddCompositeTypes();
+
+  void Apply() override;
+
+ private:
+  // Creates an array of a random size with a random existing base type and adds
+  // it to the module.
+  void AddNewArrayType();
+
+  // Creates a struct with fields of random existing types and adds it to the
+  // module.
+  void AddNewStructType();
+
+  // For each vector type not already present in the module, randomly decides
+  // whether to add it to the module.
+  void MaybeAddMissingVectorTypes();
+
+  // For each matrix type not already present in the module, randomly decides
+  // whether to add it to the module.
+  void MaybeAddMissingMatrixTypes();
+
+  // Returns the id of a scalar or composite type declared in the module,
+  // chosen randomly.
+  uint32_t ChooseScalarOrCompositeType();
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_ADD_COMPOSITE_TYPES_H_

+ 64 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_dead_blocks.cpp

@@ -0,0 +1,64 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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.
+
+#include "source/fuzz/fuzzer_pass_add_dead_blocks.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/transformation_add_dead_block.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassAddDeadBlocks::FuzzerPassAddDeadBlocks(
+    opt::IRContext* ir_context, FactManager* fact_manager,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+
+FuzzerPassAddDeadBlocks::~FuzzerPassAddDeadBlocks() = default;
+
+void FuzzerPassAddDeadBlocks::Apply() {
+  // We iterate over all blocks in the module collecting up those at which we
+  // might add a branch to a new dead block.  We then loop over all such
+  // candidates and actually apply transformations.  This separation is to
+  // avoid modifying the module as we traverse it.
+  std::vector<TransformationAddDeadBlock> candidate_transformations;
+  for (auto& function : *GetIRContext()->module()) {
+    for (auto& block : function) {
+      if (!GetFuzzerContext()->ChoosePercentage(
+              GetFuzzerContext()->GetChanceOfAddingDeadBlock())) {
+        continue;
+      }
+      // We speculatively create a transformation, and then apply it (below) if
+      // it turns out to be applicable.  This avoids duplicating the logic for
+      // applicability checking.
+      //
+      // It means that fresh ids for transformations that turn out not to be
+      // applicable end up being unused.
+      candidate_transformations.emplace_back(TransformationAddDeadBlock(
+          GetFuzzerContext()->GetFreshId(), block.id(),
+          GetFuzzerContext()->ChooseEven()));
+    }
+  }
+  // Apply all those transformations that are in fact applicable.
+  for (auto& transformation : candidate_transformations) {
+    if (transformation.IsApplicable(GetIRContext(), *GetFactManager())) {
+      transformation.Apply(GetIRContext(), GetFactManager());
+      *GetTransformations()->add_transformation() = transformation.ToMessage();
+    }
+  }
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 39 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_dead_blocks.h

@@ -0,0 +1,39 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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.
+
+#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_DEAD_BLOCKS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_ADD_DEAD_BLOCKS_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Fuzzer pass to add dynamically unreachable blocks to the module.  Future
+// passes can then manipulate such blocks.
+class FuzzerPassAddDeadBlocks : public FuzzerPass {
+ public:
+  FuzzerPassAddDeadBlocks(opt::IRContext* ir_context, FactManager* fact_manager,
+                          FuzzerContext* fuzzer_context,
+                          protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassAddDeadBlocks();
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_ADD_DEAD_BLOCKS_H_

+ 0 - 1
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_construct_composites.cpp

@@ -148,7 +148,6 @@ void FuzzerPassConstructComposites::Apply() {
         transformation.Apply(GetIRContext(), GetFactManager());
         *GetTransformations()->add_transformation() =
             transformation.ToMessage();
-        // Indicate that one instruction was added.
       });
 }
 

+ 748 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_donate_modules.cpp

@@ -0,0 +1,748 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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.
+
+#include "source/fuzz/fuzzer_pass_donate_modules.h"
+
+#include <map>
+#include <queue>
+#include <set>
+
+#include "source/fuzz/instruction_message.h"
+#include "source/fuzz/transformation_add_constant_boolean.h"
+#include "source/fuzz/transformation_add_constant_composite.h"
+#include "source/fuzz/transformation_add_constant_scalar.h"
+#include "source/fuzz/transformation_add_function.h"
+#include "source/fuzz/transformation_add_global_undef.h"
+#include "source/fuzz/transformation_add_global_variable.h"
+#include "source/fuzz/transformation_add_type_array.h"
+#include "source/fuzz/transformation_add_type_boolean.h"
+#include "source/fuzz/transformation_add_type_float.h"
+#include "source/fuzz/transformation_add_type_function.h"
+#include "source/fuzz/transformation_add_type_int.h"
+#include "source/fuzz/transformation_add_type_matrix.h"
+#include "source/fuzz/transformation_add_type_pointer.h"
+#include "source/fuzz/transformation_add_type_struct.h"
+#include "source/fuzz/transformation_add_type_vector.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassDonateModules::FuzzerPassDonateModules(
+    opt::IRContext* ir_context, FactManager* fact_manager,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations,
+    const std::vector<fuzzerutil::ModuleSupplier>& donor_suppliers)
+    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations),
+      donor_suppliers_(donor_suppliers) {}
+
+FuzzerPassDonateModules::~FuzzerPassDonateModules() = default;
+
+void FuzzerPassDonateModules::Apply() {
+  // If there are no donor suppliers, this fuzzer pass is a no-op.
+  if (donor_suppliers_.empty()) {
+    return;
+  }
+
+  // Donate at least one module, and probabilistically decide when to stop
+  // donating modules.
+  do {
+    // Choose a donor supplier at random, and get the module that it provides.
+    std::unique_ptr<opt::IRContext> donor_ir_context = donor_suppliers_.at(
+        GetFuzzerContext()->RandomIndex(donor_suppliers_))();
+    assert(donor_ir_context != nullptr && "Supplying of donor failed");
+    // Donate the supplied module.
+    //
+    // Randomly decide whether to make the module livesafe (see
+    // FactFunctionIsLivesafe); doing so allows it to be used for live code
+    // injection but restricts its behaviour to allow this, and means that its
+    // functions cannot be transformed as if they were arbitrary dead code.
+    bool make_livesafe = GetFuzzerContext()->ChoosePercentage(
+        GetFuzzerContext()->ChanceOfMakingDonorLivesafe());
+    DonateSingleModule(donor_ir_context.get(), make_livesafe);
+  } while (GetFuzzerContext()->ChoosePercentage(
+      GetFuzzerContext()->GetChanceOfDonatingAdditionalModule()));
+}
+
+void FuzzerPassDonateModules::DonateSingleModule(
+    opt::IRContext* donor_ir_context, bool make_livesafe) {
+  // The ids used by the donor module may very well clash with ids defined in
+  // the recipient module.  Furthermore, some instructions defined in the donor
+  // module will be equivalent to instructions defined in the recipient module,
+  // and it is not always legal to re-declare equivalent instructions.  For
+  // example, OpTypeVoid cannot be declared twice.
+  //
+  // To handle this, we maintain a mapping from an id used in the donor module
+  // to the corresponding id that will be used by the donated code when it
+  // appears in the recipient module.
+  //
+  // This mapping is populated in two ways:
+  // (1) by mapping a donor instruction's result id to the id of some equivalent
+  //     existing instruction in the recipient (e.g. this has to be done for
+  //     OpTypeVoid)
+  // (2) by mapping a donor instruction's result id to a freshly chosen id that
+  //     is guaranteed to be different from any id already used by the recipient
+  //     (or from any id already chosen to handle a previous donor id)
+  std::map<uint32_t, uint32_t> original_id_to_donated_id;
+
+  HandleExternalInstructionImports(donor_ir_context,
+                                   &original_id_to_donated_id);
+  HandleTypesAndValues(donor_ir_context, &original_id_to_donated_id);
+  HandleFunctions(donor_ir_context, &original_id_to_donated_id, make_livesafe);
+
+  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3115) Handle some
+  //  kinds of decoration.
+}
+
+SpvStorageClass FuzzerPassDonateModules::AdaptStorageClass(
+    SpvStorageClass donor_storage_class) {
+  switch (donor_storage_class) {
+    case SpvStorageClassFunction:
+    case SpvStorageClassPrivate:
+      // We leave these alone
+      return donor_storage_class;
+    case SpvStorageClassInput:
+    case SpvStorageClassOutput:
+    case SpvStorageClassUniform:
+    case SpvStorageClassUniformConstant:
+    case SpvStorageClassPushConstant:
+      // We change these to Private
+      return SpvStorageClassPrivate;
+    default:
+      // Handle other cases on demand.
+      assert(false && "Currently unsupported storage class.");
+      return SpvStorageClassMax;
+  }
+}
+
+void FuzzerPassDonateModules::HandleExternalInstructionImports(
+    opt::IRContext* donor_ir_context,
+    std::map<uint32_t, uint32_t>* original_id_to_donated_id) {
+  // Consider every external instruction set import in the donor module.
+  for (auto& donor_import : donor_ir_context->module()->ext_inst_imports()) {
+    const auto& donor_import_name_words = donor_import.GetInOperand(0).words;
+    // Look for an identical import in the recipient module.
+    for (auto& existing_import : GetIRContext()->module()->ext_inst_imports()) {
+      const auto& existing_import_name_words =
+          existing_import.GetInOperand(0).words;
+      if (donor_import_name_words == existing_import_name_words) {
+        // A matching import has found.  Map the result id for the donor import
+        // to the id of the existing import, so that when donor instructions
+        // rely on the import they will be rewritten to use the existing import.
+        original_id_to_donated_id->insert(
+            {donor_import.result_id(), existing_import.result_id()});
+        break;
+      }
+    }
+    // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3116): At present
+    //  we do not handle donation of instruction imports, i.e. we do not allow
+    //  the donor to import instruction sets that the recipient did not already
+    //  import.  It might be a good idea to allow this, but it requires some
+    //  thought.
+    assert(original_id_to_donated_id->count(donor_import.result_id()) &&
+           "Donation of imports is not yet supported.");
+  }
+}
+
+void FuzzerPassDonateModules::HandleTypesAndValues(
+    opt::IRContext* donor_ir_context,
+    std::map<uint32_t, uint32_t>* original_id_to_donated_id) {
+  // Consider every type/global/constant/undef in the module.
+  for (auto& type_or_value : donor_ir_context->module()->types_values()) {
+    // Each such instruction generates a result id, and as part of donation we
+    // need to associate the donor's result id with a new result id.  That new
+    // result id will either be the id of some existing instruction, or a fresh
+    // id.  This variable captures it.
+    uint32_t new_result_id;
+
+    // Decide how to handle each kind of instruction on a case-by-case basis.
+    //
+    // Because the donor module is required to be valid, when we encounter a
+    // type comprised of component types (e.g. an aggregate or pointer), we know
+    // that its component types will have been considered previously, and that
+    // |original_id_to_donated_id| will already contain an entry for them.
+    switch (type_or_value.opcode()) {
+      case SpvOpTypeVoid: {
+        // Void has to exist already in order for us to have an entry point.
+        // Get the existing id of void.
+        opt::analysis::Void void_type;
+        new_result_id = GetIRContext()->get_type_mgr()->GetId(&void_type);
+        assert(new_result_id &&
+               "The module being transformed will always have 'void' type "
+               "declared.");
+      } break;
+      case SpvOpTypeBool: {
+        // Bool cannot be declared multiple times, so use its existing id if
+        // present, or add a declaration of Bool with a fresh id if not.
+        opt::analysis::Bool bool_type;
+        auto bool_type_id = GetIRContext()->get_type_mgr()->GetId(&bool_type);
+        if (bool_type_id) {
+          new_result_id = bool_type_id;
+        } else {
+          new_result_id = GetFuzzerContext()->GetFreshId();
+          ApplyTransformation(TransformationAddTypeBoolean(new_result_id));
+        }
+      } break;
+      case SpvOpTypeInt: {
+        // Int cannot be declared multiple times with the same width and
+        // signedness, so check whether an existing identical Int type is
+        // present and use its id if so.  Otherwise add a declaration of the
+        // Int type used by the donor, with a fresh id.
+        const uint32_t width = type_or_value.GetSingleWordInOperand(0);
+        const bool is_signed =
+            static_cast<bool>(type_or_value.GetSingleWordInOperand(1));
+        opt::analysis::Integer int_type(width, is_signed);
+        auto int_type_id = GetIRContext()->get_type_mgr()->GetId(&int_type);
+        if (int_type_id) {
+          new_result_id = int_type_id;
+        } else {
+          new_result_id = GetFuzzerContext()->GetFreshId();
+          ApplyTransformation(
+              TransformationAddTypeInt(new_result_id, width, is_signed));
+        }
+      } break;
+      case SpvOpTypeFloat: {
+        // Similar to SpvOpTypeInt.
+        const uint32_t width = type_or_value.GetSingleWordInOperand(0);
+        opt::analysis::Float float_type(width);
+        auto float_type_id = GetIRContext()->get_type_mgr()->GetId(&float_type);
+        if (float_type_id) {
+          new_result_id = float_type_id;
+        } else {
+          new_result_id = GetFuzzerContext()->GetFreshId();
+          ApplyTransformation(TransformationAddTypeFloat(new_result_id, width));
+        }
+      } break;
+      case SpvOpTypeVector: {
+        // It is not legal to have two Vector type declarations with identical
+        // element types and element counts, so check whether an existing
+        // identical Vector type is present and use its id if so.  Otherwise add
+        // a declaration of the Vector type used by the donor, with a fresh id.
+
+        // When considering the vector's component type id, we look up the id
+        // use in the donor to find the id to which this has been remapped.
+        uint32_t component_type_id = original_id_to_donated_id->at(
+            type_or_value.GetSingleWordInOperand(0));
+        auto component_type =
+            GetIRContext()->get_type_mgr()->GetType(component_type_id);
+        assert(component_type && "The base type should be registered.");
+        auto component_count = type_or_value.GetSingleWordInOperand(1);
+        opt::analysis::Vector vector_type(component_type, component_count);
+        auto vector_type_id =
+            GetIRContext()->get_type_mgr()->GetId(&vector_type);
+        if (vector_type_id) {
+          new_result_id = vector_type_id;
+        } else {
+          new_result_id = GetFuzzerContext()->GetFreshId();
+          ApplyTransformation(TransformationAddTypeVector(
+              new_result_id, component_type_id, component_count));
+        }
+      } break;
+      case SpvOpTypeMatrix: {
+        // Similar to SpvOpTypeVector.
+        uint32_t column_type_id = original_id_to_donated_id->at(
+            type_or_value.GetSingleWordInOperand(0));
+        auto column_type =
+            GetIRContext()->get_type_mgr()->GetType(column_type_id);
+        assert(column_type && column_type->AsVector() &&
+               "The column type should be a registered vector type.");
+        auto column_count = type_or_value.GetSingleWordInOperand(1);
+        opt::analysis::Matrix matrix_type(column_type, column_count);
+        auto matrix_type_id =
+            GetIRContext()->get_type_mgr()->GetId(&matrix_type);
+        if (matrix_type_id) {
+          new_result_id = matrix_type_id;
+        } else {
+          new_result_id = GetFuzzerContext()->GetFreshId();
+          ApplyTransformation(TransformationAddTypeMatrix(
+              new_result_id, column_type_id, column_count));
+        }
+
+      } break;
+      case SpvOpTypeArray: {
+        // It is OK to have multiple structurally identical array types, so
+        // we go ahead and add a remapped version of the type declared by the
+        // donor.
+        new_result_id = GetFuzzerContext()->GetFreshId();
+        ApplyTransformation(TransformationAddTypeArray(
+            new_result_id,
+            original_id_to_donated_id->at(
+                type_or_value.GetSingleWordInOperand(0)),
+            original_id_to_donated_id->at(
+                type_or_value.GetSingleWordInOperand(1))));
+      } break;
+      case SpvOpTypeStruct: {
+        // Similar to SpvOpTypeArray.
+        new_result_id = GetFuzzerContext()->GetFreshId();
+        std::vector<uint32_t> member_type_ids;
+        type_or_value.ForEachInId(
+            [&member_type_ids,
+             &original_id_to_donated_id](const uint32_t* component_type_id) {
+              member_type_ids.push_back(
+                  original_id_to_donated_id->at(*component_type_id));
+            });
+        ApplyTransformation(
+            TransformationAddTypeStruct(new_result_id, member_type_ids));
+      } break;
+      case SpvOpTypePointer: {
+        // Similar to SpvOpTypeArray.
+        new_result_id = GetFuzzerContext()->GetFreshId();
+        ApplyTransformation(TransformationAddTypePointer(
+            new_result_id,
+            AdaptStorageClass(static_cast<SpvStorageClass>(
+                type_or_value.GetSingleWordInOperand(0))),
+            original_id_to_donated_id->at(
+                type_or_value.GetSingleWordInOperand(1))));
+      } break;
+      case SpvOpTypeFunction: {
+        // It is not OK to have multiple function types that use identical ids
+        // for their return and parameter types.  We thus go through all
+        // existing function types to look for a match.  We do not use the
+        // type manager here because we want to regard two function types that
+        // are structurally identical but that differ with respect to the
+        // actual ids used for pointer types as different.
+        //
+        // Example:
+        //
+        // %1 = OpTypeVoid
+        // %2 = OpTypeInt 32 0
+        // %3 = OpTypePointer Function %2
+        // %4 = OpTypePointer Function %2
+        // %5 = OpTypeFunction %1 %3
+        // %6 = OpTypeFunction %1 %4
+        //
+        // We regard %5 and %6 as distinct function types here, even though
+        // they both have the form "uint32* -> void"
+
+        std::vector<uint32_t> return_and_parameter_types;
+        for (uint32_t i = 0; i < type_or_value.NumInOperands(); i++) {
+          return_and_parameter_types.push_back(original_id_to_donated_id->at(
+              type_or_value.GetSingleWordInOperand(i)));
+        }
+        uint32_t existing_function_id = fuzzerutil::FindFunctionType(
+            GetIRContext(), return_and_parameter_types);
+        if (existing_function_id) {
+          new_result_id = existing_function_id;
+        } else {
+          // No match was found, so add a remapped version of the function type
+          // to the module, with a fresh id.
+          new_result_id = GetFuzzerContext()->GetFreshId();
+          std::vector<uint32_t> argument_type_ids;
+          for (uint32_t i = 1; i < type_or_value.NumInOperands(); i++) {
+            argument_type_ids.push_back(original_id_to_donated_id->at(
+                type_or_value.GetSingleWordInOperand(i)));
+          }
+          ApplyTransformation(TransformationAddTypeFunction(
+              new_result_id,
+              original_id_to_donated_id->at(
+                  type_or_value.GetSingleWordInOperand(0)),
+              argument_type_ids));
+        }
+      } break;
+      case SpvOpConstantTrue:
+      case SpvOpConstantFalse: {
+        // It is OK to have duplicate definitions of True and False, so add
+        // these to the module, using a remapped Bool type.
+        new_result_id = GetFuzzerContext()->GetFreshId();
+        ApplyTransformation(TransformationAddConstantBoolean(
+            new_result_id, type_or_value.opcode() == SpvOpConstantTrue));
+      } break;
+      case SpvOpConstant: {
+        // It is OK to have duplicate constant definitions, so add this to the
+        // module using a remapped result type.
+        new_result_id = GetFuzzerContext()->GetFreshId();
+        std::vector<uint32_t> data_words;
+        type_or_value.ForEachInOperand(
+            [&data_words](const uint32_t* in_operand) {
+              data_words.push_back(*in_operand);
+            });
+        ApplyTransformation(TransformationAddConstantScalar(
+            new_result_id,
+            original_id_to_donated_id->at(type_or_value.type_id()),
+            data_words));
+      } break;
+      case SpvOpConstantComposite: {
+        // It is OK to have duplicate constant composite definitions, so add
+        // this to the module using remapped versions of all consituent ids and
+        // the result type.
+        new_result_id = GetFuzzerContext()->GetFreshId();
+        std::vector<uint32_t> constituent_ids;
+        type_or_value.ForEachInId(
+            [&constituent_ids,
+             &original_id_to_donated_id](const uint32_t* constituent_id) {
+              constituent_ids.push_back(
+                  original_id_to_donated_id->at(*constituent_id));
+            });
+        ApplyTransformation(TransformationAddConstantComposite(
+            new_result_id,
+            original_id_to_donated_id->at(type_or_value.type_id()),
+            constituent_ids));
+      } break;
+      case SpvOpVariable: {
+        // This is a global variable that could have one of various storage
+        // classes.  However, we change all global variable pointer storage
+        // classes (such as Uniform, Input and Output) to private when donating
+        // pointer types.  Thus this variable's pointer type is guaranteed to
+        // have storage class private.  As a result, we simply add a Private
+        // storage class global variable, using remapped versions of the result
+        // type and initializer ids for the global variable in the donor.
+        //
+        // We regard the added variable as having an arbitrary value.  This
+        // means that future passes can add stores to the variable in any
+        // way they wish, and pass them as pointer parameters to functions
+        // without worrying about whether their data might get modified.
+        new_result_id = GetFuzzerContext()->GetFreshId();
+        ApplyTransformation(TransformationAddGlobalVariable(
+            new_result_id,
+            original_id_to_donated_id->at(type_or_value.type_id()),
+            type_or_value.NumInOperands() == 1
+                ? 0
+                : original_id_to_donated_id->at(
+                      type_or_value.GetSingleWordInOperand(1)),
+            true));
+      } break;
+      case SpvOpUndef: {
+        // It is fine to have multiple Undef instructions of the same type, so
+        // we just add this to the recipient module.
+        new_result_id = GetFuzzerContext()->GetFreshId();
+        ApplyTransformation(TransformationAddGlobalUndef(
+            new_result_id,
+            original_id_to_donated_id->at(type_or_value.type_id())));
+      } break;
+      default: {
+        assert(0 && "Unknown type/value.");
+        new_result_id = 0;
+      } break;
+    }
+    // Update the id mapping to associate the instruction's result id with its
+    // corresponding id in the recipient.
+    original_id_to_donated_id->insert(
+        {type_or_value.result_id(), new_result_id});
+  }
+}
+
+void FuzzerPassDonateModules::HandleFunctions(
+    opt::IRContext* donor_ir_context,
+    std::map<uint32_t, uint32_t>* original_id_to_donated_id,
+    bool make_livesafe) {
+  // Get the ids of functions in the donor module, topologically sorted
+  // according to the donor's call graph.
+  auto topological_order =
+      GetFunctionsInCallGraphTopologicalOrder(donor_ir_context);
+
+  // Donate the functions in reverse topological order.  This ensures that a
+  // function gets donated before any function that depends on it.  This allows
+  // donation of the functions to be separated into a number of transformations,
+  // each adding one function, such that every prefix of transformations leaves
+  // the module valid.
+  for (auto function_id = topological_order.rbegin();
+       function_id != topological_order.rend(); ++function_id) {
+    // Find the function to be donated.
+    opt::Function* function_to_donate = nullptr;
+    for (auto& function : *donor_ir_context->module()) {
+      if (function.result_id() == *function_id) {
+        function_to_donate = &function;
+        break;
+      }
+    }
+    assert(function_to_donate && "Function to be donated was not found.");
+
+    // We will collect up protobuf messages representing the donor function's
+    // instructions here, and use them to create an AddFunction transformation.
+    std::vector<protobufs::Instruction> donated_instructions;
+
+    // Scan through the function, remapping each result id that it generates to
+    // a fresh id.  This is necessary because functions include forward
+    // references, e.g. to labels.
+    function_to_donate->ForEachInst([this, &original_id_to_donated_id](
+                                        const opt::Instruction* instruction) {
+      if (instruction->result_id()) {
+        original_id_to_donated_id->insert(
+            {instruction->result_id(), GetFuzzerContext()->GetFreshId()});
+      }
+    });
+
+    // Consider every instruction of the donor function.
+    function_to_donate->ForEachInst(
+        [&donated_instructions,
+         &original_id_to_donated_id](const opt::Instruction* instruction) {
+          // Get the instruction's input operands into donation-ready form,
+          // remapping any id uses in the process.
+          opt::Instruction::OperandList input_operands;
+
+          // Consider each input operand in turn.
+          for (uint32_t in_operand_index = 0;
+               in_operand_index < instruction->NumInOperands();
+               in_operand_index++) {
+            std::vector<uint32_t> operand_data;
+            const opt::Operand& in_operand =
+                instruction->GetInOperand(in_operand_index);
+            switch (in_operand.type) {
+              case SPV_OPERAND_TYPE_ID:
+              case SPV_OPERAND_TYPE_TYPE_ID:
+              case SPV_OPERAND_TYPE_RESULT_ID:
+              case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID:
+              case SPV_OPERAND_TYPE_SCOPE_ID:
+                // This is an id operand - it consists of a single word of data,
+                // which needs to be remapped so that it is replaced with the
+                // donated form of the id.
+                operand_data.push_back(
+                    original_id_to_donated_id->at(in_operand.words[0]));
+                break;
+              default:
+                // For non-id operands, we just add each of the data words.
+                for (auto word : in_operand.words) {
+                  operand_data.push_back(word);
+                }
+                break;
+            }
+            input_operands.push_back({in_operand.type, operand_data});
+          }
+          // Remap the result type and result id (if present) of the
+          // instruction, and turn it into a protobuf message.
+          donated_instructions.push_back(MakeInstructionMessage(
+              instruction->opcode(),
+              instruction->type_id()
+                  ? original_id_to_donated_id->at(instruction->type_id())
+                  : 0,
+              instruction->result_id()
+                  ? original_id_to_donated_id->at(instruction->result_id())
+                  : 0,
+              input_operands));
+        });
+
+    if (make_livesafe) {
+      // Various types and constants must be in place for a function to be made
+      // live-safe.  Add them if not already present.
+      FindOrCreateBoolType();  // Needed for comparisons
+      FindOrCreatePointerTo32BitIntegerType(
+          false, SpvStorageClassFunction);  // Needed for adding loop limiters
+      FindOrCreate32BitIntegerConstant(
+          0, false);  // Needed for initializing loop limiters
+      FindOrCreate32BitIntegerConstant(
+          1, false);  // Needed for incrementing loop limiters
+
+      // Get a fresh id for the variable that will be used as a loop limiter.
+      const uint32_t loop_limiter_variable_id =
+          GetFuzzerContext()->GetFreshId();
+      // Choose a random loop limit, and add the required constant to the
+      // module if not already there.
+      const uint32_t loop_limit = FindOrCreate32BitIntegerConstant(
+          GetFuzzerContext()->GetRandomLoopLimit(), false);
+
+      // Consider every loop header in the function to donate, and create a
+      // structure capturing the ids to be used for manipulating the loop
+      // limiter each time the loop is iterated.
+      std::vector<protobufs::LoopLimiterInfo> loop_limiters;
+      for (auto& block : *function_to_donate) {
+        if (block.IsLoopHeader()) {
+          protobufs::LoopLimiterInfo loop_limiter;
+          // Grab the loop header's id, mapped to its donated value.
+          loop_limiter.set_loop_header_id(
+              original_id_to_donated_id->at(block.id()));
+          // Get fresh ids that will be used to load the loop limiter, increment
+          // it, compare it with the loop limit, and an id for a new block that
+          // will contain the loop's original terminator.
+          loop_limiter.set_load_id(GetFuzzerContext()->GetFreshId());
+          loop_limiter.set_increment_id(GetFuzzerContext()->GetFreshId());
+          loop_limiter.set_compare_id(GetFuzzerContext()->GetFreshId());
+          loop_limiter.set_logical_op_id(GetFuzzerContext()->GetFreshId());
+          loop_limiters.emplace_back(loop_limiter);
+        }
+      }
+
+      // Consider every access chain in the function to donate, and create a
+      // structure containing the ids necessary to clamp the access chain
+      // indices to be in-bounds.
+      std::vector<protobufs::AccessChainClampingInfo>
+          access_chain_clamping_info;
+      for (auto& block : *function_to_donate) {
+        for (auto& inst : block) {
+          switch (inst.opcode()) {
+            case SpvOpAccessChain:
+            case SpvOpInBoundsAccessChain: {
+              protobufs::AccessChainClampingInfo clamping_info;
+              clamping_info.set_access_chain_id(
+                  original_id_to_donated_id->at(inst.result_id()));
+
+              auto base_object = donor_ir_context->get_def_use_mgr()->GetDef(
+                  inst.GetSingleWordInOperand(0));
+              assert(base_object && "The base object must exist.");
+              auto pointer_type = donor_ir_context->get_def_use_mgr()->GetDef(
+                  base_object->type_id());
+              assert(pointer_type &&
+                     pointer_type->opcode() == SpvOpTypePointer &&
+                     "The base object must have pointer type.");
+
+              auto should_be_composite_type =
+                  donor_ir_context->get_def_use_mgr()->GetDef(
+                      pointer_type->GetSingleWordInOperand(1));
+
+              // Walk the access chain, creating fresh ids to facilitate
+              // clamping each index.  For simplicity we do this for every
+              // index, even though constant indices will not end up being
+              // clamped.
+              for (uint32_t index = 1; index < inst.NumInOperands(); index++) {
+                auto compare_and_select_ids =
+                    clamping_info.add_compare_and_select_ids();
+                compare_and_select_ids->set_first(
+                    GetFuzzerContext()->GetFreshId());
+                compare_and_select_ids->set_second(
+                    GetFuzzerContext()->GetFreshId());
+
+                // Get the bound for the component being indexed into.
+                uint32_t bound =
+                    TransformationAddFunction::GetBoundForCompositeIndex(
+                        donor_ir_context, *should_be_composite_type);
+                const uint32_t index_id = inst.GetSingleWordInOperand(index);
+                auto index_inst =
+                    donor_ir_context->get_def_use_mgr()->GetDef(index_id);
+                auto index_type_inst =
+                    donor_ir_context->get_def_use_mgr()->GetDef(
+                        index_inst->type_id());
+                assert(index_type_inst->opcode() == SpvOpTypeInt);
+                assert(index_type_inst->GetSingleWordInOperand(0) == 32);
+                opt::analysis::Integer* index_int_type =
+                    donor_ir_context->get_type_mgr()
+                        ->GetType(index_type_inst->result_id())
+                        ->AsInteger();
+                if (index_inst->opcode() != SpvOpConstant) {
+                  // We will have to clamp this index, so we need a constant
+                  // whose value is one less than the bound, to compare
+                  // against and to use as the clamped value.
+                  FindOrCreate32BitIntegerConstant(bound - 1,
+                                                   index_int_type->IsSigned());
+                }
+                should_be_composite_type =
+                    TransformationAddFunction::FollowCompositeIndex(
+                        donor_ir_context, *should_be_composite_type, index_id);
+              }
+              access_chain_clamping_info.push_back(clamping_info);
+              break;
+            }
+            default:
+              break;
+          }
+        }
+      }
+
+      // If the function contains OpKill or OpUnreachable instructions, and has
+      // non-void return type, then we need a value %v to use in order to turn
+      // these into instructions of the form OpReturn %v.
+      uint32_t kill_unreachable_return_value_id;
+      auto function_return_type_inst =
+          donor_ir_context->get_def_use_mgr()->GetDef(
+              function_to_donate->type_id());
+      if (function_return_type_inst->opcode() == SpvOpTypeVoid) {
+        // The return type is void, so we don't need a return value.
+        kill_unreachable_return_value_id = 0;
+      } else {
+        // We do need a return value; we use OpUndef.
+        kill_unreachable_return_value_id =
+            FindOrCreateGlobalUndef(function_return_type_inst->type_id());
+      }
+      // Add the function in a livesafe manner.
+      ApplyTransformation(TransformationAddFunction(
+          donated_instructions, loop_limiter_variable_id, loop_limit,
+          loop_limiters, kill_unreachable_return_value_id,
+          access_chain_clamping_info));
+    } else {
+      // Add the function in a non-livesafe manner.
+      ApplyTransformation(TransformationAddFunction(donated_instructions));
+    }
+  }
+}
+
+std::vector<uint32_t>
+FuzzerPassDonateModules::GetFunctionsInCallGraphTopologicalOrder(
+    opt::IRContext* context) {
+  // This is an implementation of Kahn’s algorithm for topological sorting.
+
+  // For each function id, stores the number of distinct functions that call
+  // the function.
+  std::map<uint32_t, uint32_t> function_in_degree;
+
+  // We first build a call graph for the module, and compute the in-degree for
+  // each function in the process.
+  // TODO(afd): If there is functionality elsewhere in the SPIR-V tools
+  //  framework to construct call graphs it could be nice to re-use it here.
+  std::map<uint32_t, std::set<uint32_t>> call_graph_edges;
+
+  // Initialize function in-degree and call graph edges to 0 and empty.
+  for (auto& function : *context->module()) {
+    function_in_degree[function.result_id()] = 0;
+    call_graph_edges[function.result_id()] = std::set<uint32_t>();
+  }
+
+  // Consider every function.
+  for (auto& function : *context->module()) {
+    // Avoid considering the same callee of this function multiple times by
+    // recording known callees.
+    std::set<uint32_t> known_callees;
+    // Consider every function call instruction in every block.
+    for (auto& block : function) {
+      for (auto& instruction : block) {
+        if (instruction.opcode() != SpvOpFunctionCall) {
+          continue;
+        }
+        // Get the id of the function being called.
+        uint32_t callee = instruction.GetSingleWordInOperand(0);
+        if (known_callees.count(callee)) {
+          // We have already considered a call to this function - ignore it.
+          continue;
+        }
+        // Increase the callee's in-degree and add an edge to the call graph.
+        function_in_degree[callee]++;
+        call_graph_edges[function.result_id()].insert(callee);
+        // Mark the callee as 'known'.
+        known_callees.insert(callee);
+      }
+    }
+  }
+
+  // This is the sorted order of function ids that we will eventually return.
+  std::vector<uint32_t> result;
+
+  // Populate a queue with all those function ids with in-degree zero.
+  std::queue<uint32_t> queue;
+  for (auto& entry : function_in_degree) {
+    if (entry.second == 0) {
+      queue.push(entry.first);
+    }
+  }
+
+  // Pop ids from the queue, adding them to the sorted order and decreasing the
+  // in-degrees of their successors.  A successor who's in-degree becomes zero
+  // gets added to the queue.
+  while (!queue.empty()) {
+    auto next = queue.front();
+    queue.pop();
+    result.push_back(next);
+    for (auto successor : call_graph_edges.at(next)) {
+      assert(function_in_degree.at(successor) > 0 &&
+             "The in-degree cannot be zero if the function is a successor.");
+      function_in_degree[successor] = function_in_degree.at(successor) - 1;
+      if (function_in_degree.at(successor) == 0) {
+        queue.push(successor);
+      }
+    }
+  }
+
+  assert(result.size() == function_in_degree.size() &&
+         "Every function should appear in the sort.");
+
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 93 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_donate_modules.h

@@ -0,0 +1,93 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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.
+
+#ifndef SOURCE_FUZZ_FUZZER_PASS_DONATE_MODULES_H_
+#define SOURCE_FUZZ_FUZZER_PASS_DONATE_MODULES_H_
+
+#include <vector>
+
+#include "source/fuzz/fuzzer_pass.h"
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// A fuzzer pass that randomly adds code from other SPIR-V modules to the module
+// being transformed.
+class FuzzerPassDonateModules : public FuzzerPass {
+ public:
+  FuzzerPassDonateModules(
+      opt::IRContext* ir_context, FactManager* fact_manager,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations,
+      const std::vector<fuzzerutil::ModuleSupplier>& donor_suppliers);
+
+  ~FuzzerPassDonateModules();
+
+  void Apply() override;
+
+  // Donates the global declarations and functions of |donor_ir_context| into
+  // the fuzzer pass's IR context.  |make_livesafe| dictates whether the
+  // functions of the donated module will be made livesafe (see
+  // FactFunctionIsLivesafe).
+  void DonateSingleModule(opt::IRContext* donor_ir_context, bool make_livesafe);
+
+ private:
+  // Adapts a storage class coming from a donor module so that it will work
+  // in a recipient module, e.g. by changing Uniform to Private.
+  static SpvStorageClass AdaptStorageClass(SpvStorageClass donor_storage_class);
+
+  // Identifies all external instruction set imports in |donor_ir_context| and
+  // populates |original_id_to_donated_id| with a mapping from the donor's id
+  // for such an import to a corresponding import in the recipient.  Aborts if
+  // no such corresponding import is available.
+  void HandleExternalInstructionImports(
+      opt::IRContext* donor_ir_context,
+      std::map<uint32_t, uint32_t>* original_id_to_donated_id);
+
+  // Considers all types, globals, constants and undefs in |donor_ir_context|.
+  // For each instruction, uses |original_to_donated_id| to map its result id to
+  // either (1) the id of an existing identical instruction in the recipient, or
+  // (2) to a fresh id, in which case the instruction is also added to the
+  // recipient (with any operand ids that it uses being remapped via
+  // |original_id_to_donated_id|).
+  void HandleTypesAndValues(
+      opt::IRContext* donor_ir_context,
+      std::map<uint32_t, uint32_t>* original_id_to_donated_id);
+
+  // Assumes that |donor_ir_context| does not exhibit recursion.  Considers the
+  // functions in |donor_ir_context|'s call graph in a reverse-topologically-
+  // sorted order (leaves-to-root), adding each function to the recipient
+  // module, rewritten to use fresh ids and using |original_id_to_donated_id| to
+  // remap ids.  The |make_livesafe| argument captures whether the functions in
+  // the module are required to be made livesafe before being added to the
+  // recipient.
+  void HandleFunctions(opt::IRContext* donor_ir_context,
+                       std::map<uint32_t, uint32_t>* original_id_to_donated_id,
+                       bool make_livesafe);
+
+  // Returns the ids of all functions in |context| in a topological order in
+  // relation to the call graph of |context|, which is assumed to be recursion-
+  // free.
+  static std::vector<uint32_t> GetFunctionsInCallGraphTopologicalOrder(
+      opt::IRContext* context);
+
+  // Functions that supply SPIR-V modules
+  std::vector<fuzzerutil::ModuleSupplier> donor_suppliers_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_DONATE_MODULES_H_

+ 65 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_merge_blocks.cpp

@@ -0,0 +1,65 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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.
+
+#include "source/fuzz/fuzzer_pass_merge_blocks.h"
+
+#include <vector>
+
+#include "source/fuzz/transformation_merge_blocks.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassMergeBlocks::FuzzerPassMergeBlocks(
+    opt::IRContext* ir_context, FactManager* fact_manager,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+
+FuzzerPassMergeBlocks::~FuzzerPassMergeBlocks() = default;
+
+void FuzzerPassMergeBlocks::Apply() {
+  // First we populate a sequence of transformations that we might consider
+  // applying.
+  std::vector<TransformationMergeBlocks> potential_transformations;
+  // We do this by considering every block of every function.
+  for (auto& function : *GetIRContext()->module()) {
+    for (auto& block : function) {
+      // We probabilistically decide to ignore some blocks.
+      if (!GetFuzzerContext()->ChoosePercentage(
+              GetFuzzerContext()->GetChanceOfMergingBlocks())) {
+        continue;
+      }
+      // For other blocks, we add a transformation to merge the block into its
+      // predecessor if that transformation would be applicable.
+      TransformationMergeBlocks transformation(block.id());
+      if (transformation.IsApplicable(GetIRContext(), *GetFactManager())) {
+        potential_transformations.push_back(transformation);
+      }
+    }
+  }
+
+  while (!potential_transformations.empty()) {
+    uint32_t index = GetFuzzerContext()->RandomIndex(potential_transformations);
+    auto transformation = potential_transformations.at(index);
+    potential_transformations.erase(potential_transformations.begin() + index);
+    if (transformation.IsApplicable(GetIRContext(), *GetFactManager())) {
+      transformation.Apply(GetIRContext(), GetFactManager());
+      *GetTransformations()->add_transformation() = transformation.ToMessage();
+    }
+  }
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 38 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_merge_blocks.h

@@ -0,0 +1,38 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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.
+
+#ifndef SOURCE_FUZZ_FUZZER_PASS_MERGE_BLOCKS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_MERGE_BLOCKS_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// A fuzzer pass for merging blocks in the module.
+class FuzzerPassMergeBlocks : public FuzzerPass {
+ public:
+  FuzzerPassMergeBlocks(opt::IRContext* ir_context, FactManager* fact_manager,
+                        FuzzerContext* fuzzer_context,
+                        protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassMergeBlocks();
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_MERGE_BLOCKS_H_

+ 99 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_outline_functions.cpp

@@ -0,0 +1,99 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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.
+
+#include "source/fuzz/fuzzer_pass_outline_functions.h"
+
+#include <vector>
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/transformation_outline_function.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassOutlineFunctions::FuzzerPassOutlineFunctions(
+    opt::IRContext* ir_context, FactManager* fact_manager,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+
+FuzzerPassOutlineFunctions::~FuzzerPassOutlineFunctions() = default;
+
+void FuzzerPassOutlineFunctions::Apply() {
+  std::vector<opt::Function*> original_functions;
+  for (auto& function : *GetIRContext()->module()) {
+    original_functions.push_back(&function);
+  }
+  for (auto& function : original_functions) {
+    if (!GetFuzzerContext()->ChoosePercentage(
+            GetFuzzerContext()->GetChanceOfOutliningFunction())) {
+      continue;
+    }
+    std::vector<opt::BasicBlock*> blocks;
+    for (auto& block : *function) {
+      blocks.push_back(&block);
+    }
+    auto entry_block = blocks[GetFuzzerContext()->RandomIndex(blocks)];
+    auto dominator_analysis = GetIRContext()->GetDominatorAnalysis(function);
+    auto postdominator_analysis =
+        GetIRContext()->GetPostDominatorAnalysis(function);
+    std::vector<opt::BasicBlock*> candidate_exit_blocks;
+    for (auto postdominates_entry_block = entry_block;
+         postdominates_entry_block != nullptr;
+         postdominates_entry_block = postdominator_analysis->ImmediateDominator(
+             postdominates_entry_block)) {
+      if (dominator_analysis->Dominates(entry_block,
+                                        postdominates_entry_block)) {
+        candidate_exit_blocks.push_back(postdominates_entry_block);
+      }
+    }
+    if (candidate_exit_blocks.empty()) {
+      continue;
+    }
+    auto exit_block = candidate_exit_blocks[GetFuzzerContext()->RandomIndex(
+        candidate_exit_blocks)];
+
+    auto region_blocks = TransformationOutlineFunction::GetRegionBlocks(
+        GetIRContext(), entry_block, exit_block);
+    std::map<uint32_t, uint32_t> input_id_to_fresh_id;
+    for (auto id : TransformationOutlineFunction::GetRegionInputIds(
+             GetIRContext(), region_blocks, exit_block)) {
+      input_id_to_fresh_id[id] = GetFuzzerContext()->GetFreshId();
+    }
+    std::map<uint32_t, uint32_t> output_id_to_fresh_id;
+    for (auto id : TransformationOutlineFunction::GetRegionOutputIds(
+             GetIRContext(), region_blocks, exit_block)) {
+      output_id_to_fresh_id[id] = GetFuzzerContext()->GetFreshId();
+    }
+    TransformationOutlineFunction transformation(
+        entry_block->id(), exit_block->id(),
+        /*new_function_struct_return_type_id*/
+        GetFuzzerContext()->GetFreshId(),
+        /*new_function_type_id*/ GetFuzzerContext()->GetFreshId(),
+        /*new_function_id*/ GetFuzzerContext()->GetFreshId(),
+        /*new_function_region_entry_block*/
+        GetFuzzerContext()->GetFreshId(),
+        /*new_caller_result_id*/ GetFuzzerContext()->GetFreshId(),
+        /*new_callee_result_id*/ GetFuzzerContext()->GetFreshId(),
+        /*input_id_to_fresh_id*/ std::move(input_id_to_fresh_id),
+        /*output_id_to_fresh_id*/ std::move(output_id_to_fresh_id));
+    if (transformation.IsApplicable(GetIRContext(), *GetFactManager())) {
+      transformation.Apply(GetIRContext(), GetFactManager());
+      *GetTransformations()->add_transformation() = transformation.ToMessage();
+    }
+  }
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 40 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_outline_functions.h

@@ -0,0 +1,40 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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.
+
+#ifndef SOURCE_FUZZ_FUZZER_PASS_OUTLINE_FUNCTIONS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_OUTLINE_FUNCTIONS_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// A fuzzer pass for outlining single-entry single-exit regions of a  control
+// flow graph into their own functions.
+class FuzzerPassOutlineFunctions : public FuzzerPass {
+ public:
+  FuzzerPassOutlineFunctions(
+      opt::IRContext* ir_context, FactManager* fact_manager,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassOutlineFunctions();
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_OUTLINE_FUNCTIONS_H_

+ 116 - 37
3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp

@@ -103,10 +103,23 @@ bool PhiIdsOkForNewEdge(
     }
     phi_index++;
   }
-  // Return false if not all of the ids for extending OpPhi instructions are
-  // needed. This might turn out to be stricter than necessary; perhaps it would
-  // be OK just to not use the ids in this case.
-  return phi_index == static_cast<uint32_t>(phi_ids.size());
+  // We allow some of the ids provided for extending OpPhi instructions to be
+  // unused.  Their presence does no harm, and requiring a perfect match may
+  // make transformations less likely to cleanly apply.
+  return true;
+}
+
+uint32_t MaybeGetBoolConstantId(opt::IRContext* context, bool value) {
+  opt::analysis::Bool bool_type;
+  auto registered_bool_type =
+      context->get_type_mgr()->GetRegisteredType(&bool_type);
+  if (!registered_bool_type) {
+    return 0;
+  }
+  opt::analysis::BoolConstant bool_constant(registered_bool_type->AsBool(),
+                                            value);
+  return context->get_constant_mgr()->FindDeclaredConstant(
+      &bool_constant, context->get_type_mgr()->GetId(&bool_type));
 }
 
 void AddUnreachableEdgeAndUpdateOpPhis(
@@ -119,12 +132,10 @@ void AddUnreachableEdgeAndUpdateOpPhis(
          "Precondition on terminator of bb_from is not satisfied");
 
   // Get the id of the boolean constant to be used as the condition.
-  opt::analysis::Bool bool_type;
-  opt::analysis::BoolConstant bool_constant(
-      context->get_type_mgr()->GetRegisteredType(&bool_type)->AsBool(),
-      condition_value);
-  uint32_t bool_id = context->get_constant_mgr()->FindDeclaredConstant(
-      &bool_constant, context->get_type_mgr()->GetId(&bool_type));
+  uint32_t bool_id = MaybeGetBoolConstantId(context, condition_value);
+  assert(
+      bool_id &&
+      "Precondition that condition value must be available is not satisfied");
 
   const bool from_to_edge_already_exists = bb_from->IsSuccessor(bb_to);
   auto successor = bb_from->terminator()->GetSingleWordInOperand(0);
@@ -147,13 +158,11 @@ void AddUnreachableEdgeAndUpdateOpPhis(
         break;
       }
       assert(phi_index < static_cast<uint32_t>(phi_ids.size()) &&
-             "There should be exactly one phi id per OpPhi instruction.");
+             "There should be at least one phi id per OpPhi instruction.");
       inst.AddOperand({SPV_OPERAND_TYPE_ID, {phi_ids[phi_index]}});
       inst.AddOperand({SPV_OPERAND_TYPE_ID, {bb_from->id()}});
       phi_index++;
     }
-    assert(phi_index == static_cast<uint32_t>(phi_ids.size()) &&
-           "There should be exactly one phi id per OpPhi instruction.");
   }
 }
 
@@ -217,6 +226,20 @@ bool CanMakeSynonymOf(opt::IRContext* ir_context, opt::Instruction* inst) {
     // We can only make a synonym of an instruction that has a type.
     return false;
   }
+  auto type_inst = ir_context->get_def_use_mgr()->GetDef(inst->type_id());
+  if (type_inst->opcode() == SpvOpTypePointer) {
+    switch (inst->opcode()) {
+      case SpvOpConstantNull:
+      case SpvOpUndef:
+        // We disallow making synonyms of null or undefined pointers.  This is
+        // to provide the property that if the original shader exhibited no bad
+        // pointer accesses, the transformed shader will not either.
+        return false;
+      default:
+        break;
+    }
+  }
+
   // We do not make synonyms of objects that have decorations: if the synonym is
   // not decorated analogously, using the original object vs. its synonymous
   // form may not be equivalent.
@@ -247,33 +270,36 @@ uint32_t WalkCompositeTypeIndices(
     auto should_be_composite_type =
         context->get_def_use_mgr()->GetDef(sub_object_type_id);
     assert(should_be_composite_type && "The type should exist.");
-    if (SpvOpTypeArray == should_be_composite_type->opcode()) {
-      auto array_length = GetArraySize(*should_be_composite_type, context);
-      if (array_length == 0 || index >= array_length) {
-        return 0;
+    switch (should_be_composite_type->opcode()) {
+      case SpvOpTypeArray: {
+        auto array_length = GetArraySize(*should_be_composite_type, context);
+        if (array_length == 0 || index >= array_length) {
+          return 0;
+        }
+        sub_object_type_id =
+            should_be_composite_type->GetSingleWordInOperand(0);
+        break;
       }
-      sub_object_type_id = should_be_composite_type->GetSingleWordInOperand(0);
-    } else if (SpvOpTypeMatrix == should_be_composite_type->opcode()) {
-      auto matrix_column_count =
-          should_be_composite_type->GetSingleWordInOperand(1);
-      if (index >= matrix_column_count) {
-        return 0;
+      case SpvOpTypeMatrix:
+      case SpvOpTypeVector: {
+        auto count = should_be_composite_type->GetSingleWordInOperand(1);
+        if (index >= count) {
+          return 0;
+        }
+        sub_object_type_id =
+            should_be_composite_type->GetSingleWordInOperand(0);
+        break;
       }
-      sub_object_type_id = should_be_composite_type->GetSingleWordInOperand(0);
-    } else if (SpvOpTypeStruct == should_be_composite_type->opcode()) {
-      if (index >= GetNumberOfStructMembers(*should_be_composite_type)) {
-        return 0;
+      case SpvOpTypeStruct: {
+        if (index >= GetNumberOfStructMembers(*should_be_composite_type)) {
+          return 0;
+        }
+        sub_object_type_id =
+            should_be_composite_type->GetSingleWordInOperand(index);
+        break;
       }
-      sub_object_type_id =
-          should_be_composite_type->GetSingleWordInOperand(index);
-    } else if (SpvOpTypeVector == should_be_composite_type->opcode()) {
-      auto vector_length = should_be_composite_type->GetSingleWordInOperand(1);
-      if (index >= vector_length) {
+      default:
         return 0;
-      }
-      sub_object_type_id = should_be_composite_type->GetSingleWordInOperand(0);
-    } else {
-      return 0;
     }
   }
   return sub_object_type_id;
@@ -302,7 +328,8 @@ uint32_t GetArraySize(const opt::Instruction& array_type_instruction,
 bool IsValid(opt::IRContext* context) {
   std::vector<uint32_t> binary;
   context->module()->ToBinary(&binary, false);
-  return SpirvTools(context->grammar().target_env()).Validate(binary);
+  SpirvTools tools(context->grammar().target_env());
+  return tools.Validate(binary);
 }
 
 std::unique_ptr<opt::IRContext> CloneIRContext(opt::IRContext* context) {
@@ -312,6 +339,58 @@ std::unique_ptr<opt::IRContext> CloneIRContext(opt::IRContext* context) {
                      binary.size());
 }
 
+bool IsNonFunctionTypeId(opt::IRContext* ir_context, uint32_t id) {
+  auto type = ir_context->get_type_mgr()->GetType(id);
+  return type && !type->AsFunction();
+}
+
+bool IsMergeOrContinue(opt::IRContext* ir_context, uint32_t block_id) {
+  bool result = false;
+  ir_context->get_def_use_mgr()->WhileEachUse(
+      block_id,
+      [&result](const opt::Instruction* use_instruction,
+                uint32_t /*unused*/) -> bool {
+        switch (use_instruction->opcode()) {
+          case SpvOpLoopMerge:
+          case SpvOpSelectionMerge:
+            result = true;
+            return false;
+          default:
+            return true;
+        }
+      });
+  return result;
+}
+
+uint32_t FindFunctionType(opt::IRContext* ir_context,
+                          const std::vector<uint32_t>& type_ids) {
+  // Look through the existing types for a match.
+  for (auto& type_or_value : ir_context->types_values()) {
+    if (type_or_value.opcode() != SpvOpTypeFunction) {
+      // We are only interested in function types.
+      continue;
+    }
+    if (type_or_value.NumInOperands() != type_ids.size()) {
+      // Not a match: different numbers of arguments.
+      continue;
+    }
+    // Check whether the return type and argument types match.
+    bool input_operands_match = true;
+    for (uint32_t i = 0; i < type_or_value.NumInOperands(); i++) {
+      if (type_ids[i] != type_or_value.GetSingleWordInOperand(i)) {
+        input_operands_match = false;
+        break;
+      }
+    }
+    if (input_operands_match) {
+      // Everything matches.
+      return type_or_value.result_id();
+    }
+  }
+  // No match was found.
+  return 0;
+}
+
 }  // namespace fuzzerutil
 
 }  // namespace fuzz

+ 24 - 3
3rdparty/spirv-tools/source/fuzz/fuzzer_util.h

@@ -25,9 +25,12 @@
 namespace spvtools {
 namespace fuzz {
 
-// Provides global utility methods for use by the fuzzer
+// Provides types and global utility methods for use by the fuzzer
 namespace fuzzerutil {
 
+// Function type that produces a SPIR-V module.
+using ModuleSupplier = std::function<std::unique_ptr<opt::IRContext>()>;
+
 // Returns true if and only if the module does not define the given id.
 bool IsFreshId(opt::IRContext* context, uint32_t id);
 
@@ -49,8 +52,13 @@ bool PhiIdsOkForNewEdge(
     opt::IRContext* context, opt::BasicBlock* bb_from, opt::BasicBlock* bb_to,
     const google::protobuf::RepeatedField<google::protobuf::uint32>& phi_ids);
 
-// Requires that PhiIdsOkForNewEdge(context, bb_from, bb_to, phi_ids) holds,
-// and that bb_from ends with "OpBranch %some_block".  Turns OpBranch into
+// Returns the id of a boolean constant with value |value| if it exists in the
+// module, or 0 otherwise.
+uint32_t MaybeGetBoolConstantId(opt::IRContext* context, bool value);
+
+// Requires that a boolean constant with value |condition_value| is available,
+// that PhiIdsOkForNewEdge(context, bb_from, bb_to, phi_ids) holds, and that
+// bb_from ends with "OpBranch %some_block".  Turns OpBranch into
 // "OpBranchConditional |condition_value| ...", such that control will branch
 // to %some_block, with |bb_to| being the unreachable alternative.  Updates
 // OpPhi instructions in |bb_to| using |phi_ids| so that the new edge is valid.
@@ -116,6 +124,19 @@ bool IsValid(opt::IRContext* context);
 // parsing it again.
 std::unique_ptr<opt::IRContext> CloneIRContext(opt::IRContext* context);
 
+// Returns true if and only if |id| is the id of a type that is not a function
+// type.
+bool IsNonFunctionTypeId(opt::IRContext* ir_context, uint32_t id);
+
+// Returns true if and only if |block_id| is a merge block or continue target
+bool IsMergeOrContinue(opt::IRContext* ir_context, uint32_t block_id);
+
+// Returns the result id of an instruction of the form:
+//  %id = OpTypeFunction |type_ids|
+// or 0 if no such instruction exists.
+uint32_t FindFunctionType(opt::IRContext* ir_context,
+                          const std::vector<uint32_t>& type_ids);
+
 }  // namespace fuzzerutil
 
 }  // namespace fuzz

+ 68 - 0
3rdparty/spirv-tools/source/fuzz/instruction_message.cpp

@@ -0,0 +1,68 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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.
+
+#include "source/fuzz/instruction_message.h"
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+protobufs::Instruction MakeInstructionMessage(
+    SpvOp opcode, uint32_t result_type_id, uint32_t result_id,
+    const opt::Instruction::OperandList& input_operands) {
+  protobufs::Instruction result;
+  result.set_opcode(opcode);
+  result.set_result_type_id(result_type_id);
+  result.set_result_id(result_id);
+  for (auto& operand : input_operands) {
+    auto operand_message = result.add_input_operand();
+    operand_message->set_operand_type(static_cast<uint32_t>(operand.type));
+    for (auto operand_word : operand.words) {
+      operand_message->add_operand_data(operand_word);
+    }
+  }
+  return result;
+}
+
+std::unique_ptr<opt::Instruction> InstructionFromMessage(
+    opt::IRContext* ir_context,
+    const protobufs::Instruction& instruction_message) {
+  // First, update the module's id bound with respect to the new instruction,
+  // if it has a result id.
+  if (instruction_message.result_id()) {
+    fuzzerutil::UpdateModuleIdBound(ir_context,
+                                    instruction_message.result_id());
+  }
+  // Now create a sequence of input operands from the input operand data in the
+  // protobuf message.
+  opt::Instruction::OperandList in_operands;
+  for (auto& operand_message : instruction_message.input_operand()) {
+    opt::Operand::OperandData operand_data;
+    for (auto& word : operand_message.operand_data()) {
+      operand_data.push_back(word);
+    }
+    in_operands.push_back(
+        {static_cast<spv_operand_type_t>(operand_message.operand_type()),
+         operand_data});
+  }
+  // Create and return the instruction.
+  return MakeUnique<opt::Instruction>(
+      ir_context, static_cast<SpvOp>(instruction_message.opcode()),
+      instruction_message.result_type_id(), instruction_message.result_id(),
+      in_operands);
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 43 - 0
3rdparty/spirv-tools/source/fuzz/instruction_message.h

@@ -0,0 +1,43 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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.
+
+#ifndef SOURCE_FUZZ_INSTRUCTION_MESSAGE_H_
+#define SOURCE_FUZZ_INSTRUCTION_MESSAGE_H_
+
+#include <memory>
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/opt/instruction.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Creates an Instruction protobuf message from its component parts.
+protobufs::Instruction MakeInstructionMessage(
+    SpvOp opcode, uint32_t result_type_id, uint32_t result_id,
+    const opt::Instruction::OperandList& input_operands);
+
+// Creates and returns an opt::Instruction from protobuf message
+// |instruction_message|, relative to |ir_context|.  In the process, the module
+// id bound associated with |ir_context| is updated to be at least as large as
+// the result id (if any) associated with the new instruction.
+std::unique_ptr<opt::Instruction> InstructionFromMessage(
+    opt::IRContext* ir_context,
+    const protobufs::Instruction& instruction_message);
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_INSTRUCTION_MESSAGE_H_

+ 385 - 1
3rdparty/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto

@@ -21,6 +21,16 @@ syntax = "proto3";
 
 package spvtools.fuzz.protobufs;
 
+message UInt32Pair {
+
+  // A pair of uint32s; useful for defining mappings.
+
+  uint32 first = 1;
+
+  uint32 second = 2;
+
+}
+
 message InstructionDescriptor {
 
   // Describes an instruction in some block of a function with respect to a
@@ -116,6 +126,37 @@ message UniformBufferElementDescriptor {
 
 }
 
+message InstructionOperand {
+
+  // Represents an operand to a SPIR-V instruction.
+
+  // The type of the operand.
+  uint32 operand_type = 1;
+
+  // The data associated with the operand.  For most operands (e.g. ids,
+  // storage classes and literals) this will be a single word.
+  repeated uint32 operand_data = 2;
+
+}
+
+message Instruction {
+
+  // Represents a SPIR-V instruction.
+
+  // The instruction's opcode (e.g. OpLabel).
+  uint32 opcode = 1;
+
+  // The id of the instruction's result type; 0 if there is no result type.
+  uint32 result_type_id = 2;
+
+  // The id of the instruction's result; 0 if there is no result.
+  uint32 result_id = 3;
+
+  // Zero or more input operands.
+  repeated InstructionOperand input_operand = 4;
+
+}
+
 message FactSequence {
   repeated Fact fact = 1;
 }
@@ -125,6 +166,9 @@ message Fact {
     // Order the fact options by numeric id (rather than alphabetically).
     FactConstantUniform constant_uniform_fact = 1;
     FactDataSynonym data_synonym_fact = 2;
+    FactBlockIsDead block_is_dead_fact = 3;
+    FactFunctionIsLivesafe function_is_livesafe_fact = 4;
+    FactValueOfVariableIsArbitrary value_of_variable_is_arbitrary = 5;
   }
 }
 
@@ -159,6 +203,99 @@ message FactDataSynonym {
 
 }
 
+message FactBlockIsDead {
+
+  // Records the fact that a block is guaranteed to be dynamically unreachable.
+  // This is useful because it informs the fuzzer that rather arbitrary changes
+  // can be made to this block.
+
+  uint32 block_id = 1;
+}
+
+message FactFunctionIsLivesafe {
+
+  // Records the fact that a function is guaranteed to be "livesafe", meaning
+  // that it will not make out-of-bounds accesses, does not contain reachable
+  // OpKill or OpUnreachable instructions, does not contain loops that will
+  // execute for large numbers of iterations, and only invokes other livesafe
+  // functions.
+
+  uint32 function_id = 1;
+}
+
+message FactValueOfVariableIsArbitrary {
+
+  // Records the fact that the value stored in the variable or function
+  // parameter with the given id can be arbitrary: the module's observable
+  // behaviour does not depend on it.  This means that arbitrary stores can be
+  // made to the variable, and that nothing can be guaranteed about values
+  // loaded from the variable.
+
+  // The result id of an OpVariable instruction, or an OpFunctionParameter
+  // instruction with pointer type
+  uint32 variable_id = 1;
+}
+
+message AccessChainClampingInfo {
+
+  // When making a function livesafe it is necessary to clamp the indices that
+  // occur as operands to access chain instructions so that they are guaranteed
+  // to be in bounds.  This message type allows an access chain instruction to
+  // have an associated sequence of ids that are reserved for comparing an
+  // access chain index with a bound (e.g. an array size), and selecting
+  // between the access chain index (if it is within bounds) and the bound (if
+  // it is not).
+  //
+  // This allows turning an instruction of the form:
+  //
+  // %result = OpAccessChain %type %object ... %index ...
+  //
+  // into:
+  //
+  // %t1 = OpULessThanEqual %bool %index %bound_minus_one
+  // %t2 = OpSelect %int_type %t1 %index %bound_minus_one
+  // %result = OpAccessChain %type %object ... %t2 ...
+
+  // The result id of an OpAccessChain or OpInBoundsAccessChain instruction.
+  uint32 access_chain_id = 1;
+
+  // A series of pairs of fresh ids, one per access chain index, for the results
+  // of a compare instruction and a select instruction, serving the roles of %t1
+  // and %t2 in the above example.
+  repeated UInt32Pair compare_and_select_ids = 2;
+
+}
+
+message LoopLimiterInfo {
+
+  // Structure capturing the information required to manipulate a loop limiter
+  // at a loop header.
+
+  // The header for the loop.
+  uint32 loop_header_id = 1;
+
+  // A fresh id into which the loop limiter's current value can be loaded.
+  uint32 load_id = 2;
+
+  // A fresh id that can be used to increment the loaded value by 1.
+  uint32 increment_id = 3;
+
+  // A fresh id that can be used to compare the loaded value with the loop
+  // limit.
+  uint32 compare_id = 4;
+
+  // A fresh id that can be used to compute the conjunction or disjunction of
+  // an original loop exit condition with |compare_id|, if the loop's back edge
+  // block can conditionally exit the loop.
+  uint32 logical_op_id = 5;
+
+  // A sequence of ids suitable for extending OpPhi instructions of the loop
+  // merge block if it did not previously have an incoming edge from the loop
+  // back edge block.
+  repeated uint32 phi_id = 6;
+
+}
+
 message TransformationSequence {
   repeated Transformation transformation = 1;
 }
@@ -190,6 +327,18 @@ message Transformation {
     TransformationSetMemoryOperandsMask set_memory_operands_mask = 20;
     TransformationCompositeExtract composite_extract = 21;
     TransformationVectorShuffle vector_shuffle = 22;
+    TransformationOutlineFunction outline_function = 23;
+    TransformationMergeBlocks merge_blocks = 24;
+    TransformationAddTypeVector add_type_vector = 25;
+    TransformationAddTypeArray add_type_array = 26;
+    TransformationAddTypeMatrix add_type_matrix = 27;
+    TransformationAddTypeStruct add_type_struct = 28;
+    TransformationAddTypeFunction add_type_function = 29;
+    TransformationAddConstantComposite add_constant_composite = 30;
+    TransformationAddGlobalVariable add_global_variable = 31;
+    TransformationAddGlobalUndef add_global_undef = 32;
+    TransformationAddFunction add_function = 33;
+    TransformationAddDeadBlock add_dead_block = 34;
     // Add additional option using the next available number.
   }
 }
@@ -206,9 +355,24 @@ message TransformationAddConstantBoolean {
 
 }
 
+message TransformationAddConstantComposite {
+
+  // Adds a constant of the given composite type to the module.
+
+  // Fresh id for the composite
+  uint32 fresh_id = 1;
+
+  // A composite type id
+  uint32 type_id = 2;
+
+  // Constituent ids for the composite
+  repeated uint32 constituent_id = 3;
+
+}
+
 message TransformationAddConstantScalar {
 
-  // Adds a constant of the given scalar type
+  // Adds a constant of the given scalar type.
 
   // Id for the constant
   uint32 fresh_id = 1;
@@ -221,6 +385,25 @@ message TransformationAddConstantScalar {
 
 }
 
+message TransformationAddDeadBlock {
+
+  // Adds a new block to the module that is statically reachable from an
+  // existing block, but dynamically unreachable.
+
+  // Fresh id for the dead block
+  uint32 fresh_id = 1;
+
+  // Id of an existing block terminated with OpBranch, such that this OpBranch
+  // can be replaced with an OpBranchConditional to its exiting successor or
+  // the dead block
+  uint32 existing_block = 2;
+
+  // Determines whether the condition associated with the OpBranchConditional
+  // is true or false
+  bool condition_value = 3;
+
+}
+
 message TransformationAddDeadBreak {
 
   // A transformation that turns a basic block that unconditionally branches to
@@ -262,6 +445,77 @@ message TransformationAddDeadContinue {
 
 }
 
+message TransformationAddFunction {
+
+  // Adds a SPIR-V function to the module.
+
+  // The series of instructions that comprise the function.
+  repeated Instruction instruction = 1;
+
+  // True if and only if the given function should be made livesafe (see
+  // FactFunctionIsLivesafe for definition).
+  bool is_livesafe = 2;
+
+  // Fresh id for a new variable that will serve as a "loop limiter" for the
+  // function; only relevant if |is_livesafe| holds.
+  uint32 loop_limiter_variable_id = 3;
+
+  // Id of an existing unsigned integer constant providing the maximum value
+  // that the loop limiter can reach before the loop is broken from; only
+  // relevant if |is_livesafe| holds.
+  uint32 loop_limit_constant_id = 4;
+
+  // Fresh ids for each loop in the function that allow the loop limiter to be
+  // manipulated; only relevant if |is_livesafe| holds.
+  repeated LoopLimiterInfo loop_limiter_info = 5;
+
+  // Id of an existing global value with the same return type as the function
+  // that can be used to replace OpKill and OpReachable instructions with
+  // ReturnValue instructions.  Ignored if the function has void return type.
+  uint32 kill_unreachable_return_value_id = 6;
+
+  // A mapping (represented as a sequence) from every access chain result id in
+  // the function to the ids required to clamp its indices to ensure they are in
+  // bounds.
+  repeated AccessChainClampingInfo access_chain_clamping_info = 7;
+
+}
+
+message TransformationAddGlobalUndef {
+
+  // Adds an undefined value of a given type to the module at global scope.
+
+  // Fresh id for the undefined value
+  uint32 fresh_id = 1;
+
+  // The type of the undefined value
+  uint32 type_id = 2;
+
+}
+
+message TransformationAddGlobalVariable {
+
+  // Adds a global variable of the given type to the module, with Private
+  // storage class and optionally with an initializer.
+
+  // Fresh id for the global variable
+  uint32 fresh_id = 1;
+
+  // The type of the global variable
+  uint32 type_id = 2;
+
+  // Optional initializer; 0 if there is no initializer
+  uint32 initializer_id = 3;
+
+  // True if and only if the value of the variable should be regarded, in
+  // general, as totally unknown and subject to change (even if, due to an
+  // initializer, the original value is known).  This is the case for variables
+  // added when a module is donated, for example, and means that stores to such
+  // variables can be performed in an arbitrary fashion.
+  bool value_is_arbitrary = 4;
+
+}
+
 message TransformationAddNoContractionDecoration {
 
   // Applies OpDecorate NoContraction to the given result id
@@ -271,6 +525,21 @@ message TransformationAddNoContractionDecoration {
 
 }
 
+message TransformationAddTypeArray {
+
+  // Adds an array type of the given element type and size to the module
+
+  // Fresh id for the array type
+  uint32 fresh_id = 1;
+
+  // The array's element type
+  uint32 element_type_id = 2;
+
+  // The array's size
+  uint32 size_id = 3;
+
+}
+
 message TransformationAddTypeBoolean {
 
   // Adds OpTypeBool to the module
@@ -292,6 +561,21 @@ message TransformationAddTypeFloat {
 
 }
 
+message TransformationAddTypeFunction {
+
+  // Adds a function type to the module
+
+  // Fresh id for the function type
+  uint32 fresh_id = 1;
+
+  // The function's return type
+  uint32 return_type_id = 2;
+
+  // The function's argument types
+  repeated uint32 argument_type_id = 3;
+
+}
+
 message TransformationAddTypeInt {
 
   // Adds OpTypeInt to the module with the given width and signedness
@@ -307,6 +591,22 @@ message TransformationAddTypeInt {
 
 }
 
+message TransformationAddTypeMatrix {
+
+  // Adds a matrix type to the module
+
+  // Fresh id for the matrix type
+  uint32 fresh_id = 1;
+
+  // The matrix's column type, which must be a floating-point vector (as per
+  // the "data rules" in the SPIR-V specification).
+  uint32 column_type_id = 2;
+
+  // The matrix's column count
+  uint32 column_count = 3;
+
+}
+
 message TransformationAddTypePointer {
 
   // Adds OpTypePointer to the module, with the given storage class and base
@@ -323,6 +623,33 @@ message TransformationAddTypePointer {
 
 }
 
+message TransformationAddTypeStruct {
+
+  // Adds a struct type to the module
+
+  // Fresh id for the struct type
+  uint32 fresh_id = 1;
+
+  // The struct's member types
+  repeated uint32 member_type_id = 3;
+
+}
+
+message TransformationAddTypeVector {
+
+  // Adds a vector type to the module
+
+  // Fresh id for the vector type
+  uint32 fresh_id = 1;
+
+  // The vector's component type
+  uint32 component_type_id = 2;
+
+  // The vector's component count
+  uint32 component_count = 3;
+
+}
+
 message TransformationCompositeConstruct {
 
   // A transformation that introduces an OpCompositeConstruct instruction to
@@ -380,6 +707,16 @@ message TransformationCopyObject {
 
 }
 
+message TransformationMergeBlocks {
+
+  // A transformation that merges a block with its predecessor.
+
+  // The id of the block that is to be merged with its predecessor; the merged
+  // block will have the *predecessor's* id.
+  uint32 block_id = 1;
+
+}
+
 message TransformationMoveBlockDown {
 
   // A transformation that moves a basic block to be one position lower in
@@ -389,6 +726,53 @@ message TransformationMoveBlockDown {
   uint32 block_id = 1;
 }
 
+message TransformationOutlineFunction {
+
+  // A transformation that outlines a single-entry single-exit region of a
+  // control flow graph into a separate function, and replaces the region with
+  // a call to that function.
+
+  // Id of the entry block of the single-entry single-exit region to be outlined
+  uint32 entry_block = 1;
+
+  // Id of the exit block of the single-entry single-exit region to be outlined
+  uint32 exit_block = 2;
+
+  // Id of a struct that will store the return values of the new function
+  uint32 new_function_struct_return_type_id = 3;
+
+  // A fresh id for the type of the outlined function
+  uint32 new_function_type_id = 4;
+
+  // A fresh id for the outlined function itself
+  uint32 new_function_id = 5;
+
+  // A fresh id to represent the block in the outlined function that represents
+  // the first block of the outlined region.
+  uint32 new_function_region_entry_block = 6;
+
+  // A fresh id for the result of the OpFunctionCall instruction that will call
+  // the outlined function
+  uint32 new_caller_result_id = 7;
+
+  // A fresh id to capture the return value of the outlined function - the
+  // argument to OpReturn
+  uint32 new_callee_result_id = 8;
+
+  // Ids defined outside the region and used inside the region will become
+  // parameters to the outlined function.  This is a mapping from used ids to
+  // fresh parameter ids.
+  repeated UInt32Pair input_id_to_fresh_id = 9;
+
+  // Ids defined inside the region and used outside the region will become
+  // fresh ids defined by the outlined function, which get copied into the
+  // function's struct return value and then copied into their destination ids
+  // by the caller.  This is a mapping from original ids to corresponding fresh
+  // ids.
+  repeated UInt32Pair output_id_to_fresh_id = 10;
+
+}
+
 message TransformationReplaceBooleanConstantWithConstantBinary {
 
   // A transformation to capture replacing a use of a boolean constant with

+ 55 - 0
3rdparty/spirv-tools/source/fuzz/transformation.cpp

@@ -16,19 +16,32 @@
 
 #include <cassert>
 
+#include "source/fuzz/fuzzer_util.h"
 #include "source/fuzz/transformation_add_constant_boolean.h"
+#include "source/fuzz/transformation_add_constant_composite.h"
 #include "source/fuzz/transformation_add_constant_scalar.h"
+#include "source/fuzz/transformation_add_dead_block.h"
 #include "source/fuzz/transformation_add_dead_break.h"
 #include "source/fuzz/transformation_add_dead_continue.h"
+#include "source/fuzz/transformation_add_function.h"
+#include "source/fuzz/transformation_add_global_undef.h"
+#include "source/fuzz/transformation_add_global_variable.h"
 #include "source/fuzz/transformation_add_no_contraction_decoration.h"
+#include "source/fuzz/transformation_add_type_array.h"
 #include "source/fuzz/transformation_add_type_boolean.h"
 #include "source/fuzz/transformation_add_type_float.h"
+#include "source/fuzz/transformation_add_type_function.h"
 #include "source/fuzz/transformation_add_type_int.h"
+#include "source/fuzz/transformation_add_type_matrix.h"
 #include "source/fuzz/transformation_add_type_pointer.h"
+#include "source/fuzz/transformation_add_type_struct.h"
+#include "source/fuzz/transformation_add_type_vector.h"
 #include "source/fuzz/transformation_composite_construct.h"
 #include "source/fuzz/transformation_composite_extract.h"
 #include "source/fuzz/transformation_copy_object.h"
+#include "source/fuzz/transformation_merge_blocks.h"
 #include "source/fuzz/transformation_move_block_down.h"
+#include "source/fuzz/transformation_outline_function.h"
 #include "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h"
 #include "source/fuzz/transformation_replace_constant_with_uniform.h"
 #include "source/fuzz/transformation_replace_id_with_synonym.h"
@@ -51,28 +64,52 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
     case protobufs::Transformation::TransformationCase::kAddConstantBoolean:
       return MakeUnique<TransformationAddConstantBoolean>(
           message.add_constant_boolean());
+    case protobufs::Transformation::TransformationCase::kAddConstantComposite:
+      return MakeUnique<TransformationAddConstantComposite>(
+          message.add_constant_composite());
     case protobufs::Transformation::TransformationCase::kAddConstantScalar:
       return MakeUnique<TransformationAddConstantScalar>(
           message.add_constant_scalar());
+    case protobufs::Transformation::TransformationCase::kAddDeadBlock:
+      return MakeUnique<TransformationAddDeadBlock>(message.add_dead_block());
     case protobufs::Transformation::TransformationCase::kAddDeadBreak:
       return MakeUnique<TransformationAddDeadBreak>(message.add_dead_break());
     case protobufs::Transformation::TransformationCase::kAddDeadContinue:
       return MakeUnique<TransformationAddDeadContinue>(
           message.add_dead_continue());
+    case protobufs::Transformation::TransformationCase::kAddFunction:
+      return MakeUnique<TransformationAddFunction>(message.add_function());
+    case protobufs::Transformation::TransformationCase::kAddGlobalUndef:
+      return MakeUnique<TransformationAddGlobalUndef>(
+          message.add_global_undef());
+    case protobufs::Transformation::TransformationCase::kAddGlobalVariable:
+      return MakeUnique<TransformationAddGlobalVariable>(
+          message.add_global_variable());
     case protobufs::Transformation::TransformationCase::
         kAddNoContractionDecoration:
       return MakeUnique<TransformationAddNoContractionDecoration>(
           message.add_no_contraction_decoration());
+    case protobufs::Transformation::TransformationCase::kAddTypeArray:
+      return MakeUnique<TransformationAddTypeArray>(message.add_type_array());
     case protobufs::Transformation::TransformationCase::kAddTypeBoolean:
       return MakeUnique<TransformationAddTypeBoolean>(
           message.add_type_boolean());
     case protobufs::Transformation::TransformationCase::kAddTypeFloat:
       return MakeUnique<TransformationAddTypeFloat>(message.add_type_float());
+    case protobufs::Transformation::TransformationCase::kAddTypeFunction:
+      return MakeUnique<TransformationAddTypeFunction>(
+          message.add_type_function());
     case protobufs::Transformation::TransformationCase::kAddTypeInt:
       return MakeUnique<TransformationAddTypeInt>(message.add_type_int());
+    case protobufs::Transformation::TransformationCase::kAddTypeMatrix:
+      return MakeUnique<TransformationAddTypeMatrix>(message.add_type_matrix());
     case protobufs::Transformation::TransformationCase::kAddTypePointer:
       return MakeUnique<TransformationAddTypePointer>(
           message.add_type_pointer());
+    case protobufs::Transformation::TransformationCase::kAddTypeStruct:
+      return MakeUnique<TransformationAddTypeStruct>(message.add_type_struct());
+    case protobufs::Transformation::TransformationCase::kAddTypeVector:
+      return MakeUnique<TransformationAddTypeVector>(message.add_type_vector());
     case protobufs::Transformation::TransformationCase::kCompositeConstruct:
       return MakeUnique<TransformationCompositeConstruct>(
           message.composite_construct());
@@ -81,8 +118,13 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
           message.composite_extract());
     case protobufs::Transformation::TransformationCase::kCopyObject:
       return MakeUnique<TransformationCopyObject>(message.copy_object());
+    case protobufs::Transformation::TransformationCase::kMergeBlocks:
+      return MakeUnique<TransformationMergeBlocks>(message.merge_blocks());
     case protobufs::Transformation::TransformationCase::kMoveBlockDown:
       return MakeUnique<TransformationMoveBlockDown>(message.move_block_down());
+    case protobufs::Transformation::TransformationCase::kOutlineFunction:
+      return MakeUnique<TransformationOutlineFunction>(
+          message.outline_function());
     case protobufs::Transformation::TransformationCase::
         kReplaceBooleanConstantWithConstantBinary:
       return MakeUnique<TransformationReplaceBooleanConstantWithConstantBinary>(
@@ -118,5 +160,18 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
   return nullptr;
 }
 
+bool Transformation::CheckIdIsFreshAndNotUsedByThisTransformation(
+    uint32_t id, opt::IRContext* context,
+    std::set<uint32_t>* ids_used_by_this_transformation) {
+  if (!fuzzerutil::IsFreshId(context, id)) {
+    return false;
+  }
+  if (ids_used_by_this_transformation->count(id) != 0) {
+    return false;
+  }
+  ids_used_by_this_transformation->insert(id);
+  return true;
+}
+
 }  // namespace fuzz
 }  // namespace spvtools

+ 9 - 0
3rdparty/spirv-tools/source/fuzz/transformation.h

@@ -83,6 +83,15 @@ class Transformation {
   // representation of a transformation given by |message|.
   static std::unique_ptr<Transformation> FromMessage(
       const protobufs::Transformation& message);
+
+  // Helper that returns true if and only if (a) |id| is a fresh id for the
+  // module, and (b) |id| is not in |ids_used_by_this_transformation|, a set of
+  // ids already known to be in use by a transformation.  This is useful when
+  // checking id freshness for a transformation that uses many ids, all of which
+  // must be distinct.
+  static bool CheckIdIsFreshAndNotUsedByThisTransformation(
+      uint32_t id, opt::IRContext* context,
+      std::set<uint32_t>* ids_used_by_this_transformation);
 };
 
 }  // namespace fuzz

+ 130 - 0
3rdparty/spirv-tools/source/fuzz/transformation_add_constant_composite.cpp

@@ -0,0 +1,130 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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.
+
+#include "source/fuzz/transformation_add_constant_composite.h"
+
+#include <vector>
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAddConstantComposite::TransformationAddConstantComposite(
+    const spvtools::fuzz::protobufs::TransformationAddConstantComposite&
+        message)
+    : message_(message) {}
+
+TransformationAddConstantComposite::TransformationAddConstantComposite(
+    uint32_t fresh_id, uint32_t type_id,
+    const std::vector<uint32_t>& constituent_ids) {
+  message_.set_fresh_id(fresh_id);
+  message_.set_type_id(type_id);
+  for (auto constituent_id : constituent_ids) {
+    message_.add_constituent_id(constituent_id);
+  }
+}
+
+bool TransformationAddConstantComposite::IsApplicable(
+    opt::IRContext* context,
+    const spvtools::fuzz::FactManager& /*unused*/) const {
+  // Check that the given id is fresh.
+  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+    return false;
+  }
+  // Check that the composite type id is an instruction id.
+  auto composite_type_instruction =
+      context->get_def_use_mgr()->GetDef(message_.type_id());
+  if (!composite_type_instruction) {
+    return false;
+  }
+  // Gather up the operands for the composite constant, in the process checking
+  // whether the given type really defines a composite.
+  std::vector<uint32_t> constituent_type_ids;
+  switch (composite_type_instruction->opcode()) {
+    case SpvOpTypeArray:
+      for (uint32_t index = 0;
+           index <
+           fuzzerutil::GetArraySize(*composite_type_instruction, context);
+           index++) {
+        constituent_type_ids.push_back(
+            composite_type_instruction->GetSingleWordInOperand(0));
+      }
+      break;
+    case SpvOpTypeMatrix:
+    case SpvOpTypeVector:
+      for (uint32_t index = 0;
+           index < composite_type_instruction->GetSingleWordInOperand(1);
+           index++) {
+        constituent_type_ids.push_back(
+            composite_type_instruction->GetSingleWordInOperand(0));
+      }
+      break;
+    case SpvOpTypeStruct:
+      composite_type_instruction->ForEachInOperand(
+          [&constituent_type_ids](const uint32_t* member_type_id) {
+            constituent_type_ids.push_back(*member_type_id);
+          });
+      break;
+    default:
+      // Not a composite type.
+      return false;
+  }
+
+  // Check that the number of provided operands matches the number of
+  // constituents required by the type.
+  if (constituent_type_ids.size() !=
+      static_cast<uint32_t>(message_.constituent_id().size())) {
+    return false;
+  }
+
+  // Check that every provided operand refers to an instruction of the
+  // corresponding constituent type.
+  for (uint32_t index = 0; index < constituent_type_ids.size(); index++) {
+    auto constituent_instruction =
+        context->get_def_use_mgr()->GetDef(message_.constituent_id(index));
+    if (!constituent_instruction) {
+      return false;
+    }
+    if (constituent_instruction->type_id() != constituent_type_ids.at(index)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+void TransformationAddConstantComposite::Apply(
+    opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
+  opt::Instruction::OperandList in_operands;
+  for (auto constituent_id : message_.constituent_id()) {
+    in_operands.push_back({SPV_OPERAND_TYPE_ID, {constituent_id}});
+  }
+  context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
+      context, SpvOpConstantComposite, message_.type_id(), message_.fresh_id(),
+      in_operands));
+  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+  // We have added an instruction to the module, so need to be careful about the
+  // validity of existing analyses.
+  context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationAddConstantComposite::ToMessage()
+    const {
+  protobufs::Transformation result;
+  *result.mutable_add_constant_composite() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 58 - 0
3rdparty/spirv-tools/source/fuzz/transformation_add_constant_composite.h

@@ -0,0 +1,58 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_CONSTANT_COMPOSITE_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ADD_CONSTANT_COMPOSITE_H_
+
+#include <vector>
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationAddConstantComposite : public Transformation {
+ public:
+  explicit TransformationAddConstantComposite(
+      const protobufs::TransformationAddConstantComposite& message);
+
+  TransformationAddConstantComposite(
+      uint32_t fresh_id, uint32_t type_id,
+      const std::vector<uint32_t>& constituent_ids);
+
+  // - |message_.fresh_id| must be a fresh id
+  // - |message_.type_id| must be the id of a composite type
+  // - |message_.constituent_id| must refer to ids that match the constituent
+  //   types of this composite type
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // Adds an OpConstantComposite instruction defining a constant of type
+  // |message_.type_id|, using |message_.constituent_id| as constituents, with
+  // result id |message_.fresh_id|.
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationAddConstantComposite message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_ADD_CONSTANT_COMPOSITE_H_

+ 169 - 0
3rdparty/spirv-tools/source/fuzz/transformation_add_dead_block.cpp

@@ -0,0 +1,169 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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.
+
+#include "source/fuzz/transformation_add_dead_block.h"
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAddDeadBlock::TransformationAddDeadBlock(
+    const spvtools::fuzz::protobufs::TransformationAddDeadBlock& message)
+    : message_(message) {}
+
+TransformationAddDeadBlock::TransformationAddDeadBlock(uint32_t fresh_id,
+                                                       uint32_t existing_block,
+                                                       bool condition_value) {
+  message_.set_fresh_id(fresh_id);
+  message_.set_existing_block(existing_block);
+  message_.set_condition_value(condition_value);
+}
+
+bool TransformationAddDeadBlock::IsApplicable(
+    opt::IRContext* context,
+    const spvtools::fuzz::FactManager& /*unused*/) const {
+  // The new block's id must be fresh.
+  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+    return false;
+  }
+
+  // First, we check that a constant with the same value as
+  // |message_.condition_value| is present.
+  if (!fuzzerutil::MaybeGetBoolConstantId(context,
+                                          message_.condition_value())) {
+    // The required constant is not present, so the transformation cannot be
+    // applied.
+    return false;
+  }
+
+  // The existing block must indeed exist.
+  auto existing_block =
+      fuzzerutil::MaybeFindBlock(context, message_.existing_block());
+  if (!existing_block) {
+    return false;
+  }
+
+  // It must not head a loop.
+  if (existing_block->IsLoopHeader()) {
+    return false;
+  }
+
+  // It must end with OpBranch.
+  if (existing_block->terminator()->opcode() != SpvOpBranch) {
+    return false;
+  }
+
+  // Its successor must not be a merge block nor continue target.
+  auto successor_block_id =
+      existing_block->terminator()->GetSingleWordInOperand(0);
+  if (fuzzerutil::IsMergeOrContinue(context, successor_block_id)) {
+    return false;
+  }
+
+  // The successor must not be a loop header (i.e., |message_.existing_block|
+  // must not be a back-edge block.
+  if (context->cfg()->block(successor_block_id)->IsLoopHeader()) {
+    return false;
+  }
+
+  return true;
+}
+
+void TransformationAddDeadBlock::Apply(
+    opt::IRContext* context, spvtools::fuzz::FactManager* fact_manager) const {
+  // Update the module id bound so that it is at least the id of the new block.
+  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+
+  // Get the existing block and its successor.
+  auto existing_block = context->cfg()->block(message_.existing_block());
+  auto successor_block_id =
+      existing_block->terminator()->GetSingleWordInOperand(0);
+
+  // Get the id of the boolean value that will be used as the branch condition.
+  auto bool_id =
+      fuzzerutil::MaybeGetBoolConstantId(context, message_.condition_value());
+
+  // Make a new block that unconditionally branches to the original successor
+  // block.
+  auto enclosing_function = existing_block->GetParent();
+  std::unique_ptr<opt::BasicBlock> new_block = MakeUnique<opt::BasicBlock>(
+      MakeUnique<opt::Instruction>(context, SpvOpLabel, 0, message_.fresh_id(),
+                                   opt::Instruction::OperandList()));
+  new_block->AddInstruction(MakeUnique<opt::Instruction>(
+      context, SpvOpBranch, 0, 0,
+      opt::Instruction::OperandList(
+          {{SPV_OPERAND_TYPE_ID, {successor_block_id}}})));
+
+  // Turn the original block into a selection merge, with its original successor
+  // as the merge block.
+  existing_block->terminator()->InsertBefore(MakeUnique<opt::Instruction>(
+      context, SpvOpSelectionMerge, 0, 0,
+      opt::Instruction::OperandList(
+          {{SPV_OPERAND_TYPE_ID, {successor_block_id}},
+           {SPV_OPERAND_TYPE_SELECTION_CONTROL,
+            {SpvSelectionControlMaskNone}}})));
+
+  // Change the original block's terminator to be a conditional branch on the
+  // given boolean, with the original successor and the new successor as branch
+  // targets, and such that at runtime control will always transfer to the
+  // original successor.
+  existing_block->terminator()->SetOpcode(SpvOpBranchConditional);
+  existing_block->terminator()->SetInOperands(
+      {{SPV_OPERAND_TYPE_ID, {bool_id}},
+       {SPV_OPERAND_TYPE_ID,
+        {message_.condition_value() ? successor_block_id
+                                    : message_.fresh_id()}},
+       {SPV_OPERAND_TYPE_ID,
+        {message_.condition_value() ? message_.fresh_id()
+                                    : successor_block_id}}});
+
+  // Add the new block to the enclosing function.
+  new_block->SetParent(enclosing_function);
+  enclosing_function->InsertBasicBlockAfter(std::move(new_block),
+                                            existing_block);
+
+  // Record the fact that the new block is dead.
+  fact_manager->AddFactBlockIsDead(message_.fresh_id());
+
+  // Fix up OpPhi instructions in the successor block, so that the values they
+  // yield when control has transferred from the new block are the same as if
+  // control had transferred from |message_.existing_block|.  This is guaranteed
+  // to be valid since |message_.existing_block| dominates the new block by
+  // construction.  Other transformations can change these phi operands to more
+  // interesting values.
+  context->cfg()
+      ->block(successor_block_id)
+      ->ForEachPhiInst([this](opt::Instruction* phi_inst) {
+        // Copy the operand that provides the phi value for the first of any
+        // existing predecessors.
+        opt::Operand copy_of_existing_operand = phi_inst->GetInOperand(0);
+        // Use this as the value associated with the new predecessor.
+        phi_inst->AddOperand(std::move(copy_of_existing_operand));
+        phi_inst->AddOperand({SPV_OPERAND_TYPE_ID, {message_.fresh_id()}});
+      });
+
+  // Do not rely on any existing analysis results since the control flow graph
+  // of the module has changed.
+  context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationAddDeadBlock::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_add_dead_block() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 63 - 0
3rdparty/spirv-tools/source/fuzz/transformation_add_dead_block.h

@@ -0,0 +1,63 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_DEAD_BLOCK_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ADD_DEAD_BLOCK_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationAddDeadBlock : public Transformation {
+ public:
+  explicit TransformationAddDeadBlock(
+      const protobufs::TransformationAddDeadBlock& message);
+
+  TransformationAddDeadBlock(uint32_t fresh_id, uint32_t existing_block,
+                             bool condition_value);
+
+  // - |message_.fresh_id| must be a fresh id
+  // - A constant with the same value as |message_.condition_value| must be
+  //   available
+  // - |message_.existing_block| must be a block that is not a loop header,
+  //   and that ends with OpBranch to a block that is not a merge block nor
+  //   continue target - this is because the successor will become the merge
+  //   block of a selection construct headed at |message_.existing_block|
+  // - |message_.existing_block| must not be a back-edge block, since in this
+  //   case the newly-added block would lead to another back-edge to the
+  //   associated loop header
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // Changes the OpBranch from |message_.existing_block| to its successor 's'
+  // to an OpBranchConditional to either 's' or a new block,
+  // |message_.fresh_id|, which itself unconditionally branches to 's'.  The
+  // conditional branch uses |message.condition_value| as its condition, and is
+  // arranged so that control will pass to 's' at runtime.
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationAddDeadBlock message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_ADD_DEAD_BLOCK_H_

+ 2 - 9
3rdparty/spirv-tools/source/fuzz/transformation_add_dead_break.cpp

@@ -111,15 +111,8 @@ bool TransformationAddDeadBreak::IsApplicable(
     opt::IRContext* context, const FactManager& /*unused*/) const {
   // First, we check that a constant with the same value as
   // |message_.break_condition_value| is present.
-  opt::analysis::Bool bool_type;
-  auto registered_bool_type =
-      context->get_type_mgr()->GetRegisteredType(&bool_type);
-  if (!registered_bool_type) {
-    return false;
-  }
-  opt::analysis::BoolConstant bool_constant(registered_bool_type->AsBool(),
-                                            message_.break_condition_value());
-  if (!context->get_constant_mgr()->FindConstant(&bool_constant)) {
+  if (!fuzzerutil::MaybeGetBoolConstantId(context,
+                                          message_.break_condition_value())) {
     // The required constant is not present, so the transformation cannot be
     // applied.
     return false;

+ 2 - 9
3rdparty/spirv-tools/source/fuzz/transformation_add_dead_continue.cpp

@@ -37,15 +37,8 @@ bool TransformationAddDeadContinue::IsApplicable(
     opt::IRContext* context, const FactManager& /*unused*/) const {
   // First, we check that a constant with the same value as
   // |message_.continue_condition_value| is present.
-  opt::analysis::Bool bool_type;
-  auto registered_bool_type =
-      context->get_type_mgr()->GetRegisteredType(&bool_type);
-  if (!registered_bool_type) {
-    return false;
-  }
-  opt::analysis::BoolConstant bool_constant(
-      registered_bool_type->AsBool(), message_.continue_condition_value());
-  if (!context->get_constant_mgr()->FindConstant(&bool_constant)) {
+  if (!fuzzerutil::MaybeGetBoolConstantId(
+          context, message_.continue_condition_value())) {
     // The required constant is not present, so the transformation cannot be
     // applied.
     return false;

+ 921 - 0
3rdparty/spirv-tools/source/fuzz/transformation_add_function.cpp

@@ -0,0 +1,921 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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.
+
+#include "source/fuzz/transformation_add_function.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_message.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAddFunction::TransformationAddFunction(
+    const spvtools::fuzz::protobufs::TransformationAddFunction& message)
+    : message_(message) {}
+
+TransformationAddFunction::TransformationAddFunction(
+    const std::vector<protobufs::Instruction>& instructions) {
+  for (auto& instruction : instructions) {
+    *message_.add_instruction() = instruction;
+  }
+  message_.set_is_livesafe(false);
+}
+
+TransformationAddFunction::TransformationAddFunction(
+    const std::vector<protobufs::Instruction>& instructions,
+    uint32_t loop_limiter_variable_id, uint32_t loop_limit_constant_id,
+    const std::vector<protobufs::LoopLimiterInfo>& loop_limiters,
+    uint32_t kill_unreachable_return_value_id,
+    const std::vector<protobufs::AccessChainClampingInfo>&
+        access_chain_clampers) {
+  for (auto& instruction : instructions) {
+    *message_.add_instruction() = instruction;
+  }
+  message_.set_is_livesafe(true);
+  message_.set_loop_limiter_variable_id(loop_limiter_variable_id);
+  message_.set_loop_limit_constant_id(loop_limit_constant_id);
+  for (auto& loop_limiter : loop_limiters) {
+    *message_.add_loop_limiter_info() = loop_limiter;
+  }
+  message_.set_kill_unreachable_return_value_id(
+      kill_unreachable_return_value_id);
+  for (auto& access_clamper : access_chain_clampers) {
+    *message_.add_access_chain_clamping_info() = access_clamper;
+  }
+}
+
+bool TransformationAddFunction::IsApplicable(
+    opt::IRContext* context,
+    const spvtools::fuzz::FactManager& fact_manager) const {
+  // This transformation may use a lot of ids, all of which need to be fresh
+  // and distinct.  This set tracks them.
+  std::set<uint32_t> ids_used_by_this_transformation;
+
+  // Ensure that all result ids in the new function are fresh and distinct.
+  for (auto& instruction : message_.instruction()) {
+    if (instruction.result_id()) {
+      if (!CheckIdIsFreshAndNotUsedByThisTransformation(
+              instruction.result_id(), context,
+              &ids_used_by_this_transformation)) {
+        return false;
+      }
+    }
+  }
+
+  if (message_.is_livesafe()) {
+    // Ensure that all ids provided for making the function livesafe are fresh
+    // and distinct.
+    if (!CheckIdIsFreshAndNotUsedByThisTransformation(
+            message_.loop_limiter_variable_id(), context,
+            &ids_used_by_this_transformation)) {
+      return false;
+    }
+    for (auto& loop_limiter_info : message_.loop_limiter_info()) {
+      if (!CheckIdIsFreshAndNotUsedByThisTransformation(
+              loop_limiter_info.load_id(), context,
+              &ids_used_by_this_transformation)) {
+        return false;
+      }
+      if (!CheckIdIsFreshAndNotUsedByThisTransformation(
+              loop_limiter_info.increment_id(), context,
+              &ids_used_by_this_transformation)) {
+        return false;
+      }
+      if (!CheckIdIsFreshAndNotUsedByThisTransformation(
+              loop_limiter_info.compare_id(), context,
+              &ids_used_by_this_transformation)) {
+        return false;
+      }
+      if (!CheckIdIsFreshAndNotUsedByThisTransformation(
+              loop_limiter_info.logical_op_id(), context,
+              &ids_used_by_this_transformation)) {
+        return false;
+      }
+    }
+    for (auto& access_chain_clamping_info :
+         message_.access_chain_clamping_info()) {
+      for (auto& pair : access_chain_clamping_info.compare_and_select_ids()) {
+        if (!CheckIdIsFreshAndNotUsedByThisTransformation(
+                pair.first(), context, &ids_used_by_this_transformation)) {
+          return false;
+        }
+        if (!CheckIdIsFreshAndNotUsedByThisTransformation(
+                pair.second(), context, &ids_used_by_this_transformation)) {
+          return false;
+        }
+      }
+    }
+  }
+
+  // Because checking all the conditions for a function to be valid is a big
+  // job that the SPIR-V validator can already do, a "try it and see" approach
+  // is taken here.
+
+  // We first clone the current module, so that we can try adding the new
+  // function without risking wrecking |context|.
+  auto cloned_module = fuzzerutil::CloneIRContext(context);
+
+  // We try to add a function to the cloned module, which may fail if
+  // |message_.instruction| is not sufficiently well-formed.
+  if (!TryToAddFunction(cloned_module.get())) {
+    return false;
+  }
+
+  if (message_.is_livesafe()) {
+    // We make the cloned module livesafe.
+    if (!TryToMakeFunctionLivesafe(cloned_module.get(), fact_manager)) {
+      return false;
+    }
+  }
+
+  // Having managed to add the new function to the cloned module, and
+  // potentially also made it livesafe, we ascertain whether the cloned module
+  // is still valid.  If it is, the transformation is applicable.
+  return fuzzerutil::IsValid(cloned_module.get());
+}
+
+void TransformationAddFunction::Apply(
+    opt::IRContext* context, spvtools::fuzz::FactManager* fact_manager) const {
+  // Add the function to the module.  As the transformation is applicable, this
+  // should succeed.
+  bool success = TryToAddFunction(context);
+  assert(success && "The function should be successfully added.");
+  (void)(success);  // Keep release builds happy (otherwise they may complain
+                    // that |success| is not used).
+
+  // Record the fact that all pointer parameters and variables declared in the
+  // function should be regarded as having arbitrary values.  This allows other
+  // passes to store arbitrarily to such variables, and to pass them freely as
+  // parameters to other functions knowing that it is OK if they get
+  // over-written.
+  for (auto& instruction : message_.instruction()) {
+    switch (instruction.opcode()) {
+      case SpvOpFunctionParameter:
+        if (context->get_def_use_mgr()
+                ->GetDef(instruction.result_type_id())
+                ->opcode() == SpvOpTypePointer) {
+          fact_manager->AddFactValueOfVariableIsArbitrary(
+              instruction.result_id());
+        }
+        break;
+      case SpvOpVariable:
+        fact_manager->AddFactValueOfVariableIsArbitrary(
+            instruction.result_id());
+        break;
+      default:
+        break;
+    }
+  }
+
+  if (message_.is_livesafe()) {
+    // Make the function livesafe, which also should succeed.
+    success = TryToMakeFunctionLivesafe(context, *fact_manager);
+    assert(success && "It should be possible to make the function livesafe.");
+    (void)(success);  // Keep release builds happy.
+
+    // Inform the fact manager that the function is livesafe.
+    assert(message_.instruction(0).opcode() == SpvOpFunction &&
+           "The first instruction of an 'add function' transformation must be "
+           "OpFunction.");
+    fact_manager->AddFactFunctionIsLivesafe(
+        message_.instruction(0).result_id());
+  } else {
+    // Inform the fact manager that all blocks in the function are dead.
+    for (auto& inst : message_.instruction()) {
+      if (inst.opcode() == SpvOpLabel) {
+        fact_manager->AddFactBlockIsDead(inst.result_id());
+      }
+    }
+  }
+  context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationAddFunction::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_add_function() = message_;
+  return result;
+}
+
+bool TransformationAddFunction::TryToAddFunction(
+    opt::IRContext* context) const {
+  // This function returns false if |message_.instruction| was not well-formed
+  // enough to actually create a function and add it to |context|.
+
+  // A function must have at least some instructions.
+  if (message_.instruction().empty()) {
+    return false;
+  }
+
+  // A function must start with OpFunction.
+  auto function_begin = message_.instruction(0);
+  if (function_begin.opcode() != SpvOpFunction) {
+    return false;
+  }
+
+  // Make a function, headed by the OpFunction instruction.
+  std::unique_ptr<opt::Function> new_function = MakeUnique<opt::Function>(
+      InstructionFromMessage(context, function_begin));
+
+  // Keeps track of which instruction protobuf message we are currently
+  // considering.
+  uint32_t instruction_index = 1;
+  const auto num_instructions =
+      static_cast<uint32_t>(message_.instruction().size());
+
+  // Iterate through all function parameter instructions, adding parameters to
+  // the new function.
+  while (instruction_index < num_instructions &&
+         message_.instruction(instruction_index).opcode() ==
+             SpvOpFunctionParameter) {
+    new_function->AddParameter(InstructionFromMessage(
+        context, message_.instruction(instruction_index)));
+    instruction_index++;
+  }
+
+  // After the parameters, there needs to be a label.
+  if (instruction_index == num_instructions ||
+      message_.instruction(instruction_index).opcode() != SpvOpLabel) {
+    return false;
+  }
+
+  // Iterate through the instructions block by block until the end of the
+  // function is reached.
+  while (instruction_index < num_instructions &&
+         message_.instruction(instruction_index).opcode() != SpvOpFunctionEnd) {
+    // Invariant: we should always be at a label instruction at this point.
+    assert(message_.instruction(instruction_index).opcode() == SpvOpLabel);
+
+    // Make a basic block using the label instruction, with the new function
+    // as its parent.
+    std::unique_ptr<opt::BasicBlock> block =
+        MakeUnique<opt::BasicBlock>(InstructionFromMessage(
+            context, message_.instruction(instruction_index)));
+    block->SetParent(new_function.get());
+
+    // Consider successive instructions until we hit another label or the end
+    // of the function, adding each such instruction to the block.
+    instruction_index++;
+    while (instruction_index < num_instructions &&
+           message_.instruction(instruction_index).opcode() !=
+               SpvOpFunctionEnd &&
+           message_.instruction(instruction_index).opcode() != SpvOpLabel) {
+      block->AddInstruction(InstructionFromMessage(
+          context, message_.instruction(instruction_index)));
+      instruction_index++;
+    }
+    // Add the block to the new function.
+    new_function->AddBasicBlock(std::move(block));
+  }
+  // Having considered all the blocks, we should be at the last instruction and
+  // it needs to be OpFunctionEnd.
+  if (instruction_index != num_instructions - 1 ||
+      message_.instruction(instruction_index).opcode() != SpvOpFunctionEnd) {
+    return false;
+  }
+  // Set the function's final instruction, add the function to the module and
+  // report success.
+  new_function->SetFunctionEnd(
+      InstructionFromMessage(context, message_.instruction(instruction_index)));
+  context->AddFunction(std::move(new_function));
+
+  context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+
+  return true;
+}
+
+bool TransformationAddFunction::TryToMakeFunctionLivesafe(
+    opt::IRContext* context, const FactManager& fact_manager) const {
+  assert(message_.is_livesafe() && "Precondition: is_livesafe must hold.");
+
+  // Get a pointer to the added function.
+  opt::Function* added_function = nullptr;
+  for (auto& function : *context->module()) {
+    if (function.result_id() == message_.instruction(0).result_id()) {
+      added_function = &function;
+      break;
+    }
+  }
+  assert(added_function && "The added function should have been found.");
+
+  if (!TryToAddLoopLimiters(context, added_function)) {
+    // Adding loop limiters did not work; bail out.
+    return false;
+  }
+
+  // Consider all the instructions in the function, and:
+  // - attempt to replace OpKill and OpUnreachable with return instructions
+  // - attempt to clamp access chains to be within bounds
+  // - check that OpFunctionCall instructions are only to livesafe functions
+  for (auto& block : *added_function) {
+    for (auto& inst : block) {
+      switch (inst.opcode()) {
+        case SpvOpKill:
+        case SpvOpUnreachable:
+          if (!TryToTurnKillOrUnreachableIntoReturn(context, added_function,
+                                                    &inst)) {
+            return false;
+          }
+          break;
+        case SpvOpAccessChain:
+        case SpvOpInBoundsAccessChain:
+          if (!TryToClampAccessChainIndices(context, &inst)) {
+            return false;
+          }
+          break;
+        case SpvOpFunctionCall:
+          // A livesafe function my only call other livesafe functions.
+          if (!fact_manager.FunctionIsLivesafe(
+                  inst.GetSingleWordInOperand(0))) {
+            return false;
+          }
+        default:
+          break;
+      }
+    }
+  }
+  return true;
+}
+
+bool TransformationAddFunction::TryToAddLoopLimiters(
+    opt::IRContext* context, opt::Function* added_function) const {
+  // Collect up all the loop headers so that we can subsequently add loop
+  // limiting logic.
+  std::vector<opt::BasicBlock*> loop_headers;
+  for (auto& block : *added_function) {
+    if (block.IsLoopHeader()) {
+      loop_headers.push_back(&block);
+    }
+  }
+
+  if (loop_headers.empty()) {
+    // There are no loops, so no need to add any loop limiters.
+    return true;
+  }
+
+  // Check that the module contains appropriate ingredients for declaring and
+  // manipulating a loop limiter.
+
+  auto loop_limit_constant_id_instr =
+      context->get_def_use_mgr()->GetDef(message_.loop_limit_constant_id());
+  if (!loop_limit_constant_id_instr ||
+      loop_limit_constant_id_instr->opcode() != SpvOpConstant) {
+    // The loop limit constant id instruction must exist and have an
+    // appropriate opcode.
+    return false;
+  }
+
+  auto loop_limit_type = context->get_def_use_mgr()->GetDef(
+      loop_limit_constant_id_instr->type_id());
+  if (loop_limit_type->opcode() != SpvOpTypeInt ||
+      loop_limit_type->GetSingleWordInOperand(0) != 32) {
+    // The type of the loop limit constant must be 32-bit integer.  It
+    // doesn't actually matter whether the integer is signed or not.
+    return false;
+  }
+
+  // Find the id of the "unsigned int" type.
+  opt::analysis::Integer unsigned_int_type(32, false);
+  uint32_t unsigned_int_type_id =
+      context->get_type_mgr()->GetId(&unsigned_int_type);
+  if (!unsigned_int_type_id) {
+    // Unsigned int is not available; we need this type in order to add loop
+    // limiters.
+    return false;
+  }
+  auto registered_unsigned_int_type =
+      context->get_type_mgr()->GetRegisteredType(&unsigned_int_type);
+
+  // Look for 0 of type unsigned int.
+  opt::analysis::IntConstant zero(registered_unsigned_int_type->AsInteger(),
+                                  {0});
+  auto registered_zero = context->get_constant_mgr()->FindConstant(&zero);
+  if (!registered_zero) {
+    // We need 0 in order to be able to initialize loop limiters.
+    return false;
+  }
+  uint32_t zero_id = context->get_constant_mgr()
+                         ->GetDefiningInstruction(registered_zero)
+                         ->result_id();
+
+  // Look for 1 of type unsigned int.
+  opt::analysis::IntConstant one(registered_unsigned_int_type->AsInteger(),
+                                 {1});
+  auto registered_one = context->get_constant_mgr()->FindConstant(&one);
+  if (!registered_one) {
+    // We need 1 in order to be able to increment loop limiters.
+    return false;
+  }
+  uint32_t one_id = context->get_constant_mgr()
+                        ->GetDefiningInstruction(registered_one)
+                        ->result_id();
+
+  // Look for pointer-to-unsigned int type.
+  opt::analysis::Pointer pointer_to_unsigned_int_type(
+      registered_unsigned_int_type, SpvStorageClassFunction);
+  uint32_t pointer_to_unsigned_int_type_id =
+      context->get_type_mgr()->GetId(&pointer_to_unsigned_int_type);
+  if (!pointer_to_unsigned_int_type_id) {
+    // We need pointer-to-unsigned int in order to declare the loop limiter
+    // variable.
+    return false;
+  }
+
+  // Look for bool type.
+  opt::analysis::Bool bool_type;
+  uint32_t bool_type_id = context->get_type_mgr()->GetId(&bool_type);
+  if (!bool_type_id) {
+    // We need bool in order to compare the loop limiter's value with the loop
+    // limit constant.
+    return false;
+  }
+
+  // Declare the loop limiter variable at the start of the function's entry
+  // block, via an instruction of the form:
+  //   %loop_limiter_var = SpvOpVariable %ptr_to_uint Function %zero
+  added_function->begin()->begin()->InsertBefore(MakeUnique<opt::Instruction>(
+      context, SpvOpVariable, pointer_to_unsigned_int_type_id,
+      message_.loop_limiter_variable_id(),
+      opt::Instruction::OperandList(
+          {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}},
+           {SPV_OPERAND_TYPE_ID, {zero_id}}})));
+  // Update the module's id bound since we have added the loop limiter
+  // variable id.
+  fuzzerutil::UpdateModuleIdBound(context, message_.loop_limiter_variable_id());
+
+  // Consider each loop in turn.
+  for (auto loop_header : loop_headers) {
+    // Look for the loop's back-edge block.  This is a predecessor of the loop
+    // header that is dominated by the loop header.
+    uint32_t back_edge_block_id = 0;
+    for (auto pred : context->cfg()->preds(loop_header->id())) {
+      if (context->GetDominatorAnalysis(added_function)
+              ->Dominates(loop_header->id(), pred)) {
+        back_edge_block_id = pred;
+        break;
+      }
+    }
+    if (!back_edge_block_id) {
+      // The loop's back-edge block must be unreachable.  This means that the
+      // loop cannot iterate, so there is no need to make it lifesafe; we can
+      // move on from this loop.
+      continue;
+    }
+    auto back_edge_block = context->cfg()->block(back_edge_block_id);
+
+    // Go through the sequence of loop limiter infos and find the one
+    // corresponding to this loop.
+    bool found = false;
+    protobufs::LoopLimiterInfo loop_limiter_info;
+    for (auto& info : message_.loop_limiter_info()) {
+      if (info.loop_header_id() == loop_header->id()) {
+        loop_limiter_info = info;
+        found = true;
+        break;
+      }
+    }
+    if (!found) {
+      // We don't have loop limiter info for this loop header.
+      return false;
+    }
+
+    // The back-edge block either has the form:
+    //
+    // (1)
+    //
+    // %l = OpLabel
+    //      ... instructions ...
+    //      OpBranch %loop_header
+    //
+    // (2)
+    //
+    // %l = OpLabel
+    //      ... instructions ...
+    //      OpBranchConditional %c %loop_header %loop_merge
+    //
+    // (3)
+    //
+    // %l = OpLabel
+    //      ... instructions ...
+    //      OpBranchConditional %c %loop_merge %loop_header
+    //
+    // We turn these into the following:
+    //
+    // (1)
+    //
+    //  %l = OpLabel
+    //       ... instructions ...
+    // %t1 = OpLoad %uint32 %loop_limiter
+    // %t2 = OpIAdd %uint32 %t1 %one
+    //       OpStore %loop_limiter %t2
+    // %t3 = OpUGreaterThanEqual %bool %t1 %loop_limit
+    //       OpBranchConditional %t3 %loop_merge %loop_header
+    //
+    // (2)
+    //
+    //  %l = OpLabel
+    //       ... instructions ...
+    // %t1 = OpLoad %uint32 %loop_limiter
+    // %t2 = OpIAdd %uint32 %t1 %one
+    //       OpStore %loop_limiter %t2
+    // %t3 = OpULessThan %bool %t1 %loop_limit
+    // %t4 = OpLogicalAnd %bool %c %t3
+    //       OpBranchConditional %t4 %loop_header %loop_merge
+    //
+    // (3)
+    //
+    //  %l = OpLabel
+    //       ... instructions ...
+    // %t1 = OpLoad %uint32 %loop_limiter
+    // %t2 = OpIAdd %uint32 %t1 %one
+    //       OpStore %loop_limiter %t2
+    // %t3 = OpUGreaterThanEqual %bool %t1 %loop_limit
+    // %t4 = OpLogicalOr %bool %c %t3
+    //       OpBranchConditional %t4 %loop_merge %loop_header
+
+    auto back_edge_block_terminator = back_edge_block->terminator();
+    bool compare_using_greater_than_equal;
+    if (back_edge_block_terminator->opcode() == SpvOpBranch) {
+      compare_using_greater_than_equal = true;
+    } else {
+      assert(back_edge_block_terminator->opcode() == SpvOpBranchConditional);
+      assert(((back_edge_block_terminator->GetSingleWordInOperand(1) ==
+                   loop_header->id() &&
+               back_edge_block_terminator->GetSingleWordInOperand(2) ==
+                   loop_header->MergeBlockId()) ||
+              (back_edge_block_terminator->GetSingleWordInOperand(2) ==
+                   loop_header->id() &&
+               back_edge_block_terminator->GetSingleWordInOperand(1) ==
+                   loop_header->MergeBlockId())) &&
+             "A back edge edge block must branch to"
+             " either the loop header or merge");
+      compare_using_greater_than_equal =
+          back_edge_block_terminator->GetSingleWordInOperand(1) ==
+          loop_header->MergeBlockId();
+    }
+
+    std::vector<std::unique_ptr<opt::Instruction>> new_instructions;
+
+    // Add a load from the loop limiter variable, of the form:
+    //   %t1 = OpLoad %uint32 %loop_limiter
+    new_instructions.push_back(MakeUnique<opt::Instruction>(
+        context, SpvOpLoad, unsigned_int_type_id, loop_limiter_info.load_id(),
+        opt::Instruction::OperandList(
+            {{SPV_OPERAND_TYPE_ID, {message_.loop_limiter_variable_id()}}})));
+
+    // Increment the loaded value:
+    //   %t2 = OpIAdd %uint32 %t1 %one
+    new_instructions.push_back(MakeUnique<opt::Instruction>(
+        context, SpvOpIAdd, unsigned_int_type_id,
+        loop_limiter_info.increment_id(),
+        opt::Instruction::OperandList(
+            {{SPV_OPERAND_TYPE_ID, {loop_limiter_info.load_id()}},
+             {SPV_OPERAND_TYPE_ID, {one_id}}})));
+
+    // Store the incremented value back to the loop limiter variable:
+    //   OpStore %loop_limiter %t2
+    new_instructions.push_back(MakeUnique<opt::Instruction>(
+        context, SpvOpStore, 0, 0,
+        opt::Instruction::OperandList(
+            {{SPV_OPERAND_TYPE_ID, {message_.loop_limiter_variable_id()}},
+             {SPV_OPERAND_TYPE_ID, {loop_limiter_info.increment_id()}}})));
+
+    // Compare the loaded value with the loop limit; either:
+    //   %t3 = OpUGreaterThanEqual %bool %t1 %loop_limit
+    // or
+    //   %t3 = OpULessThan %bool %t1 %loop_limit
+    new_instructions.push_back(MakeUnique<opt::Instruction>(
+        context,
+        compare_using_greater_than_equal ? SpvOpUGreaterThanEqual
+                                         : SpvOpULessThan,
+        bool_type_id, loop_limiter_info.compare_id(),
+        opt::Instruction::OperandList(
+            {{SPV_OPERAND_TYPE_ID, {loop_limiter_info.load_id()}},
+             {SPV_OPERAND_TYPE_ID, {message_.loop_limit_constant_id()}}})));
+
+    if (back_edge_block_terminator->opcode() == SpvOpBranchConditional) {
+      new_instructions.push_back(MakeUnique<opt::Instruction>(
+          context,
+          compare_using_greater_than_equal ? SpvOpLogicalOr : SpvOpLogicalAnd,
+          bool_type_id, loop_limiter_info.logical_op_id(),
+          opt::Instruction::OperandList(
+              {{SPV_OPERAND_TYPE_ID,
+                {back_edge_block_terminator->GetSingleWordInOperand(0)}},
+               {SPV_OPERAND_TYPE_ID, {loop_limiter_info.compare_id()}}})));
+    }
+
+    // Add the new instructions at the end of the back edge block, before the
+    // terminator and any loop merge instruction (as the back edge block can
+    // be the loop header).
+    if (back_edge_block->GetLoopMergeInst()) {
+      back_edge_block->GetLoopMergeInst()->InsertBefore(
+          std::move(new_instructions));
+    } else {
+      back_edge_block_terminator->InsertBefore(std::move(new_instructions));
+    }
+
+    if (back_edge_block_terminator->opcode() == SpvOpBranchConditional) {
+      back_edge_block_terminator->SetInOperand(
+          0, {loop_limiter_info.logical_op_id()});
+    } else {
+      assert(back_edge_block_terminator->opcode() == SpvOpBranch &&
+             "Back-edge terminator must be OpBranch or OpBranchConditional");
+
+      // Check that, if the merge block starts with OpPhi instructions, suitable
+      // ids have been provided to give these instructions a value corresponding
+      // to the new incoming edge from the back edge block.
+      auto merge_block = context->cfg()->block(loop_header->MergeBlockId());
+      if (!fuzzerutil::PhiIdsOkForNewEdge(context, back_edge_block, merge_block,
+                                          loop_limiter_info.phi_id())) {
+        return false;
+      }
+
+      // Augment OpPhi instructions at the loop merge with the given ids.
+      uint32_t phi_index = 0;
+      for (auto& inst : *merge_block) {
+        if (inst.opcode() != SpvOpPhi) {
+          break;
+        }
+        assert(phi_index <
+                   static_cast<uint32_t>(loop_limiter_info.phi_id().size()) &&
+               "There should be at least one phi id per OpPhi instruction.");
+        inst.AddOperand(
+            {SPV_OPERAND_TYPE_ID, {loop_limiter_info.phi_id(phi_index)}});
+        inst.AddOperand({SPV_OPERAND_TYPE_ID, {back_edge_block_id}});
+        phi_index++;
+      }
+
+      // Add the new edge, by changing OpBranch to OpBranchConditional.
+      // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3162): This
+      //  could be a problem if the merge block was originally unreachable: it
+      //  might now be dominated by other blocks that it appears earlier than in
+      //  the module.
+      back_edge_block_terminator->SetOpcode(SpvOpBranchConditional);
+      back_edge_block_terminator->SetInOperands(opt::Instruction::OperandList(
+          {{SPV_OPERAND_TYPE_ID, {loop_limiter_info.compare_id()}},
+           {SPV_OPERAND_TYPE_ID, {loop_header->MergeBlockId()}
+
+           },
+           {SPV_OPERAND_TYPE_ID, {loop_header->id()}}}));
+    }
+
+    // Update the module's id bound with respect to the various ids that
+    // have been used for loop limiter manipulation.
+    fuzzerutil::UpdateModuleIdBound(context, loop_limiter_info.load_id());
+    fuzzerutil::UpdateModuleIdBound(context, loop_limiter_info.increment_id());
+    fuzzerutil::UpdateModuleIdBound(context, loop_limiter_info.compare_id());
+    fuzzerutil::UpdateModuleIdBound(context, loop_limiter_info.logical_op_id());
+  }
+  return true;
+}
+
+bool TransformationAddFunction::TryToTurnKillOrUnreachableIntoReturn(
+    opt::IRContext* context, opt::Function* added_function,
+    opt::Instruction* kill_or_unreachable_inst) const {
+  assert((kill_or_unreachable_inst->opcode() == SpvOpKill ||
+          kill_or_unreachable_inst->opcode() == SpvOpUnreachable) &&
+         "Precondition: instruction must be OpKill or OpUnreachable.");
+
+  // Get the function's return type.
+  auto function_return_type_inst =
+      context->get_def_use_mgr()->GetDef(added_function->type_id());
+
+  if (function_return_type_inst->opcode() == SpvOpTypeVoid) {
+    // The function has void return type, so change this instruction to
+    // OpReturn.
+    kill_or_unreachable_inst->SetOpcode(SpvOpReturn);
+  } else {
+    // The function has non-void return type, so change this instruction
+    // to OpReturnValue, using the value id provided with the
+    // transformation.
+
+    // We first check that the id, %id, provided with the transformation
+    // specifically to turn OpKill and OpUnreachable instructions into
+    // OpReturnValue %id has the same type as the function's return type.
+    if (context->get_def_use_mgr()
+            ->GetDef(message_.kill_unreachable_return_value_id())
+            ->type_id() != function_return_type_inst->result_id()) {
+      return false;
+    }
+    kill_or_unreachable_inst->SetOpcode(SpvOpReturnValue);
+    kill_or_unreachable_inst->SetInOperands(
+        {{SPV_OPERAND_TYPE_ID, {message_.kill_unreachable_return_value_id()}}});
+  }
+  return true;
+}
+
+bool TransformationAddFunction::TryToClampAccessChainIndices(
+    opt::IRContext* context, opt::Instruction* access_chain_inst) const {
+  assert((access_chain_inst->opcode() == SpvOpAccessChain ||
+          access_chain_inst->opcode() == SpvOpInBoundsAccessChain) &&
+         "Precondition: instruction must be OpAccessChain or "
+         "OpInBoundsAccessChain.");
+
+  // Find the AccessChainClampingInfo associated with this access chain.
+  const protobufs::AccessChainClampingInfo* access_chain_clamping_info =
+      nullptr;
+  for (auto& clamping_info : message_.access_chain_clamping_info()) {
+    if (clamping_info.access_chain_id() == access_chain_inst->result_id()) {
+      access_chain_clamping_info = &clamping_info;
+      break;
+    }
+  }
+  if (!access_chain_clamping_info) {
+    // No access chain clamping information was found; the function cannot be
+    // made livesafe.
+    return false;
+  }
+
+  // Check that there is a (compare_id, select_id) pair for every
+  // index associated with the instruction.
+  if (static_cast<uint32_t>(
+          access_chain_clamping_info->compare_and_select_ids().size()) !=
+      access_chain_inst->NumInOperands() - 1) {
+    return false;
+  }
+
+  // Walk the access chain, clamping each index to be within bounds if it is
+  // not a constant.
+  auto base_object = context->get_def_use_mgr()->GetDef(
+      access_chain_inst->GetSingleWordInOperand(0));
+  assert(base_object && "The base object must exist.");
+  auto pointer_type =
+      context->get_def_use_mgr()->GetDef(base_object->type_id());
+  assert(pointer_type && pointer_type->opcode() == SpvOpTypePointer &&
+         "The base object must have pointer type.");
+  auto should_be_composite_type = context->get_def_use_mgr()->GetDef(
+      pointer_type->GetSingleWordInOperand(1));
+
+  // Consider each index input operand in turn (operand 0 is the base object).
+  for (uint32_t index = 1; index < access_chain_inst->NumInOperands();
+       index++) {
+    // We are going to turn:
+    //
+    // %result = OpAccessChain %type %object ... %index ...
+    //
+    // into:
+    //
+    // %t1 = OpULessThanEqual %bool %index %bound_minus_one
+    // %t2 = OpSelect %int_type %t1 %index %bound_minus_one
+    // %result = OpAccessChain %type %object ... %t2 ...
+    //
+    // ... unless %index is already a constant.
+
+    // Get the bound for the composite being indexed into; e.g. the number of
+    // columns of matrix or the size of an array.
+    uint32_t bound =
+        GetBoundForCompositeIndex(context, *should_be_composite_type);
+
+    // Get the instruction associated with the index and figure out its integer
+    // type.
+    const uint32_t index_id = access_chain_inst->GetSingleWordInOperand(index);
+    auto index_inst = context->get_def_use_mgr()->GetDef(index_id);
+    auto index_type_inst =
+        context->get_def_use_mgr()->GetDef(index_inst->type_id());
+    assert(index_type_inst->opcode() == SpvOpTypeInt);
+    assert(index_type_inst->GetSingleWordInOperand(0) == 32);
+    opt::analysis::Integer* index_int_type =
+        context->get_type_mgr()
+            ->GetType(index_type_inst->result_id())
+            ->AsInteger();
+
+    if (index_inst->opcode() != SpvOpConstant) {
+      // The index is non-constant so we need to clamp it.
+      assert(should_be_composite_type->opcode() != SpvOpTypeStruct &&
+             "Access chain indices into structures are required to be "
+             "constants.");
+      opt::analysis::IntConstant bound_minus_one(index_int_type, {bound - 1});
+      if (!context->get_constant_mgr()->FindConstant(&bound_minus_one)) {
+        // We do not have an integer constant whose value is |bound| -1.
+        return false;
+      }
+
+      opt::analysis::Bool bool_type;
+      uint32_t bool_type_id = context->get_type_mgr()->GetId(&bool_type);
+      if (!bool_type_id) {
+        // Bool type is not declared; we cannot do a comparison.
+        return false;
+      }
+
+      uint32_t bound_minus_one_id =
+          context->get_constant_mgr()
+              ->GetDefiningInstruction(&bound_minus_one)
+              ->result_id();
+
+      uint32_t compare_id =
+          access_chain_clamping_info->compare_and_select_ids(index - 1).first();
+      uint32_t select_id =
+          access_chain_clamping_info->compare_and_select_ids(index - 1)
+              .second();
+      std::vector<std::unique_ptr<opt::Instruction>> new_instructions;
+
+      // Compare the index with the bound via an instruction of the form:
+      //   %t1 = OpULessThanEqual %bool %index %bound_minus_one
+      new_instructions.push_back(MakeUnique<opt::Instruction>(
+          context, SpvOpULessThanEqual, bool_type_id, compare_id,
+          opt::Instruction::OperandList(
+              {{SPV_OPERAND_TYPE_ID, {index_inst->result_id()}},
+               {SPV_OPERAND_TYPE_ID, {bound_minus_one_id}}})));
+
+      // Select the index if in-bounds, otherwise one less than the bound:
+      //   %t2 = OpSelect %int_type %t1 %index %bound_minus_one
+      new_instructions.push_back(MakeUnique<opt::Instruction>(
+          context, SpvOpSelect, index_type_inst->result_id(), select_id,
+          opt::Instruction::OperandList(
+              {{SPV_OPERAND_TYPE_ID, {compare_id}},
+               {SPV_OPERAND_TYPE_ID, {index_inst->result_id()}},
+               {SPV_OPERAND_TYPE_ID, {bound_minus_one_id}}})));
+
+      // Add the new instructions before the access chain
+      access_chain_inst->InsertBefore(std::move(new_instructions));
+
+      // Replace %index with %t2.
+      access_chain_inst->SetInOperand(index, {select_id});
+      fuzzerutil::UpdateModuleIdBound(context, compare_id);
+      fuzzerutil::UpdateModuleIdBound(context, select_id);
+    } else {
+      // TODO(afd): At present the SPIR-V spec is not clear on whether
+      //  statically out-of-bounds indices mean that a module is invalid (so
+      //  that it should be rejected by the validator), or that such accesses
+      //  yield undefined results.  Via the following assertion, we assume that
+      //  functions added to the module do not feature statically out-of-bounds
+      //  accesses.
+      // Assert that the index is smaller (unsigned) than this value.
+      // Return false if it is not (to keep compilers happy).
+      if (index_inst->GetSingleWordInOperand(0) >= bound) {
+        assert(false &&
+               "The function has a statically out-of-bounds access; "
+               "this should not occur.");
+        return false;
+      }
+    }
+    should_be_composite_type =
+        FollowCompositeIndex(context, *should_be_composite_type, index_id);
+  }
+  return true;
+}
+
+uint32_t TransformationAddFunction::GetBoundForCompositeIndex(
+    opt::IRContext* context, const opt::Instruction& composite_type_inst) {
+  switch (composite_type_inst.opcode()) {
+    case SpvOpTypeArray:
+      return fuzzerutil::GetArraySize(composite_type_inst, context);
+    case SpvOpTypeMatrix:
+    case SpvOpTypeVector:
+      return composite_type_inst.GetSingleWordInOperand(1);
+    case SpvOpTypeStruct: {
+      return fuzzerutil::GetNumberOfStructMembers(composite_type_inst);
+    }
+    default:
+      assert(false && "Unknown composite type.");
+      return 0;
+  }
+}
+
+opt::Instruction* TransformationAddFunction::FollowCompositeIndex(
+    opt::IRContext* context, const opt::Instruction& composite_type_inst,
+    uint32_t index_id) {
+  uint32_t sub_object_type_id;
+  switch (composite_type_inst.opcode()) {
+    case SpvOpTypeArray:
+      sub_object_type_id = composite_type_inst.GetSingleWordInOperand(0);
+      break;
+    case SpvOpTypeMatrix:
+    case SpvOpTypeVector:
+      sub_object_type_id = composite_type_inst.GetSingleWordInOperand(0);
+      break;
+    case SpvOpTypeStruct: {
+      auto index_inst = context->get_def_use_mgr()->GetDef(index_id);
+      assert(index_inst->opcode() == SpvOpConstant);
+      assert(
+          context->get_def_use_mgr()->GetDef(index_inst->type_id())->opcode() ==
+          SpvOpTypeInt);
+      assert(context->get_def_use_mgr()
+                 ->GetDef(index_inst->type_id())
+                 ->GetSingleWordInOperand(0) == 32);
+      uint32_t index_value = index_inst->GetSingleWordInOperand(0);
+      sub_object_type_id =
+          composite_type_inst.GetSingleWordInOperand(index_value);
+      break;
+    }
+    default:
+      assert(false && "Unknown composite type.");
+      sub_object_type_id = 0;
+      break;
+  }
+  assert(sub_object_type_id && "No sub-object found.");
+  return context->get_def_use_mgr()->GetDef(sub_object_type_id);
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 124 - 0
3rdparty/spirv-tools/source/fuzz/transformation_add_function.h

@@ -0,0 +1,124 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_FUNCTION_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ADD_FUNCTION_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationAddFunction : public Transformation {
+ public:
+  explicit TransformationAddFunction(
+      const protobufs::TransformationAddFunction& message);
+
+  // Creates a transformation to add a non live-safe function.
+  explicit TransformationAddFunction(
+      const std::vector<protobufs::Instruction>& instructions);
+
+  // Creates a transformation to add a live-safe function.
+  TransformationAddFunction(
+      const std::vector<protobufs::Instruction>& instructions,
+      uint32_t loop_limiter_variable_id, uint32_t loop_limit_constant_id,
+      const std::vector<protobufs::LoopLimiterInfo>& loop_limiters,
+      uint32_t kill_unreachable_return_value_id,
+      const std::vector<protobufs::AccessChainClampingInfo>&
+          access_chain_clampers);
+
+  // - |message_.instruction| must correspond to a sufficiently well-formed
+  //   sequence of instructions that a function can be created from them
+  // - If |message_.is_livesafe| holds then |message_| must contain suitable
+  //   ingredients to make the function livesafe, and the function must only
+  //   invoke other livesafe functions
+  // - Adding the created function to the module must lead to a valid module.
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // Adds the function defined by |message_.instruction| to the module, making
+  // it livesafe if |message_.is_livesafe| holds.
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+  // Helper method that returns the bound for indexing into a composite of type
+  // |composite_type_inst|, i.e. the number of fields of a struct, the size of
+  // an array, the number of components of a vector, or the number of columns of
+  // a matrix.
+  static uint32_t GetBoundForCompositeIndex(
+      opt::IRContext* context, const opt::Instruction& composite_type_inst);
+
+  // Helper method that, given composite type |composite_type_inst|, returns the
+  // type of the sub-object at index |index_id|, which is required to be in-
+  // bounds.
+  static opt::Instruction* FollowCompositeIndex(
+      opt::IRContext* context, const opt::Instruction& composite_type_inst,
+      uint32_t index_id);
+
+ private:
+  // Attempts to create a function from the series of instructions in
+  // |message_.instruction| and add it to |context|.
+  //
+  // Returns false if adding the function is not possible due to the messages
+  // not respecting the basic structure of a function, e.g. if there is no
+  // OpFunction instruction or no blocks; in this case |context| is left in an
+  // indeterminate state.
+  //
+  // Otherwise returns true.  Whether |context| is valid after addition of the
+  // function depends on the contents of |message_.instruction|.
+  //
+  // Intended usage:
+  // - Perform a dry run of this method on a clone of a module, and use
+  //   the validator to check whether the resulting module is valid.  Working
+  //   on a clone means it does not matter if the function fails to be cleanly
+  //   added, or leads to an invalid module.
+  // - If the dry run succeeds, run the method on the real module of interest,
+  //   to add the function.
+  bool TryToAddFunction(opt::IRContext* context) const;
+
+  // Should only be called if |message_.is_livesafe| holds.  Attempts to make
+  // the function livesafe (see FactFunctionIsLivesafe for a definition).
+  // Returns false if this is not possible, due to |message_| or |context| not
+  // containing sufficient ingredients (such as types and fresh ids) to add
+  // the instrumentation necessary to make the function livesafe.
+  bool TryToMakeFunctionLivesafe(opt::IRContext* context,
+                                 const FactManager& fact_manager) const;
+
+  // A helper for TryToMakeFunctionLivesafe that tries to add loop-limiting
+  // logic.
+  bool TryToAddLoopLimiters(opt::IRContext* context,
+                            opt::Function* added_function) const;
+
+  // A helper for TryToMakeFunctionLivesafe that tries to replace OpKill and
+  // OpUnreachable instructions into return instructions.
+  bool TryToTurnKillOrUnreachableIntoReturn(
+      opt::IRContext* context, opt::Function* added_function,
+      opt::Instruction* kill_or_unreachable_inst) const;
+
+  // A helper for TryToMakeFunctionLivesafe that tries to clamp access chain
+  // indices so that they are guaranteed to be in-bounds.
+  bool TryToClampAccessChainIndices(opt::IRContext* context,
+                                    opt::Instruction* access_chain_inst) const;
+
+  protobufs::TransformationAddFunction message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_ADD_FUNCTION_H_

+ 62 - 0
3rdparty/spirv-tools/source/fuzz/transformation_add_global_undef.cpp

@@ -0,0 +1,62 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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.
+
+#include "source/fuzz/transformation_add_global_undef.h"
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAddGlobalUndef::TransformationAddGlobalUndef(
+    const spvtools::fuzz::protobufs::TransformationAddGlobalUndef& message)
+    : message_(message) {}
+
+TransformationAddGlobalUndef::TransformationAddGlobalUndef(uint32_t fresh_id,
+                                                           uint32_t type_id) {
+  message_.set_fresh_id(fresh_id);
+  message_.set_type_id(type_id);
+}
+
+bool TransformationAddGlobalUndef::IsApplicable(
+    opt::IRContext* context,
+    const spvtools::fuzz::FactManager& /*unused*/) const {
+  // A fresh id is required.
+  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+    return false;
+  }
+  auto type = context->get_type_mgr()->GetType(message_.type_id());
+  // The type must exist, and must not be a function type.
+  return type && !type->AsFunction();
+}
+
+void TransformationAddGlobalUndef::Apply(
+    opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
+  context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
+      context, SpvOpUndef, message_.type_id(), message_.fresh_id(),
+      opt::Instruction::OperandList()));
+  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+  // We have added an instruction to the module, so need to be careful about the
+  // validity of existing analyses.
+  context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationAddGlobalUndef::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_add_global_undef() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 51 - 0
3rdparty/spirv-tools/source/fuzz/transformation_add_global_undef.h

@@ -0,0 +1,51 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_GLOBAL_UNDEF_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ADD_GLOBAL_UNDEF_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationAddGlobalUndef : public Transformation {
+ public:
+  explicit TransformationAddGlobalUndef(
+      const protobufs::TransformationAddGlobalUndef& message);
+
+  TransformationAddGlobalUndef(uint32_t fresh_id, uint32_t type_id);
+
+  // - |message_.fresh_id| must be fresh
+  // - |message_.type_id| must be the id of a non-function type
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // Adds an OpUndef instruction to the module, with |message_.type_id| as its
+  // type.  The instruction has result id |message_.fresh_id|.
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationAddGlobalUndef message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_ADD_GLOBAL_UNDEF_H_

+ 137 - 0
3rdparty/spirv-tools/source/fuzz/transformation_add_global_variable.cpp

@@ -0,0 +1,137 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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.
+
+#include "source/fuzz/transformation_add_global_variable.h"
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAddGlobalVariable::TransformationAddGlobalVariable(
+    const spvtools::fuzz::protobufs::TransformationAddGlobalVariable& message)
+    : message_(message) {}
+
+TransformationAddGlobalVariable::TransformationAddGlobalVariable(
+    uint32_t fresh_id, uint32_t type_id, uint32_t initializer_id,
+    bool value_is_arbitrary) {
+  message_.set_fresh_id(fresh_id);
+  message_.set_type_id(type_id);
+  message_.set_initializer_id(initializer_id);
+  message_.set_value_is_arbitrary(value_is_arbitrary);
+}
+
+bool TransformationAddGlobalVariable::IsApplicable(
+    opt::IRContext* context,
+    const spvtools::fuzz::FactManager& /*unused*/) const {
+  // The result id must be fresh.
+  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+    return false;
+  }
+  // The type id must correspond to a type.
+  auto type = context->get_type_mgr()->GetType(message_.type_id());
+  if (!type) {
+    return false;
+  }
+  // That type must be a pointer type ...
+  auto pointer_type = type->AsPointer();
+  if (!pointer_type) {
+    return false;
+  }
+  // ... with Private storage class.
+  if (pointer_type->storage_class() != SpvStorageClassPrivate) {
+    return false;
+  }
+  if (message_.initializer_id()) {
+    // The initializer id must be the id of a constant.  Check this with the
+    // constant manager.
+    auto constant_id = context->get_constant_mgr()->GetConstantsFromIds(
+        {message_.initializer_id()});
+    if (constant_id.empty()) {
+      return false;
+    }
+    assert(constant_id.size() == 1 &&
+           "We asked for the constant associated with a single id; we should "
+           "get a single constant.");
+    // The type of the constant must match the pointee type of the pointer.
+    if (pointer_type->pointee_type() != constant_id[0]->type()) {
+      return false;
+    }
+  }
+  return true;
+}
+
+void TransformationAddGlobalVariable::Apply(
+    opt::IRContext* context, spvtools::fuzz::FactManager* fact_manager) const {
+  opt::Instruction::OperandList input_operands;
+  input_operands.push_back(
+      {SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassPrivate}});
+  if (message_.initializer_id()) {
+    input_operands.push_back(
+        {SPV_OPERAND_TYPE_ID, {message_.initializer_id()}});
+  }
+  context->module()->AddGlobalValue(
+      MakeUnique<opt::Instruction>(context, SpvOpVariable, message_.type_id(),
+                                   message_.fresh_id(), input_operands));
+  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+
+  if (PrivateGlobalsMustBeDeclaredInEntryPointInterfaces(context)) {
+    // Conservatively add this global to the interface of every entry point in
+    // the module.  This means that the global is available for other
+    // transformations to use.
+    //
+    // A downside of this is that the global will be in the interface even if it
+    // ends up never being used.
+    //
+    // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3111) revisit
+    //  this if a more thorough approach to entry point interfaces is taken.
+    for (auto& entry_point : context->module()->entry_points()) {
+      entry_point.AddOperand({SPV_OPERAND_TYPE_ID, {message_.fresh_id()}});
+    }
+  }
+
+  if (message_.value_is_arbitrary()) {
+    fact_manager->AddFactValueOfVariableIsArbitrary(message_.fresh_id());
+  }
+
+  // We have added an instruction to the module, so need to be careful about the
+  // validity of existing analyses.
+  context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationAddGlobalVariable::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_add_global_variable() = message_;
+  return result;
+}
+
+bool TransformationAddGlobalVariable::
+    PrivateGlobalsMustBeDeclaredInEntryPointInterfaces(
+        opt::IRContext* context) {
+  // TODO(afd): We capture the universal environments for which this requirement
+  //  holds.  The check should be refined on demand for other target
+  //  environments.
+  switch (context->grammar().target_env()) {
+    case SPV_ENV_UNIVERSAL_1_0:
+    case SPV_ENV_UNIVERSAL_1_1:
+    case SPV_ENV_UNIVERSAL_1_2:
+    case SPV_ENV_UNIVERSAL_1_3:
+      return false;
+    default:
+      return true;
+  }
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 61 - 0
3rdparty/spirv-tools/source/fuzz/transformation_add_global_variable.h

@@ -0,0 +1,61 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_GLOBAL_VARIABLE_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ADD_GLOBAL_VARIABLE_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationAddGlobalVariable : public Transformation {
+ public:
+  explicit TransformationAddGlobalVariable(
+      const protobufs::TransformationAddGlobalVariable& message);
+
+  TransformationAddGlobalVariable(uint32_t fresh_id, uint32_t type_id,
+                                  uint32_t initializer_id,
+                                  bool value_is_arbitrary);
+
+  // - |message_.fresh_id| must be fresh
+  // - |message_.type_id| must be the id of a pointer type with Private storage
+  //   class
+  // - |message_.initializer_id| must either be 0 or the id of a constant whose
+  //   type is the pointee type of |message_.type_id|
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // Adds a global variable with Private storage class to the module, with type
+  // |message_.type_id| and either no initializer or |message_.initializer_id|
+  // as an initializer, depending on whether |message_.initializer_id| is 0.
+  // The global variable has result id |message_.fresh_id|.
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  static bool PrivateGlobalsMustBeDeclaredInEntryPointInterfaces(
+      opt::IRContext* context);
+
+  protobufs::TransformationAddGlobalVariable message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_ADD_GLOBAL_VARIABLE_H_

+ 88 - 0
3rdparty/spirv-tools/source/fuzz/transformation_add_type_array.cpp

@@ -0,0 +1,88 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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.
+
+#include "source/fuzz/transformation_add_type_array.h"
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAddTypeArray::TransformationAddTypeArray(
+    const spvtools::fuzz::protobufs::TransformationAddTypeArray& message)
+    : message_(message) {}
+
+TransformationAddTypeArray::TransformationAddTypeArray(uint32_t fresh_id,
+                                                       uint32_t element_type_id,
+                                                       uint32_t size_id) {
+  message_.set_fresh_id(fresh_id);
+  message_.set_element_type_id(element_type_id);
+  message_.set_size_id(size_id);
+}
+
+bool TransformationAddTypeArray::IsApplicable(
+    opt::IRContext* context,
+    const spvtools::fuzz::FactManager& /*unused*/) const {
+  // A fresh id is required.
+  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+    return false;
+  }
+  auto element_type =
+      context->get_type_mgr()->GetType(message_.element_type_id());
+  if (!element_type || element_type->AsFunction()) {
+    // The element type id either does not refer to a type, or refers to a
+    // function type; both are illegal.
+    return false;
+  }
+  auto constant =
+      context->get_constant_mgr()->GetConstantsFromIds({message_.size_id()});
+  if (constant.empty()) {
+    // The size id does not refer to a constant.
+    return false;
+  }
+  assert(constant.size() == 1 &&
+         "Only one constant id was provided, so only one constant should have "
+         "been returned");
+
+  auto int_constant = constant[0]->AsIntConstant();
+  if (!int_constant) {
+    // The size constant is not an integer.
+    return false;
+  }
+  // We require that the size constant be a 32-bit value that is positive when
+  // interpreted as being signed.
+  return int_constant->words().size() == 1 && int_constant->GetS32() >= 1;
+}
+
+void TransformationAddTypeArray::Apply(
+    opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
+  opt::Instruction::OperandList in_operands;
+  in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.element_type_id()}});
+  in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.size_id()}});
+  context->module()->AddType(MakeUnique<opt::Instruction>(
+      context, SpvOpTypeArray, 0, message_.fresh_id(), in_operands));
+  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+  // We have added an instruction to the module, so need to be careful about the
+  // validity of existing analyses.
+  context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationAddTypeArray::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_add_type_array() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 55 - 0
3rdparty/spirv-tools/source/fuzz/transformation_add_type_array.h

@@ -0,0 +1,55 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_ARRAY_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_ARRAY_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationAddTypeArray : public Transformation {
+ public:
+  explicit TransformationAddTypeArray(
+      const protobufs::TransformationAddTypeArray& message);
+
+  TransformationAddTypeArray(uint32_t fresh_id, uint32_t element_type_id,
+                             uint32_t size_id);
+
+  // - |message_.fresh_id| must be fresh
+  // - |message_.element_type_id| must be the id of a non-function type
+  // - |message_.size_id| must be the id of a 32-bit integer constant that is
+  //   positive when interpreted as signed.
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // Adds an OpTypeArray instruction to the module, with element type given by
+  // |message_.element_type_id| and size given by |message_.size_id|.  The
+  // result id of the instruction is |message_.fresh_id|.
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationAddTypeArray message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_ARRAY_H_

+ 113 - 0
3rdparty/spirv-tools/source/fuzz/transformation_add_type_function.cpp

@@ -0,0 +1,113 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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.
+
+#include "source/fuzz/transformation_add_type_function.h"
+
+#include <vector>
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAddTypeFunction::TransformationAddTypeFunction(
+    const spvtools::fuzz::protobufs::TransformationAddTypeFunction& message)
+    : message_(message) {}
+
+TransformationAddTypeFunction::TransformationAddTypeFunction(
+    uint32_t fresh_id, uint32_t return_type_id,
+    const std::vector<uint32_t>& argument_type_ids) {
+  message_.set_fresh_id(fresh_id);
+  message_.set_return_type_id(return_type_id);
+  for (auto id : argument_type_ids) {
+    message_.add_argument_type_id(id);
+  }
+}
+
+bool TransformationAddTypeFunction::IsApplicable(
+    opt::IRContext* context,
+    const spvtools::fuzz::FactManager& /*unused*/) const {
+  // The result id must be fresh.
+  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+    return false;
+  }
+  // The return and argument types must be type ids but not not be function
+  // type ids.
+  if (!fuzzerutil::IsNonFunctionTypeId(context, message_.return_type_id())) {
+    return false;
+  }
+  for (auto argument_type_id : message_.argument_type_id()) {
+    if (!fuzzerutil::IsNonFunctionTypeId(context, argument_type_id)) {
+      return false;
+    }
+  }
+  // Check whether there is already an OpTypeFunction definition that uses
+  // exactly the same return and argument type ids.  (Note that the type manager
+  // does not allow us to check this, as it does not distinguish between
+  // function types with different but isomorphic pointer argument types.)
+  for (auto& inst : context->module()->types_values()) {
+    if (inst.opcode() != SpvOpTypeFunction) {
+      // Consider only OpTypeFunction instructions.
+      continue;
+    }
+    if (inst.GetSingleWordInOperand(0) != message_.return_type_id()) {
+      // Different return types - cannot be the same.
+      continue;
+    }
+    if (inst.NumInOperands() !=
+        1 + static_cast<uint32_t>(message_.argument_type_id().size())) {
+      // Different numbers of arguments - cannot be the same.
+      continue;
+    }
+    bool found_argument_mismatch = false;
+    for (uint32_t index = 1; index < inst.NumInOperands(); index++) {
+      if (message_.argument_type_id(index - 1) !=
+          inst.GetSingleWordInOperand(index)) {
+        // Argument mismatch - cannot be the same.
+        found_argument_mismatch = true;
+        break;
+      }
+    }
+    if (found_argument_mismatch) {
+      continue;
+    }
+    // Everything matches - the type is already declared.
+    return false;
+  }
+  return true;
+}
+
+void TransformationAddTypeFunction::Apply(
+    opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
+  opt::Instruction::OperandList in_operands;
+  in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.return_type_id()}});
+  for (auto argument_type_id : message_.argument_type_id()) {
+    in_operands.push_back({SPV_OPERAND_TYPE_ID, {argument_type_id}});
+  }
+  context->module()->AddType(MakeUnique<opt::Instruction>(
+      context, SpvOpTypeFunction, 0, message_.fresh_id(), in_operands));
+  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+  // We have added an instruction to the module, so need to be careful about the
+  // validity of existing analyses.
+  context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationAddTypeFunction::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_add_type_function() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 59 - 0
3rdparty/spirv-tools/source/fuzz/transformation_add_type_function.h

@@ -0,0 +1,59 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_FUNCTION_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_FUNCTION_H_
+
+#include <vector>
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationAddTypeFunction : public Transformation {
+ public:
+  explicit TransformationAddTypeFunction(
+      const protobufs::TransformationAddTypeFunction& message);
+
+  TransformationAddTypeFunction(uint32_t fresh_id, uint32_t return_type_id,
+                                const std::vector<uint32_t>& argument_type_ids);
+
+  // - |message_.fresh_id| must not be used by the module
+  // - |message_.return_type_id| and each element of |message_.argument_type_id|
+  //   must be the ids of non-function types
+  // - The module must not contain an OpTypeFunction instruction defining a
+  //   function type with the signature provided by teh given return and
+  //   argument types
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // Adds an OpTypeFunction instruction to the module, with signature given by
+  // |message_.return_type_id| and |message_.argument_type_id|.  The result id
+  // for the instruction is |message_.fresh_id|.
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationAddTypeFunction message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_FUNCTION_H_

+ 71 - 0
3rdparty/spirv-tools/source/fuzz/transformation_add_type_matrix.cpp

@@ -0,0 +1,71 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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.
+
+#include "source/fuzz/transformation_add_type_matrix.h"
+
+#include "fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAddTypeMatrix::TransformationAddTypeMatrix(
+    const spvtools::fuzz::protobufs::TransformationAddTypeMatrix& message)
+    : message_(message) {}
+
+TransformationAddTypeMatrix::TransformationAddTypeMatrix(
+    uint32_t fresh_id, uint32_t column_type_id, uint32_t column_count) {
+  message_.set_fresh_id(fresh_id);
+  message_.set_column_type_id(column_type_id);
+  message_.set_column_count(column_count);
+}
+
+bool TransformationAddTypeMatrix::IsApplicable(
+    opt::IRContext* context,
+    const spvtools::fuzz::FactManager& /*unused*/) const {
+  // The result id must be fresh.
+  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+    return false;
+  }
+  // The column type must be a floating-point vector.
+  auto column_type =
+      context->get_type_mgr()->GetType(message_.column_type_id());
+  if (!column_type) {
+    return false;
+  }
+  return column_type->AsVector() &&
+         column_type->AsVector()->element_type()->AsFloat();
+}
+
+void TransformationAddTypeMatrix::Apply(
+    opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
+  opt::Instruction::OperandList in_operands;
+  in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.column_type_id()}});
+  in_operands.push_back(
+      {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.column_count()}});
+  context->module()->AddType(MakeUnique<opt::Instruction>(
+      context, SpvOpTypeMatrix, 0, message_.fresh_id(), in_operands));
+  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+  // We have added an instruction to the module, so need to be careful about the
+  // validity of existing analyses.
+  context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationAddTypeMatrix::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_add_type_matrix() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 53 - 0
3rdparty/spirv-tools/source/fuzz/transformation_add_type_matrix.h

@@ -0,0 +1,53 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_MATRIX_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_MATRIX_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationAddTypeMatrix : public Transformation {
+ public:
+  explicit TransformationAddTypeMatrix(
+      const protobufs::TransformationAddTypeMatrix& message);
+
+  TransformationAddTypeMatrix(uint32_t fresh_id, uint32_t column_type_id,
+                              uint32_t column_count);
+
+  // - |message_.fresh_id| must be a fresh id
+  // - |message_.column_type_id| must be the id of a floating-point vector type
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // Adds an OpTypeMatrix instruction to the module, with column type
+  // |message_.column_type_id| and |message_.column_count| columns, with result
+  // id |message_.fresh_id|.
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationAddTypeMatrix message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_MATRIX_H_

+ 73 - 0
3rdparty/spirv-tools/source/fuzz/transformation_add_type_struct.cpp

@@ -0,0 +1,73 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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.
+
+#include "source/fuzz/transformation_add_type_struct.h"
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAddTypeStruct::TransformationAddTypeStruct(
+    const spvtools::fuzz::protobufs::TransformationAddTypeStruct& message)
+    : message_(message) {}
+
+TransformationAddTypeStruct::TransformationAddTypeStruct(
+    uint32_t fresh_id, const std::vector<uint32_t>& member_type_ids) {
+  message_.set_fresh_id(fresh_id);
+  for (auto member_type_id : member_type_ids) {
+    message_.add_member_type_id(member_type_id);
+  }
+}
+
+bool TransformationAddTypeStruct::IsApplicable(
+    opt::IRContext* context,
+    const spvtools::fuzz::FactManager& /*unused*/) const {
+  // A fresh id is required.
+  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+    return false;
+  }
+  for (auto member_type : message_.member_type_id()) {
+    auto type = context->get_type_mgr()->GetType(member_type);
+    if (!type || type->AsFunction()) {
+      // The member type id either does not refer to a type, or refers to a
+      // function type; both are illegal.
+      return false;
+    }
+  }
+  return true;
+}
+
+void TransformationAddTypeStruct::Apply(
+    opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
+  opt::Instruction::OperandList in_operands;
+  for (auto member_type : message_.member_type_id()) {
+    in_operands.push_back({SPV_OPERAND_TYPE_ID, {member_type}});
+  }
+  context->module()->AddType(MakeUnique<opt::Instruction>(
+      context, SpvOpTypeStruct, 0, message_.fresh_id(), in_operands));
+  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+  // We have added an instruction to the module, so need to be careful about the
+  // validity of existing analyses.
+  context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationAddTypeStruct::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_add_type_struct() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 54 - 0
3rdparty/spirv-tools/source/fuzz/transformation_add_type_struct.h

@@ -0,0 +1,54 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_STRUCT_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_STRUCT_H_
+
+#include <vector>
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationAddTypeStruct : public Transformation {
+ public:
+  explicit TransformationAddTypeStruct(
+      const protobufs::TransformationAddTypeStruct& message);
+
+  TransformationAddTypeStruct(uint32_t fresh_id,
+                              const std::vector<uint32_t>& component_type_ids);
+
+  // - |message_.fresh_id| must be a fresh id
+  // - |message_.member_type_id| must be a sequence of non-function type ids
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // Adds an OpTypeStruct instruction whose field types are given by
+  // |message_.member_type_id|, with result id |message_.fresh_id|.
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationAddTypeStruct message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_STRUCT_H_

+ 69 - 0
3rdparty/spirv-tools/source/fuzz/transformation_add_type_vector.cpp

@@ -0,0 +1,69 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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.
+
+#include "source/fuzz/transformation_add_type_vector.h"
+
+#include "fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAddTypeVector::TransformationAddTypeVector(
+    const spvtools::fuzz::protobufs::TransformationAddTypeVector& message)
+    : message_(message) {}
+
+TransformationAddTypeVector::TransformationAddTypeVector(
+    uint32_t fresh_id, uint32_t component_type_id, uint32_t component_count) {
+  message_.set_fresh_id(fresh_id);
+  message_.set_component_type_id(component_type_id);
+  message_.set_component_count(component_count);
+}
+
+bool TransformationAddTypeVector::IsApplicable(
+    opt::IRContext* context,
+    const spvtools::fuzz::FactManager& /*unused*/) const {
+  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+    return false;
+  }
+  auto component_type =
+      context->get_type_mgr()->GetType(message_.component_type_id());
+  if (!component_type) {
+    return false;
+  }
+  return component_type->AsBool() || component_type->AsFloat() ||
+         component_type->AsInteger();
+}
+
+void TransformationAddTypeVector::Apply(
+    opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
+  opt::Instruction::OperandList in_operands;
+  in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.component_type_id()}});
+  in_operands.push_back(
+      {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.component_count()}});
+  context->module()->AddType(MakeUnique<opt::Instruction>(
+      context, SpvOpTypeVector, 0, message_.fresh_id(), in_operands));
+  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+  // We have added an instruction to the module, so need to be careful about the
+  // validity of existing analyses.
+  context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationAddTypeVector::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_add_type_vector() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 53 - 0
3rdparty/spirv-tools/source/fuzz/transformation_add_type_vector.h

@@ -0,0 +1,53 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_VECTOR_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_VECTOR_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationAddTypeVector : public Transformation {
+ public:
+  explicit TransformationAddTypeVector(
+      const protobufs::TransformationAddTypeVector& message);
+
+  TransformationAddTypeVector(uint32_t fresh_id, uint32_t component_type_id,
+                              uint32_t component_count);
+
+  // - |message_.fresh_id| must be a fresh id
+  // - |message_.component_type_id| must be the id of a scalar type
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // Adds an OpTypeVector instruction to the module, with component type
+  // |message_.component_type_id| and |message_.component_count| components,
+  // with result id |message_.fresh_id|.
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationAddTypeVector message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_VECTOR_H_

+ 2 - 0
3rdparty/spirv-tools/source/fuzz/transformation_copy_object.h

@@ -47,6 +47,8 @@ class TransformationCopyObject : public Transformation {
   // - It must be legal to insert an OpCopyObject instruction directly
   //   before 'inst'.
   // - |message_.object| must be available directly before 'inst'.
+  // - |message_.object| must not be a null pointer or undefined pointer (so as
+  //   to make it legal to load from copied pointers).
   bool IsApplicable(opt::IRContext* context,
                     const FactManager& fact_manager) const override;
 

+ 81 - 0
3rdparty/spirv-tools/source/fuzz/transformation_merge_blocks.cpp

@@ -0,0 +1,81 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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.
+
+#include "source/fuzz/transformation_merge_blocks.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/opt/block_merge_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationMergeBlocks::TransformationMergeBlocks(
+    const spvtools::fuzz::protobufs::TransformationMergeBlocks& message)
+    : message_(message) {}
+
+TransformationMergeBlocks::TransformationMergeBlocks(uint32_t block_id) {
+  message_.set_block_id(block_id);
+}
+
+bool TransformationMergeBlocks::IsApplicable(
+    opt::IRContext* context,
+    const spvtools::fuzz::FactManager& /*unused*/) const {
+  auto second_block = fuzzerutil::MaybeFindBlock(context, message_.block_id());
+  // The given block must exist.
+  if (!second_block) {
+    return false;
+  }
+  // The block must have just one predecessor.
+  auto predecessors = context->cfg()->preds(second_block->id());
+  if (predecessors.size() != 1) {
+    return false;
+  }
+  auto first_block = context->cfg()->block(predecessors.at(0));
+
+  return opt::blockmergeutil::CanMergeWithSuccessor(context, first_block);
+}
+
+void TransformationMergeBlocks::Apply(
+    opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
+  auto second_block = fuzzerutil::MaybeFindBlock(context, message_.block_id());
+  auto first_block =
+      context->cfg()->block(context->cfg()->preds(second_block->id()).at(0));
+
+  auto function = first_block->GetParent();
+  // We need an iterator pointing to the predecessor, hence the loop.
+  for (auto bi = function->begin(); bi != function->end(); ++bi) {
+    if (bi->id() == first_block->id()) {
+      assert(opt::blockmergeutil::CanMergeWithSuccessor(context, &*bi) &&
+             "Because 'Apply' should only be invoked if 'IsApplicable' holds, "
+             "it must be possible to merge |bi| with its successor.");
+      opt::blockmergeutil::MergeWithSuccessor(context, function, bi);
+      // Invalidate all analyses, since we have changed the module
+      // significantly.
+      context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+      return;
+    }
+  }
+  assert(false &&
+         "Control should not reach here - we should always find the desired "
+         "block");
+}
+
+protobufs::Transformation TransformationMergeBlocks::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_merge_blocks() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 54 - 0
3rdparty/spirv-tools/source/fuzz/transformation_merge_blocks.h

@@ -0,0 +1,54 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_MERGE_BLOCKS_H_
+#define SOURCE_FUZZ_TRANSFORMATION_MERGE_BLOCKS_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationMergeBlocks : public Transformation {
+ public:
+  explicit TransformationMergeBlocks(
+      const protobufs::TransformationMergeBlocks& message);
+
+  TransformationMergeBlocks(uint32_t block_id);
+
+  // - |message_.block_id| must be the id of a block, b
+  // - b must have a single predecessor, a
+  // - b must be the sole successor of a
+  // - Replacing a with the merge of a and b (and removing b) must lead to a
+  //   valid module
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // The contents of b are merged into a, and a's terminator is replaced with
+  // the terminator of b.  Block b is removed from the module.
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationMergeBlocks message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_MERGE_BLOCKS_H_

+ 943 - 0
3rdparty/spirv-tools/source/fuzz/transformation_outline_function.cpp

@@ -0,0 +1,943 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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.
+
+#include "source/fuzz/transformation_outline_function.h"
+
+#include <set>
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+namespace {
+
+std::map<uint32_t, uint32_t> PairSequenceToMap(
+    const google::protobuf::RepeatedPtrField<protobufs::UInt32Pair>&
+        pair_sequence) {
+  std::map<uint32_t, uint32_t> result;
+  for (auto& pair : pair_sequence) {
+    result[pair.first()] = pair.second();
+  }
+  return result;
+}
+
+}  // namespace
+
+TransformationOutlineFunction::TransformationOutlineFunction(
+    const spvtools::fuzz::protobufs::TransformationOutlineFunction& message)
+    : message_(message) {}
+
+TransformationOutlineFunction::TransformationOutlineFunction(
+    uint32_t entry_block, uint32_t exit_block,
+    uint32_t new_function_struct_return_type_id, uint32_t new_function_type_id,
+    uint32_t new_function_id, uint32_t new_function_region_entry_block,
+    uint32_t new_caller_result_id, uint32_t new_callee_result_id,
+    std::map<uint32_t, uint32_t>&& input_id_to_fresh_id,
+    std::map<uint32_t, uint32_t>&& output_id_to_fresh_id) {
+  message_.set_entry_block(entry_block);
+  message_.set_exit_block(exit_block);
+  message_.set_new_function_struct_return_type_id(
+      new_function_struct_return_type_id);
+  message_.set_new_function_type_id(new_function_type_id);
+  message_.set_new_function_id(new_function_id);
+  message_.set_new_function_region_entry_block(new_function_region_entry_block);
+  message_.set_new_caller_result_id(new_caller_result_id);
+  message_.set_new_callee_result_id(new_callee_result_id);
+  for (auto& entry : input_id_to_fresh_id) {
+    protobufs::UInt32Pair pair;
+    pair.set_first(entry.first);
+    pair.set_second(entry.second);
+    *message_.add_input_id_to_fresh_id() = pair;
+  }
+  for (auto& entry : output_id_to_fresh_id) {
+    protobufs::UInt32Pair pair;
+    pair.set_first(entry.first);
+    pair.set_second(entry.second);
+    *message_.add_output_id_to_fresh_id() = pair;
+  }
+}
+
+bool TransformationOutlineFunction::IsApplicable(
+    opt::IRContext* context,
+    const spvtools::fuzz::FactManager& /*unused*/) const {
+  std::set<uint32_t> ids_used_by_this_transformation;
+
+  // The various new ids used by the transformation must be fresh and distinct.
+
+  if (!CheckIdIsFreshAndNotUsedByThisTransformation(
+          message_.new_function_struct_return_type_id(), context,
+          &ids_used_by_this_transformation)) {
+    return false;
+  }
+
+  if (!CheckIdIsFreshAndNotUsedByThisTransformation(
+          message_.new_function_type_id(), context,
+          &ids_used_by_this_transformation)) {
+    return false;
+  }
+
+  if (!CheckIdIsFreshAndNotUsedByThisTransformation(
+          message_.new_function_id(), context,
+          &ids_used_by_this_transformation)) {
+    return false;
+  }
+
+  if (!CheckIdIsFreshAndNotUsedByThisTransformation(
+          message_.new_function_region_entry_block(), context,
+          &ids_used_by_this_transformation)) {
+    return false;
+  }
+
+  if (!CheckIdIsFreshAndNotUsedByThisTransformation(
+          message_.new_caller_result_id(), context,
+          &ids_used_by_this_transformation)) {
+    return false;
+  }
+
+  if (!CheckIdIsFreshAndNotUsedByThisTransformation(
+          message_.new_callee_result_id(), context,
+          &ids_used_by_this_transformation)) {
+    return false;
+  }
+
+  for (auto& pair : message_.input_id_to_fresh_id()) {
+    if (!CheckIdIsFreshAndNotUsedByThisTransformation(
+            pair.second(), context, &ids_used_by_this_transformation)) {
+      return false;
+    }
+  }
+
+  for (auto& pair : message_.output_id_to_fresh_id()) {
+    if (!CheckIdIsFreshAndNotUsedByThisTransformation(
+            pair.second(), context, &ids_used_by_this_transformation)) {
+      return false;
+    }
+  }
+
+  // The entry and exit block ids must indeed refer to blocks.
+  for (auto block_id : {message_.entry_block(), message_.exit_block()}) {
+    auto block_label = context->get_def_use_mgr()->GetDef(block_id);
+    if (!block_label || block_label->opcode() != SpvOpLabel) {
+      return false;
+    }
+  }
+
+  auto entry_block = context->cfg()->block(message_.entry_block());
+  auto exit_block = context->cfg()->block(message_.exit_block());
+
+  // The entry block cannot start with OpVariable - this would mean that
+  // outlining would remove a variable from the function containing the region
+  // being outlined.
+  if (entry_block->begin()->opcode() == SpvOpVariable) {
+    return false;
+  }
+
+  // For simplicity, we do not allow the entry block to be a loop header.
+  if (entry_block->GetLoopMergeInst()) {
+    return false;
+  }
+
+  // For simplicity, we do not allow the exit block to be a merge block or
+  // continue target.
+  if (fuzzerutil::IsMergeOrContinue(context, exit_block->id())) {
+    return false;
+  }
+
+  // The entry block cannot start with OpPhi.  This is to keep the
+  // transformation logic simple.  (Another transformation to split the OpPhis
+  // from a block could be applied to avoid this scenario.)
+  if (entry_block->begin()->opcode() == SpvOpPhi) {
+    return false;
+  }
+
+  // The block must be in the same function.
+  if (entry_block->GetParent() != exit_block->GetParent()) {
+    return false;
+  }
+
+  // The entry block must dominate the exit block.
+  auto dominator_analysis =
+      context->GetDominatorAnalysis(entry_block->GetParent());
+  if (!dominator_analysis->Dominates(entry_block, exit_block)) {
+    return false;
+  }
+
+  // The exit block must post-dominate the entry block.
+  auto postdominator_analysis =
+      context->GetPostDominatorAnalysis(entry_block->GetParent());
+  if (!postdominator_analysis->Dominates(exit_block, entry_block)) {
+    return false;
+  }
+
+  // Find all the blocks dominated by |message_.entry_block| and post-dominated
+  // by |message_.exit_block|.
+  auto region_set = GetRegionBlocks(
+      context, entry_block = context->cfg()->block(message_.entry_block()),
+      exit_block = context->cfg()->block(message_.exit_block()));
+
+  // Check whether |region_set| really is a single-entry single-exit region, and
+  // also check whether structured control flow constructs and their merge
+  // and continue constructs are either wholly in or wholly out of the region -
+  // e.g. avoid the situation where the region contains the head of a loop but
+  // not the loop's continue construct.
+  //
+  // This is achieved by going through every block in the function that contains
+  // the region.
+  for (auto& block : *entry_block->GetParent()) {
+    if (&block == exit_block) {
+      // It is OK (and typically expected) for the exit block of the region to
+      // have successors outside the region.  It is also OK for the exit block
+      // to head a structured control flow construct - the block containing the
+      // call to the outlined function will end up heading this construct if
+      // outlining takes place.
+      continue;
+    }
+
+    if (region_set.count(&block) != 0) {
+      // The block is in the region and is not the region's exit block.  Let's
+      // see whether all of the block's successors are in the region.  If they
+      // are not, the region is not single-entry single-exit.
+      bool all_successors_in_region = true;
+      block.WhileEachSuccessorLabel([&all_successors_in_region, context,
+                                     &region_set](uint32_t successor) -> bool {
+        if (region_set.count(context->cfg()->block(successor)) == 0) {
+          all_successors_in_region = false;
+          return false;
+        }
+        return true;
+      });
+      if (!all_successors_in_region) {
+        return false;
+      }
+    }
+
+    if (auto merge = block.GetMergeInst()) {
+      // The block is a loop or selection header -- the header and its
+      // associated merge block had better both be in the region or both be
+      // outside the region.
+      auto merge_block = context->cfg()->block(merge->GetSingleWordOperand(0));
+      if (region_set.count(&block) != region_set.count(merge_block)) {
+        return false;
+      }
+    }
+
+    if (auto loop_merge = block.GetLoopMergeInst()) {
+      // Similar to the above, but for the continue target of a loop.
+      auto continue_target =
+          context->cfg()->block(loop_merge->GetSingleWordOperand(1));
+      if (continue_target != exit_block &&
+          region_set.count(&block) != region_set.count(continue_target)) {
+        return false;
+      }
+    }
+  }
+
+  // For each region input id, i.e. every id defined outside the region but
+  // used inside the region, ...
+  std::map<uint32_t, uint32_t> input_id_to_fresh_id_map =
+      PairSequenceToMap(message_.input_id_to_fresh_id());
+  for (auto id : GetRegionInputIds(context, region_set, exit_block)) {
+    // There needs to be a corresponding fresh id to be used as a function
+    // parameter.
+    if (input_id_to_fresh_id_map.count(id) == 0) {
+      return false;
+    }
+    // Furthermore, if the input id has pointer type it must be an OpVariable
+    // or OpFunctionParameter.
+    auto input_id_inst = context->get_def_use_mgr()->GetDef(id);
+    if (context->get_def_use_mgr()
+            ->GetDef(input_id_inst->type_id())
+            ->opcode() == SpvOpTypePointer) {
+      switch (input_id_inst->opcode()) {
+        case SpvOpFunctionParameter:
+        case SpvOpVariable:
+          // These are OK.
+          break;
+        default:
+          // Anything else is not OK.
+          return false;
+      }
+    }
+  }
+
+  // For each region output id -- i.e. every id defined inside the region but
+  // used outside the region -- there needs to be a corresponding fresh id that
+  // can hold the value for this id computed in the outlined function.
+  std::map<uint32_t, uint32_t> output_id_to_fresh_id_map =
+      PairSequenceToMap(message_.output_id_to_fresh_id());
+  for (auto id : GetRegionOutputIds(context, region_set, exit_block)) {
+    if (output_id_to_fresh_id_map.count(id) == 0) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+void TransformationOutlineFunction::Apply(
+    opt::IRContext* context, spvtools::fuzz::FactManager* fact_manager) const {
+  // The entry block for the region before outlining.
+  auto original_region_entry_block =
+      context->cfg()->block(message_.entry_block());
+
+  // The exit block for the region before outlining.
+  auto original_region_exit_block =
+      context->cfg()->block(message_.exit_block());
+
+  // The single-entry single-exit region defined by |message_.entry_block| and
+  // |message_.exit_block|.
+  std::set<opt::BasicBlock*> region_blocks = GetRegionBlocks(
+      context, original_region_entry_block, original_region_exit_block);
+
+  // Input and output ids for the region being outlined.
+  std::vector<uint32_t> region_input_ids =
+      GetRegionInputIds(context, region_blocks, original_region_exit_block);
+  std::vector<uint32_t> region_output_ids =
+      GetRegionOutputIds(context, region_blocks, original_region_exit_block);
+
+  // Maps from input and output ids to fresh ids.
+  std::map<uint32_t, uint32_t> input_id_to_fresh_id_map =
+      PairSequenceToMap(message_.input_id_to_fresh_id());
+  std::map<uint32_t, uint32_t> output_id_to_fresh_id_map =
+      PairSequenceToMap(message_.output_id_to_fresh_id());
+
+  UpdateModuleIdBoundForFreshIds(context, input_id_to_fresh_id_map,
+                                 output_id_to_fresh_id_map);
+
+  // Construct a map that associates each output id with its type id.
+  std::map<uint32_t, uint32_t> output_id_to_type_id;
+  for (uint32_t output_id : region_output_ids) {
+    output_id_to_type_id[output_id] =
+        context->get_def_use_mgr()->GetDef(output_id)->type_id();
+  }
+
+  // The region will be collapsed to a single block that calls a function
+  // containing the outlined region.  This block needs to end with whatever
+  // the exit block of the region ended with before outlining.  We thus clone
+  // the terminator of the region's exit block, and the merge instruction for
+  // the block if there is one, so that we can append them to the end of the
+  // collapsed block later.
+  std::unique_ptr<opt::Instruction> cloned_exit_block_terminator =
+      std::unique_ptr<opt::Instruction>(
+          original_region_exit_block->terminator()->Clone(context));
+  std::unique_ptr<opt::Instruction> cloned_exit_block_merge =
+      original_region_exit_block->GetMergeInst()
+          ? std::unique_ptr<opt::Instruction>(
+                original_region_exit_block->GetMergeInst()->Clone(context))
+          : nullptr;
+
+  // Make a function prototype for the outlined function, which involves
+  // figuring out its required type.
+  std::unique_ptr<opt::Function> outlined_function =
+      PrepareFunctionPrototype(region_input_ids, region_output_ids,
+                               input_id_to_fresh_id_map, context, fact_manager);
+
+  // If the original function was livesafe, the new function should also be
+  // livesafe.
+  if (fact_manager->FunctionIsLivesafe(
+          original_region_entry_block->GetParent()->result_id())) {
+    fact_manager->AddFactFunctionIsLivesafe(message_.new_function_id());
+  }
+
+  // Adapt the region to be outlined so that its input ids are replaced with the
+  // ids of the outlined function's input parameters, and so that output ids
+  // are similarly remapped.
+  RemapInputAndOutputIdsInRegion(
+      context, *original_region_exit_block, region_blocks, region_input_ids,
+      region_output_ids, input_id_to_fresh_id_map, output_id_to_fresh_id_map);
+
+  // Fill out the body of the outlined function according to the region that is
+  // being outlined.
+  PopulateOutlinedFunction(*original_region_entry_block,
+                           *original_region_exit_block, region_blocks,
+                           region_output_ids, output_id_to_fresh_id_map,
+                           context, outlined_function.get(), fact_manager);
+
+  // Collapse the region that has been outlined into a function down to a single
+  // block that calls said function.
+  ShrinkOriginalRegion(
+      context, region_blocks, region_input_ids, region_output_ids,
+      output_id_to_type_id, outlined_function->type_id(),
+      std::move(cloned_exit_block_merge),
+      std::move(cloned_exit_block_terminator), original_region_entry_block);
+
+  // Add the outlined function to the module.
+  context->module()->AddFunction(std::move(outlined_function));
+
+  // Major surgery has been conducted on the module, so invalidate all analyses.
+  context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationOutlineFunction::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_outline_function() = message_;
+  return result;
+}
+
+std::vector<uint32_t> TransformationOutlineFunction::GetRegionInputIds(
+    opt::IRContext* context, const std::set<opt::BasicBlock*>& region_set,
+    opt::BasicBlock* region_exit_block) {
+  std::vector<uint32_t> result;
+
+  auto enclosing_function = region_exit_block->GetParent();
+
+  // Consider each parameter of the function containing the region.
+  enclosing_function->ForEachParam([context, &region_set, &result](
+                                       opt::Instruction* function_parameter) {
+    // Consider every use of the parameter.
+    context->get_def_use_mgr()->WhileEachUse(
+        function_parameter, [context, function_parameter, &region_set, &result](
+                                opt::Instruction* use, uint32_t /*unused*/) {
+          // Get the block, if any, in which the parameter is used.
+          auto use_block = context->get_instr_block(use);
+          // If the use is in a block that lies within the region, the
+          // parameter is an input id for the region.
+          if (use_block && region_set.count(use_block) != 0) {
+            result.push_back(function_parameter->result_id());
+            return false;
+          }
+          return true;
+        });
+  });
+
+  // Consider all definitions in the function that might turn out to be input
+  // ids.
+  for (auto& block : *enclosing_function) {
+    std::vector<opt::Instruction*> candidate_input_ids_for_block;
+    if (region_set.count(&block) == 0) {
+      // All instructions in blocks outside the region are candidate's for
+      // generating input ids.
+      for (auto& inst : block) {
+        candidate_input_ids_for_block.push_back(&inst);
+      }
+    } else {
+      // Blocks in the region cannot generate input ids.
+      continue;
+    }
+
+    // Consider each candidate input id to check whether it is used in the
+    // region.
+    for (auto& inst : candidate_input_ids_for_block) {
+      context->get_def_use_mgr()->WhileEachUse(
+          inst,
+          [context, &inst, region_exit_block, &region_set, &result](
+              opt::Instruction* use, uint32_t /*unused*/) -> bool {
+
+            // Find the block in which this id use occurs, recording the id as
+            // an input id if the block is outside the region, with some
+            // exceptions detailed below.
+            auto use_block = context->get_instr_block(use);
+
+            if (!use_block) {
+              // There might be no containing block, e.g. if the use is in a
+              // decoration.
+              return true;
+            }
+
+            if (region_set.count(use_block) == 0) {
+              // The use is not in the region: this does not make it an input
+              // id.
+              return true;
+            }
+
+            if (use_block == region_exit_block && use->IsBlockTerminator()) {
+              // We do not regard uses in the exit block terminator as input
+              // ids, as this terminator does not get outlined.
+              return true;
+            }
+
+            result.push_back(inst->result_id());
+            return false;
+          });
+    }
+  }
+  return result;
+}
+
+std::vector<uint32_t> TransformationOutlineFunction::GetRegionOutputIds(
+    opt::IRContext* context, const std::set<opt::BasicBlock*>& region_set,
+    opt::BasicBlock* region_exit_block) {
+  std::vector<uint32_t> result;
+
+  // Consider each block in the function containing the region.
+  for (auto& block : *region_exit_block->GetParent()) {
+    if (region_set.count(&block) == 0) {
+      // Skip blocks that are not in the region.
+      continue;
+    }
+    // Consider each use of each instruction defined in the block.
+    for (auto& inst : block) {
+      context->get_def_use_mgr()->WhileEachUse(
+          &inst,
+          [&region_set, context, &inst, region_exit_block, &result](
+              opt::Instruction* use, uint32_t /*unused*/) -> bool {
+
+            // Find the block in which this id use occurs, recording the id as
+            // an output id if the block is outside the region, with some
+            // exceptions detailed below.
+            auto use_block = context->get_instr_block(use);
+
+            if (!use_block) {
+              // There might be no containing block, e.g. if the use is in a
+              // decoration.
+              return true;
+            }
+
+            if (region_set.count(use_block) != 0) {
+              // The use is in the region.
+              if (use_block != region_exit_block || !use->IsBlockTerminator()) {
+                // Furthermore, the use is not in the terminator of the region's
+                // exit block.
+                return true;
+              }
+            }
+
+            result.push_back(inst.result_id());
+            return false;
+          });
+    }
+  }
+  return result;
+}
+
+std::set<opt::BasicBlock*> TransformationOutlineFunction::GetRegionBlocks(
+    opt::IRContext* context, opt::BasicBlock* entry_block,
+    opt::BasicBlock* exit_block) {
+  auto enclosing_function = entry_block->GetParent();
+  auto dominator_analysis = context->GetDominatorAnalysis(enclosing_function);
+  auto postdominator_analysis =
+      context->GetPostDominatorAnalysis(enclosing_function);
+
+  std::set<opt::BasicBlock*> result;
+  for (auto& block : *enclosing_function) {
+    if (dominator_analysis->Dominates(entry_block, &block) &&
+        postdominator_analysis->Dominates(exit_block, &block)) {
+      result.insert(&block);
+    }
+  }
+  return result;
+}
+
+std::unique_ptr<opt::Function>
+TransformationOutlineFunction::PrepareFunctionPrototype(
+    const std::vector<uint32_t>& region_input_ids,
+    const std::vector<uint32_t>& region_output_ids,
+    const std::map<uint32_t, uint32_t>& input_id_to_fresh_id_map,
+    opt::IRContext* context, FactManager* fact_manager) const {
+  uint32_t return_type_id = 0;
+  uint32_t function_type_id = 0;
+
+  // First, try to find an existing function type that is suitable.  This is
+  // only possible if the region generates no output ids; if it generates output
+  // ids we are going to make a new struct for those, and since that struct does
+  // not exist there cannot already be a function type with this struct as its
+  // return type.
+  if (region_output_ids.empty()) {
+    std::vector<uint32_t> return_and_parameter_types;
+    opt::analysis::Void void_type;
+    return_type_id = context->get_type_mgr()->GetId(&void_type);
+    return_and_parameter_types.push_back(return_type_id);
+    for (auto id : region_input_ids) {
+      return_and_parameter_types.push_back(
+          context->get_def_use_mgr()->GetDef(id)->type_id());
+    }
+    function_type_id =
+        fuzzerutil::FindFunctionType(context, return_and_parameter_types);
+  }
+
+  // If no existing function type was found, we need to create one.
+  if (function_type_id == 0) {
+    assert(
+        ((return_type_id == 0) == !region_output_ids.empty()) &&
+        "We should only have set the return type if there are no output ids.");
+    // If the region generates output ids, we need to make a struct with one
+    // field per output id.
+    if (!region_output_ids.empty()) {
+      opt::Instruction::OperandList struct_member_types;
+      for (uint32_t output_id : region_output_ids) {
+        auto output_id_type =
+            context->get_def_use_mgr()->GetDef(output_id)->type_id();
+        struct_member_types.push_back({SPV_OPERAND_TYPE_ID, {output_id_type}});
+      }
+      // Add a new struct type to the module.
+      context->module()->AddType(MakeUnique<opt::Instruction>(
+          context, SpvOpTypeStruct, 0,
+          message_.new_function_struct_return_type_id(),
+          std::move(struct_member_types)));
+      // The return type for the function is the newly-created struct.
+      return_type_id = message_.new_function_struct_return_type_id();
+    }
+    assert(
+        return_type_id != 0 &&
+        "We should either have a void return type, or have created a struct.");
+
+    // The region's input ids dictate the parameter types to the function.
+    opt::Instruction::OperandList function_type_operands;
+    function_type_operands.push_back({SPV_OPERAND_TYPE_ID, {return_type_id}});
+    for (auto id : region_input_ids) {
+      function_type_operands.push_back(
+          {SPV_OPERAND_TYPE_ID,
+           {context->get_def_use_mgr()->GetDef(id)->type_id()}});
+    }
+    // Add a new function type to the module, and record that this is the type
+    // id for the new function.
+    context->module()->AddType(MakeUnique<opt::Instruction>(
+        context, SpvOpTypeFunction, 0, message_.new_function_type_id(),
+        function_type_operands));
+    function_type_id = message_.new_function_type_id();
+  }
+
+  // Create a new function with |message_.new_function_id| as the function id,
+  // and the return type and function type prepared above.
+  std::unique_ptr<opt::Function> outlined_function =
+      MakeUnique<opt::Function>(MakeUnique<opt::Instruction>(
+          context, SpvOpFunction, return_type_id, message_.new_function_id(),
+          opt::Instruction::OperandList(
+              {{spv_operand_type_t ::SPV_OPERAND_TYPE_LITERAL_INTEGER,
+                {SpvFunctionControlMaskNone}},
+               {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
+                {function_type_id}}})));
+
+  // Add one parameter to the function for each input id, using the fresh ids
+  // provided in |input_id_to_fresh_id_map|.
+  for (auto id : region_input_ids) {
+    outlined_function->AddParameter(MakeUnique<opt::Instruction>(
+        context, SpvOpFunctionParameter,
+        context->get_def_use_mgr()->GetDef(id)->type_id(),
+        input_id_to_fresh_id_map.at(id), opt::Instruction::OperandList()));
+    // If the input id is an arbitrary-valued variable, the same should be true
+    // of the corresponding parameter.
+    if (fact_manager->VariableValueIsArbitrary(id)) {
+      fact_manager->AddFactValueOfVariableIsArbitrary(
+          input_id_to_fresh_id_map.at(id));
+    }
+  }
+
+  return outlined_function;
+}
+
+void TransformationOutlineFunction::UpdateModuleIdBoundForFreshIds(
+    opt::IRContext* context,
+    const std::map<uint32_t, uint32_t>& input_id_to_fresh_id_map,
+    const std::map<uint32_t, uint32_t>& output_id_to_fresh_id_map) const {
+  // Enlarge the module's id bound as needed to accommodate the various fresh
+  // ids associated with the transformation.
+  fuzzerutil::UpdateModuleIdBound(
+      context, message_.new_function_struct_return_type_id());
+  fuzzerutil::UpdateModuleIdBound(context, message_.new_function_type_id());
+  fuzzerutil::UpdateModuleIdBound(context, message_.new_function_id());
+  fuzzerutil::UpdateModuleIdBound(context,
+                                  message_.new_function_region_entry_block());
+  fuzzerutil::UpdateModuleIdBound(context, message_.new_caller_result_id());
+  fuzzerutil::UpdateModuleIdBound(context, message_.new_callee_result_id());
+
+  for (auto& entry : input_id_to_fresh_id_map) {
+    fuzzerutil::UpdateModuleIdBound(context, entry.second);
+  }
+
+  for (auto& entry : output_id_to_fresh_id_map) {
+    fuzzerutil::UpdateModuleIdBound(context, entry.second);
+  }
+}
+
+void TransformationOutlineFunction::RemapInputAndOutputIdsInRegion(
+    opt::IRContext* context, const opt::BasicBlock& original_region_exit_block,
+    const std::set<opt::BasicBlock*>& region_blocks,
+    const std::vector<uint32_t>& region_input_ids,
+    const std::vector<uint32_t>& region_output_ids,
+    const std::map<uint32_t, uint32_t>& input_id_to_fresh_id_map,
+    const std::map<uint32_t, uint32_t>& output_id_to_fresh_id_map) const {
+  // Change all uses of input ids inside the region to the corresponding fresh
+  // ids that will ultimately be parameters of the outlined function.
+  // This is done by considering each region input id in turn.
+  for (uint32_t id : region_input_ids) {
+    // We then consider each use of the input id.
+    context->get_def_use_mgr()->ForEachUse(
+        id, [context, id, &input_id_to_fresh_id_map, region_blocks](
+                opt::Instruction* use, uint32_t operand_index) {
+          // Find the block in which this use of the input id occurs.
+          opt::BasicBlock* use_block = context->get_instr_block(use);
+          // We want to rewrite the use id if its block occurs in the outlined
+          // region.
+          if (region_blocks.count(use_block) != 0) {
+            // Rewrite this use of the input id.
+            use->SetOperand(operand_index, {input_id_to_fresh_id_map.at(id)});
+          }
+        });
+  }
+
+  // Change each definition of a region output id to define the corresponding
+  // fresh ids that will store intermediate value for the output ids.  Also
+  // change all uses of the output id located in the outlined region.
+  // This is done by considering each region output id in turn.
+  for (uint32_t id : region_output_ids) {
+    // First consider each use of the output id and update the relevant uses.
+    context->get_def_use_mgr()->ForEachUse(
+        id,
+        [context, &original_region_exit_block, id, &output_id_to_fresh_id_map,
+         region_blocks](opt::Instruction* use, uint32_t operand_index) {
+          // Find the block in which this use of the output id occurs.
+          auto use_block = context->get_instr_block(use);
+          // We want to rewrite the use id if its block occurs in the outlined
+          // region, with one exception: the terminator of the exit block of
+          // the region is going to remain in the original function, so if the
+          // use appears in such a terminator instruction we leave it alone.
+          if (
+              // The block is in the region ...
+              region_blocks.count(use_block) != 0 &&
+              // ... and the use is not in the terminator instruction of the
+              // region's exit block.
+              !(use_block == &original_region_exit_block &&
+                use->IsBlockTerminator())) {
+            // Rewrite this use of the output id.
+            use->SetOperand(operand_index, {output_id_to_fresh_id_map.at(id)});
+          }
+        });
+
+    // Now change the instruction that defines the output id so that it instead
+    // defines the corresponding fresh id.  We do this after changing all the
+    // uses so that the definition of the original id is still registered when
+    // we analyse its uses.
+    context->get_def_use_mgr()->GetDef(id)->SetResultId(
+        output_id_to_fresh_id_map.at(id));
+  }
+}
+
+void TransformationOutlineFunction::PopulateOutlinedFunction(
+    const opt::BasicBlock& original_region_entry_block,
+    const opt::BasicBlock& original_region_exit_block,
+    const std::set<opt::BasicBlock*>& region_blocks,
+    const std::vector<uint32_t>& region_output_ids,
+    const std::map<uint32_t, uint32_t>& output_id_to_fresh_id_map,
+    opt::IRContext* context, opt::Function* outlined_function,
+    FactManager* fact_manager) const {
+  // When we create the exit block for the outlined region, we use this pointer
+  // to track of it so that we can manipulate it later.
+  opt::BasicBlock* outlined_region_exit_block = nullptr;
+
+  // The region entry block in the new function is identical to the entry block
+  // of the region being outlined, except that it has
+  // |message_.new_function_region_entry_block| as its id.
+  std::unique_ptr<opt::BasicBlock> outlined_region_entry_block =
+      MakeUnique<opt::BasicBlock>(MakeUnique<opt::Instruction>(
+          context, SpvOpLabel, 0, message_.new_function_region_entry_block(),
+          opt::Instruction::OperandList()));
+  outlined_region_entry_block->SetParent(outlined_function);
+
+  // If the original region's entry block was dead, the outlined region's entry
+  // block is also dead.
+  if (fact_manager->BlockIsDead(original_region_entry_block.id())) {
+    fact_manager->AddFactBlockIsDead(outlined_region_entry_block->id());
+  }
+
+  if (&original_region_entry_block == &original_region_exit_block) {
+    outlined_region_exit_block = outlined_region_entry_block.get();
+  }
+
+  for (auto& inst : original_region_entry_block) {
+    outlined_region_entry_block->AddInstruction(
+        std::unique_ptr<opt::Instruction>(inst.Clone(context)));
+  }
+  outlined_function->AddBasicBlock(std::move(outlined_region_entry_block));
+
+  // We now go through the single-entry single-exit region defined by the entry
+  // and exit blocks, adding clones of all blocks to the new function.
+
+  // Consider every block in the enclosing function.
+  auto enclosing_function = original_region_entry_block.GetParent();
+  for (auto block_it = enclosing_function->begin();
+       block_it != enclosing_function->end();) {
+    // Skip the region's entry block - we already dealt with it above.
+    if (region_blocks.count(&*block_it) == 0 ||
+        &*block_it == &original_region_entry_block) {
+      ++block_it;
+      continue;
+    }
+    // Clone the block so that it can be added to the new function.
+    auto cloned_block =
+        std::unique_ptr<opt::BasicBlock>(block_it->Clone(context));
+
+    // If this is the region's exit block, then the cloned block is the outlined
+    // region's exit block.
+    if (&*block_it == &original_region_exit_block) {
+      assert(outlined_region_exit_block == nullptr &&
+             "We should not yet have encountered the exit block.");
+      outlined_region_exit_block = cloned_block.get();
+    }
+
+    cloned_block->SetParent(outlined_function);
+
+    // Redirect any OpPhi operands whose predecessors are the original region
+    // entry block to become the new function entry block.
+    cloned_block->ForEachPhiInst([this](opt::Instruction* phi_inst) {
+      for (uint32_t predecessor_index = 1;
+           predecessor_index < phi_inst->NumInOperands();
+           predecessor_index += 2) {
+        if (phi_inst->GetSingleWordInOperand(predecessor_index) ==
+            message_.entry_block()) {
+          phi_inst->SetInOperand(predecessor_index,
+                                 {message_.new_function_region_entry_block()});
+        }
+      }
+    });
+
+    outlined_function->AddBasicBlock(std::move(cloned_block));
+    block_it = block_it.Erase();
+  }
+  assert(outlined_region_exit_block != nullptr &&
+         "We should have encountered the region's exit block when iterating "
+         "through the function");
+
+  // We now need to adapt the exit block for the region - in the new function -
+  // so that it ends with a return.
+
+  // We first eliminate the merge instruction (if any) and the terminator for
+  // the cloned exit block.
+  for (auto inst_it = outlined_region_exit_block->begin();
+       inst_it != outlined_region_exit_block->end();) {
+    if (inst_it->opcode() == SpvOpLoopMerge ||
+        inst_it->opcode() == SpvOpSelectionMerge) {
+      inst_it = inst_it.Erase();
+    } else if (inst_it->IsBlockTerminator()) {
+      inst_it = inst_it.Erase();
+    } else {
+      ++inst_it;
+    }
+  }
+
+  // We now add either OpReturn or OpReturnValue as the cloned exit block's
+  // terminator.
+  if (region_output_ids.empty()) {
+    // The case where there are no region output ids is simple: we just add
+    // OpReturn.
+    outlined_region_exit_block->AddInstruction(MakeUnique<opt::Instruction>(
+        context, SpvOpReturn, 0, 0, opt::Instruction::OperandList()));
+  } else {
+    // In the case where there are output ids, we add an OpCompositeConstruct
+    // instruction to pack all the output values into a struct, and then an
+    // OpReturnValue instruction to return this struct.
+    opt::Instruction::OperandList struct_member_operands;
+    for (uint32_t id : region_output_ids) {
+      struct_member_operands.push_back(
+          {SPV_OPERAND_TYPE_ID, {output_id_to_fresh_id_map.at(id)}});
+    }
+    outlined_region_exit_block->AddInstruction(MakeUnique<opt::Instruction>(
+        context, SpvOpCompositeConstruct,
+        message_.new_function_struct_return_type_id(),
+        message_.new_callee_result_id(), struct_member_operands));
+    outlined_region_exit_block->AddInstruction(MakeUnique<opt::Instruction>(
+        context, SpvOpReturnValue, 0, 0,
+        opt::Instruction::OperandList(
+            {{SPV_OPERAND_TYPE_ID, {message_.new_callee_result_id()}}})));
+  }
+
+  outlined_function->SetFunctionEnd(MakeUnique<opt::Instruction>(
+      context, SpvOpFunctionEnd, 0, 0, opt::Instruction::OperandList()));
+}
+
+void TransformationOutlineFunction::ShrinkOriginalRegion(
+    opt::IRContext* context, std::set<opt::BasicBlock*>& region_blocks,
+    const std::vector<uint32_t>& region_input_ids,
+    const std::vector<uint32_t>& region_output_ids,
+    const std::map<uint32_t, uint32_t>& output_id_to_type_id,
+    uint32_t return_type_id,
+    std::unique_ptr<opt::Instruction> cloned_exit_block_merge,
+    std::unique_ptr<opt::Instruction> cloned_exit_block_terminator,
+    opt::BasicBlock* original_region_entry_block) const {
+  // Erase all blocks from the original function that are in the outlined
+  // region, except for the region's entry block.
+  //
+  // In the process, identify all references to the exit block of the region,
+  // as merge blocks, continue targets, or OpPhi predecessors, and rewrite them
+  // to refer to the region entry block (the single block to which we are
+  // shrinking the region).
+  auto enclosing_function = original_region_entry_block->GetParent();
+  for (auto block_it = enclosing_function->begin();
+       block_it != enclosing_function->end();) {
+    if (&*block_it == original_region_entry_block) {
+      ++block_it;
+    } else if (region_blocks.count(&*block_it) == 0) {
+      // The block is not in the region.  Check whether it has the last block
+      // of the region as an OpPhi predecessor, and if so change the
+      // predecessor to be the first block of the region (i.e. the block
+      // containing the call to what was outlined).
+      assert(block_it->MergeBlockIdIfAny() != message_.exit_block() &&
+             "Outlined region must not end with a merge block");
+      assert(block_it->ContinueBlockIdIfAny() != message_.exit_block() &&
+             "Outlined region must not end with a continue target");
+      block_it->ForEachPhiInst([this](opt::Instruction* phi_inst) {
+        for (uint32_t predecessor_index = 1;
+             predecessor_index < phi_inst->NumInOperands();
+             predecessor_index += 2) {
+          if (phi_inst->GetSingleWordInOperand(predecessor_index) ==
+              message_.exit_block()) {
+            phi_inst->SetInOperand(predecessor_index, {message_.entry_block()});
+          }
+        }
+      });
+      ++block_it;
+    } else {
+      // The block is in the region and is not the region's entry block: kill
+      // it.
+      block_it = block_it.Erase();
+    }
+  }
+
+  // Now erase all instructions from the region's entry block, as they have
+  // been outlined.
+  for (auto inst_it = original_region_entry_block->begin();
+       inst_it != original_region_entry_block->end();) {
+    inst_it = inst_it.Erase();
+  }
+
+  // Now we add a call to the outlined function to the region's entry block.
+  opt::Instruction::OperandList function_call_operands;
+  function_call_operands.push_back(
+      {SPV_OPERAND_TYPE_ID, {message_.new_function_id()}});
+  // The function parameters are the region input ids.
+  for (auto input_id : region_input_ids) {
+    function_call_operands.push_back({SPV_OPERAND_TYPE_ID, {input_id}});
+  }
+
+  original_region_entry_block->AddInstruction(MakeUnique<opt::Instruction>(
+      context, SpvOpFunctionCall, return_type_id,
+      message_.new_caller_result_id(), function_call_operands));
+
+  // If there are output ids, the function call will return a struct.  For each
+  // output id, we add an extract operation to pull the appropriate struct
+  // member out into an output id.
+  for (uint32_t index = 0; index < region_output_ids.size(); ++index) {
+    uint32_t output_id = region_output_ids[index];
+    original_region_entry_block->AddInstruction(MakeUnique<opt::Instruction>(
+        context, SpvOpCompositeExtract, output_id_to_type_id.at(output_id),
+        output_id,
+        opt::Instruction::OperandList(
+            {{SPV_OPERAND_TYPE_ID, {message_.new_caller_result_id()}},
+             {SPV_OPERAND_TYPE_LITERAL_INTEGER, {index}}})));
+  }
+
+  // Finally, we terminate the block with the merge instruction (if any) that
+  // used to belong to the region's exit block, and the terminator that used
+  // to belong to the region's exit block.
+  if (cloned_exit_block_merge != nullptr) {
+    original_region_entry_block->AddInstruction(
+        std::move(cloned_exit_block_merge));
+  }
+  original_region_entry_block->AddInstruction(
+      std::move(cloned_exit_block_terminator));
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 221 - 0
3rdparty/spirv-tools/source/fuzz/transformation_outline_function.h

@@ -0,0 +1,221 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_OUTLINE_FUNCTION_H_
+#define SOURCE_FUZZ_TRANSFORMATION_OUTLINE_FUNCTION_H_
+
+#include <map>
+#include <set>
+#include <vector>
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationOutlineFunction : public Transformation {
+ public:
+  explicit TransformationOutlineFunction(
+      const protobufs::TransformationOutlineFunction& message);
+
+  TransformationOutlineFunction(
+      uint32_t entry_block, uint32_t exit_block,
+      uint32_t new_function_struct_return_type_id,
+      uint32_t new_function_type_id, uint32_t new_function_id,
+      uint32_t new_function_region_entry_block, uint32_t new_caller_result_id,
+      uint32_t new_callee_result_id,
+      std::map<uint32_t, uint32_t>&& input_id_to_fresh_id,
+      std::map<uint32_t, uint32_t>&& output_id_to_fresh_id);
+
+  // - All the fresh ids occurring in the transformation must be distinct and
+  //   fresh
+  // - |message_.entry_block| and |message_.exit_block| must form a single-entry
+  //   single-exit control flow graph region
+  // - |message_.entry_block| must not start with OpVariable
+  // - |message_.entry_block| must not be a loop header
+  // - |message_.exit_block| must not be a merge block or the continue target
+  //   of a loop
+  // - A structured control flow construct must lie either completely within the
+  //   region or completely outside it
+  // - |message.entry_block| must not start with OpPhi; this is to keep the
+  //   transformation simple - another transformation should be used to split
+  //   a desired entry block that starts with OpPhi if needed
+  // - |message_.input_id_to_fresh_id| must contain an entry for every id
+  //   defined outside the region but used in the region
+  // - |message_.output_id_to_fresh_id| must contain an entry for every id
+  //   defined in the region but used outside the region
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // - A new function with id |message_.new_function_id| is added to the module.
+  // - If the region generates output ids, the return type of this function is
+  //   a new struct type with one field per output id, and with type id
+  //   |message_.new_function_struct_return_type|, otherwise the function return
+  //   types is void and |message_.new_function_struct_return_type| is not used.
+  // - If the region generates input ids, the new function has one parameter per
+  //   input id.  Fresh ids for these parameters are provided by
+  //   |message_.input_id_to_fresh_id|.
+  // - Unless the type required for the new function is already known,
+  //   |message_.new_function_type_id| is used as the type id for a new function
+  //   type, and the new function uses this type.
+  // - The new function starts with a dummy block with id
+  //   |message_.new_function_first_block|, which jumps straight to a successor
+  //   block, to avoid violating rules on what the first block in a function may
+  //   look like.
+  // - The outlined region is replaced with a single block, with the same id
+  //   as |message_.entry_block|, and which calls the new function, passing the
+  //   region's input ids as parameters.  The result is  stored in
+  //   |message_.new_caller_result_id|, which has type
+  //   |message_.new_function_struct_return_type| (unless there are
+  //   no output ids, in which case the return type is void).  The components
+  //   of this returned struct are then copied out into the region's output ids.
+  //   The block ends with the merge instruction (if any) and terminator of
+  //   |message_.exit_block|.
+  // - The body of the new function is identical to the outlined region, except
+  //   that (a) the region's entry block has id
+  //   |message_.new_function_region_entry_block|, (b) input id uses are
+  //   replaced with parameter accesses, (c) and definitions of output ids are
+  //   replaced with definitions of corresponding fresh ids provided by
+  //   |message_.output_id_to_fresh_id|, and (d) the block of the function
+  //   ends by returning a composite of type
+  //   |message_.new_function_struct_return_type| comprised of all the fresh
+  //   output ids (unless the return type is void, in which case no value is
+  //   returned.
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+  // Returns the set of blocks dominated by |entry_block| and post-dominated
+  // by |exit_block|.
+  static std::set<opt::BasicBlock*> GetRegionBlocks(
+      opt::IRContext* context, opt::BasicBlock* entry_block,
+      opt::BasicBlock* exit_block);
+
+  // Yields ids that are used in |region_set| and that are either parameters
+  // to the function containing |region_set|, or are defined by blocks of this
+  // function that are outside |region_set|.
+  //
+  // Special cases: OpPhi instructions in |region_entry_block| and the
+  // terminator of |region_exit_block| do not get outlined, therefore
+  // - id uses in OpPhi instructions in |region_entry_block| are ignored
+  // - id uses in the terminator instruction of |region_exit_block| are ignored
+  static std::vector<uint32_t> GetRegionInputIds(
+      opt::IRContext* context, const std::set<opt::BasicBlock*>& region_set,
+      opt::BasicBlock* region_exit_block);
+
+  // Yields all ids that are defined in |region_set| and used outside
+  // |region_set|.
+  //
+  // Special cases: for similar reasons as for |GetRegionInputIds|,
+  // - ids defined in the region and used in the terminator of
+  //   |region_exit_block| count as output ids
+  static std::vector<uint32_t> GetRegionOutputIds(
+      opt::IRContext* context, const std::set<opt::BasicBlock*>& region_set,
+      opt::BasicBlock* region_exit_block);
+
+ private:
+  // Ensures that the module's id bound is at least the maximum of any fresh id
+  // associated with the transformation.
+  void UpdateModuleIdBoundForFreshIds(
+      opt::IRContext* context,
+      const std::map<uint32_t, uint32_t>& input_id_to_fresh_id_map,
+      const std::map<uint32_t, uint32_t>& output_id_to_fresh_id_map) const;
+
+  // Uses |input_id_to_fresh_id_map| and |output_id_to_fresh_id_map| to convert,
+  // in the region to be outlined, all the input ids in |region_input_ids| and
+  // the output ids in |region_output_ids| to their fresh counterparts.
+  // Parameters |region_blocks| provides access to the blocks that must be
+  // modified, and |original_region_exit_block| allows for some special cases
+  // where ids should not be remapped.
+  void RemapInputAndOutputIdsInRegion(
+      opt::IRContext* context,
+      const opt::BasicBlock& original_region_exit_block,
+      const std::set<opt::BasicBlock*>& region_blocks,
+      const std::vector<uint32_t>& region_input_ids,
+      const std::vector<uint32_t>& region_output_ids,
+      const std::map<uint32_t, uint32_t>& input_id_to_fresh_id_map,
+      const std::map<uint32_t, uint32_t>& output_id_to_fresh_id_map) const;
+
+  // Produce a Function object that has the right function type and parameter
+  // declarations.  The function argument types and parameter ids are dictated
+  // by |region_input_ids| and |input_id_to_fresh_id_map|.  The function return
+  // type is dictated by |region_output_ids|.
+  //
+  // A new struct type to represent the function return type, and a new function
+  // type for the function, will be added to the module (unless suitable types
+  // are already present).
+  //
+  // Facts about the function containing the outlined region that are relevant
+  // to the new function are propagated via |fact_manager|.
+  std::unique_ptr<opt::Function> PrepareFunctionPrototype(
+      const std::vector<uint32_t>& region_input_ids,
+      const std::vector<uint32_t>& region_output_ids,
+      const std::map<uint32_t, uint32_t>& input_id_to_fresh_id_map,
+      opt::IRContext* context, FactManager* fact_manager) const;
+
+  // Creates the body of the outlined function by cloning blocks from the
+  // original region, given by |region_blocks|, adapting the cloned version
+  // of |original_region_exit_block| so that it returns something appropriate,
+  // and patching up branches to |original_region_entry_block| to refer to its
+  // clone.  Parameters |region_output_ids| and |output_id_to_fresh_id_map| are
+  // used to determine what the function should return.
+  //
+  // The |fact_manager| argument allow facts about blocks being outlined, e.g.
+  // whether they are dead blocks, to be asserted about blocks that get created
+  // during outlining.
+  void PopulateOutlinedFunction(
+      const opt::BasicBlock& original_region_entry_block,
+      const opt::BasicBlock& original_region_exit_block,
+      const std::set<opt::BasicBlock*>& region_blocks,
+      const std::vector<uint32_t>& region_output_ids,
+      const std::map<uint32_t, uint32_t>& output_id_to_fresh_id_map,
+      opt::IRContext* context, opt::Function* outlined_function,
+      FactManager* fact_manager) const;
+
+  // Shrinks the outlined region, given by |region_blocks|, down to the single
+  // block |original_region_entry_block|.  This block is itself shrunk to just
+  // contain:
+  // - any OpPhi instructions that were originally present
+  // - a call to the outlined function, with parameters provided by
+  //   |region_input_ids|
+  // - instructions to route components of the call's return value into
+  //   |region_output_ids|
+  // - The merge instruction (if any) and terminator of the original region's
+  //   exit block, given by |cloned_exit_block_merge| and
+  //   |cloned_exit_block_terminator|
+  // Parameters |output_id_to_type_id| and |return_type_id| provide the
+  // provide types for the region's output ids, and the return type of the
+  // outlined function: as the module is in an inconsistent state when this
+  // function is called, this information cannot be gotten from the def-use
+  // manager.
+  void ShrinkOriginalRegion(
+      opt::IRContext* context, std::set<opt::BasicBlock*>& region_blocks,
+      const std::vector<uint32_t>& region_input_ids,
+      const std::vector<uint32_t>& region_output_ids,
+      const std::map<uint32_t, uint32_t>& output_id_to_type_id,
+      uint32_t return_type_id,
+      std::unique_ptr<opt::Instruction> cloned_exit_block_merge,
+      std::unique_ptr<opt::Instruction> cloned_exit_block_terminator,
+      opt::BasicBlock* original_region_entry_block) const;
+
+  protobufs::TransformationOutlineFunction message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_OUTLINE_FUNCTION_H_

+ 8 - 1
3rdparty/spirv-tools/source/fuzz/transformation_split_block.cpp

@@ -80,7 +80,7 @@ bool TransformationSplitBlock::IsApplicable(
 }
 
 void TransformationSplitBlock::Apply(opt::IRContext* context,
-                                     FactManager* /*unused*/) const {
+                                     FactManager* fact_manager) const {
   opt::Instruction* instruction_to_split_before =
       FindInstruction(message_.instruction_to_split_before(), context);
   opt::BasicBlock* block_to_split =
@@ -114,6 +114,13 @@ void TransformationSplitBlock::Apply(opt::IRContext* context,
            "one predecessor.");
     phi_inst->SetInOperand(1, {block_to_split->id()});
   });
+
+  // If the block being split was dead, the new block arising from the split is
+  // also dead.
+  if (fact_manager->BlockIsDead(block_to_split->id())) {
+    fact_manager->AddFactBlockIsDead(message_.fresh_id());
+  }
+
   // Invalidate all analyses
   context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
 }

+ 1 - 0
3rdparty/spirv-tools/source/fuzz/transformation_split_block.h

@@ -48,6 +48,7 @@ class TransformationSplitBlock : public Transformation {
   // - All instructions of 'blk' from 'inst' onwards are moved into the new
   //   block.
   // - 'blk' is made to jump unconditionally to the new block.
+  // - If 'blk' was dead, the new block is also dead.
   void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
 
   protobufs::Transformation ToMessage() const override;

+ 20 - 25
3rdparty/spirv-tools/source/link/linker.cpp

@@ -137,8 +137,6 @@ spv_result_t CheckImportExportCompatibility(const MessageConsumer& consumer,
 // TODO(pierremoreau): Linkage attributes applied by a group decoration are
 //                     currently not handled. (You could have a group being
 //                     applied to a single ID.)
-// TODO(pierremoreau): Run a pass for removing dead instructions, for example
-//                     OpName for prototypes of imported funcions.
 spv_result_t RemoveLinkageSpecificInstructions(
     const MessageConsumer& consumer, const LinkerOptions& options,
     const LinkageTable& linkings_to_do, DecorationManager* decoration_manager,
@@ -326,6 +324,11 @@ spv_result_t MergeModules(const MessageConsumer& consumer,
       linked_module->AddDebug3Inst(
           std::unique_ptr<Instruction>(inst.Clone(linked_context)));
 
+  for (const auto& module : input_modules)
+    for (const auto& inst : module->ext_inst_debuginfo())
+      linked_module->AddExtInstDebugInfo(
+          std::unique_ptr<Instruction>(inst.Clone(linked_context)));
+
   // If the generated module uses SPIR-V 1.1 or higher, add an
   // OpModuleProcessed instruction about the linking step.
   if (linked_module->version() >= 0x10100) {
@@ -531,24 +534,6 @@ spv_result_t RemoveLinkageSpecificInstructions(
   // TODO(pierremoreau): Remove FuncParamAttr decorations of imported
   // functions' return type.
 
-  // Remove FuncParamAttr decorations of imported functions' parameters.
-  // From the SPIR-V specification, Sec. 2.13:
-  //   When resolving imported functions, the Function Control and all Function
-  //   Parameter Attributes are taken from the function definition, and not
-  //   from the function declaration.
-  for (const auto& linking_entry : linkings_to_do) {
-    for (const auto parameter_id :
-         linking_entry.imported_symbol.parameter_ids) {
-      decoration_manager->RemoveDecorationsFrom(
-          parameter_id, [](const Instruction& inst) {
-            return (inst.opcode() == SpvOpDecorate ||
-                    inst.opcode() == SpvOpMemberDecorate) &&
-                   inst.GetSingleWordInOperand(1u) ==
-                       SpvDecorationFuncParamAttr;
-          });
-    }
-  }
-
   // Remove prototypes of imported functions
   for (const auto& linking_entry : linkings_to_do) {
     for (auto func_iter = linked_context->module()->begin();
@@ -746,24 +731,34 @@ spv_result_t Link(const Context& context, const uint32_t* const* binaries,
   opt::Pass::Status pass_res = manager.Run(&linked_context);
   if (pass_res == opt::Pass::Status::Failure) return SPV_ERROR_INVALID_DATA;
 
-  // Phase 7: Rematch import variables/functions to export variables/functions
-  for (const auto& linking_entry : linkings_to_do)
+  // Phase 7: Remove all names and decorations of import variables/functions
+  for (const auto& linking_entry : linkings_to_do) {
+    linked_context.KillNamesAndDecorates(linking_entry.imported_symbol.id);
+    for (const auto parameter_id :
+         linking_entry.imported_symbol.parameter_ids) {
+      linked_context.KillNamesAndDecorates(parameter_id);
+    }
+  }
+
+  // Phase 8: Rematch import variables/functions to export variables/functions
+  for (const auto& linking_entry : linkings_to_do) {
     linked_context.ReplaceAllUsesWith(linking_entry.imported_symbol.id,
                                       linking_entry.exported_symbol.id);
+  }
 
-  // Phase 8: Remove linkage specific instructions, such as import/export
+  // Phase 9: Remove linkage specific instructions, such as import/export
   // attributes, linkage capability, etc. if applicable
   res = RemoveLinkageSpecificInstructions(consumer, options, linkings_to_do,
                                           linked_context.get_decoration_mgr(),
                                           &linked_context);
   if (res != SPV_SUCCESS) return res;
 
-  // Phase 9: Compact the IDs used in the module
+  // Phase 10: Compact the IDs used in the module
   manager.AddPass<opt::CompactIdsPass>();
   pass_res = manager.Run(&linked_context);
   if (pass_res == opt::Pass::Status::Failure) return SPV_ERROR_INVALID_DATA;
 
-  // Phase 10: Output the module
+  // Phase 11: Output the module
   linked_context.module()->ToBinary(linked_binary, true);
 
   return SPV_SUCCESS;

+ 55 - 0
3rdparty/spirv-tools/source/operand.cpp

@@ -19,6 +19,8 @@
 
 #include <algorithm>
 
+#include "DebugInfo.h"
+#include "OpenCLDebugInfo100.h"
 #include "source/macro.h"
 #include "source/spirv_constant.h"
 #include "source/spirv_target_env.h"
@@ -29,6 +31,7 @@
 // per-item basis. https://github.com/KhronosGroup/SPIRV-Tools/issues/1195
 
 #include "operand.kinds-unified1.inc"
+#include "spirv-tools/libspirv.h"
 
 static const spv_operand_table_t kOperandTable = {
     ARRAY_SIZE(pygen_variable_OperandInfoTable),
@@ -228,6 +231,18 @@ const char* spvOperandTypeStr(spv_operand_type_t type) {
       return "debug type qualifier";
     case SPV_OPERAND_TYPE_DEBUG_OPERATION:
       return "debug operation";
+    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS:
+      return "OpenCL.DebugInfo.100 debug info flags";
+    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING:
+      return "OpenCL.DebugInfo.100 debug base type encoding";
+    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_COMPOSITE_TYPE:
+      return "OpenCL.DebugInfo.100 debug composite type";
+    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_TYPE_QUALIFIER:
+      return "OpenCL.DebugInfo.100 debug type qualifier";
+    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION:
+      return "OpenCL.DebugInfo.100 debug operation";
+    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_IMPORTED_ENTITY:
+      return "OpenCL.DebugInfo.100 debug imported entity";
 
     // The next values are for values returned from an instruction, not actually
     // an operand.  So the specific strings don't matter.  But let's add them
@@ -312,6 +327,11 @@ bool spvOperandIsConcrete(spv_operand_type_t type) {
     case SPV_OPERAND_TYPE_DEBUG_COMPOSITE_TYPE:
     case SPV_OPERAND_TYPE_DEBUG_TYPE_QUALIFIER:
     case SPV_OPERAND_TYPE_DEBUG_OPERATION:
+    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING:
+    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_COMPOSITE_TYPE:
+    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_TYPE_QUALIFIER:
+    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION:
+    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_IMPORTED_ENTITY:
       return true;
     default:
       break;
@@ -328,6 +348,7 @@ bool spvOperandIsConcreteMask(spv_operand_type_t type) {
     case SPV_OPERAND_TYPE_FUNCTION_CONTROL:
     case SPV_OPERAND_TYPE_MEMORY_ACCESS:
     case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS:
+    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS:
       return true;
     default:
       break;
@@ -493,3 +514,37 @@ std::function<bool(unsigned)> spvOperandCanBeForwardDeclaredFunction(
   }
   return out;
 }
+
+std::function<bool(unsigned)> spvDbgInfoExtOperandCanBeForwardDeclaredFunction(
+    spv_ext_inst_type_t ext_type, uint32_t key) {
+  // TODO(https://gitlab.khronos.org/spirv/SPIR-V/issues/532): Forward
+  // references for debug info instructions are still in discussion. We must
+  // update the following lines of code when we conclude the spec.
+  std::function<bool(unsigned index)> out;
+  if (ext_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) {
+    switch (OpenCLDebugInfo100Instructions(key)) {
+      case OpenCLDebugInfo100DebugFunction:
+        out = [](unsigned index) { return index == 13; };
+        break;
+      case OpenCLDebugInfo100DebugTypeComposite:
+        out = [](unsigned index) { return index >= 13; };
+        break;
+      default:
+        out = [](unsigned) { return false; };
+        break;
+    }
+  } else {
+    switch (DebugInfoInstructions(key)) {
+      case DebugInfoDebugFunction:
+        out = [](unsigned index) { return index == 13; };
+        break;
+      case DebugInfoDebugTypeComposite:
+        out = [](unsigned index) { return index >= 12; };
+        break;
+      default:
+        out = [](unsigned) { return false; };
+        break;
+    }
+  }
+  return out;
+}

+ 7 - 0
3rdparty/spirv-tools/source/operand.h

@@ -141,4 +141,11 @@ bool spvIsInIdType(spv_operand_type_t type);
 std::function<bool(unsigned)> spvOperandCanBeForwardDeclaredFunction(
     SpvOp opcode);
 
+// Takes the instruction key of a debug info extension instruction
+// and returns a function object that will return true if the index
+// of the operand can be forward declared. This function will
+// used in the SSA validation stage of the pipeline
+std::function<bool(unsigned)> spvDbgInfoExtOperandCanBeForwardDeclaredFunction(
+    spv_ext_inst_type_t ext_type, uint32_t key);
+
 #endif  // SOURCE_OPERAND_H_

+ 35 - 10
3rdparty/spirv-tools/source/opt/amd_ext_to_khr.cpp

@@ -53,12 +53,6 @@ analysis::Type* GetUIntType(IRContext* ctx) {
   return ctx->get_type_mgr()->GetRegisteredType(&int_type);
 }
 
-bool NotImplementedYet(IRContext*, Instruction*,
-                       const std::vector<const analysis::Constant*>&) {
-  assert(false && "Not implemented.");
-  return false;
-}
-
 // Returns a folding rule that replaces |op(a,b,c)| by |op(op(a,b),c)|, where
 // |op| is either min or max. |opcode| is the binary opcode in the GLSLstd450
 // extended instruction set that corresponds to the trinary instruction being
@@ -686,13 +680,13 @@ bool ReplaceCubeFaceCoord(IRContext* ctx, Instruction* inst,
   return true;
 }
 
-// A folding rule that will replace the CubeFaceCoordAMD extended
+// A folding rule that will replace the CubeFaceIndexAMD extended
 // instruction in the SPV_AMD_gcn_shader_ballot.  Returns true if the folding
 // is successful.
 //
 // The instruction
 //
-//  %result = OpExtInst %v2float %1 CubeFaceCoordAMD %input
+//  %result = OpExtInst %float %1 CubeFaceIndexAMD %input
 //
 // with
 //
@@ -705,7 +699,7 @@ bool ReplaceCubeFaceCoord(IRContext* ctx, Instruction* inst,
 //      %is_z_neg = OpFOrdLessThan %bool %z %float_0
 //      %is_y_neg = OpFOrdLessThan %bool %y %float_0
 //      %is_x_neg = OpFOrdLessThan %bool %x %float_0
-//      %amax_x_y = OpExtInst %float %n_1 FMax %ay %ax
+//      %amax_x_y = OpExtInst %float %n_1 FMax %ax %ay
 //      %is_z_max = OpFOrdGreaterThanEqual %bool %az %amax_x_y
 //        %y_gt_x = OpFOrdGreaterThanEqual %bool %ay %ax
 //        %case_z = OpSelect %float %is_z_neg %float_5 %float4
@@ -800,6 +794,37 @@ bool ReplaceCubeFaceIndex(IRContext* ctx, Instruction* inst,
   return true;
 }
 
+// A folding rule that will replace the TimeAMD extended instruction in the
+// SPV_AMD_gcn_shader_ballot.  It returns true if the folding is successful.
+// It returns False, otherwise.
+//
+// The instruction
+//
+//  %result = OpExtInst %uint64 %1 TimeAMD
+//
+// with
+//
+//  %result = OpReadClockKHR %uint64 %uint_3
+//
+// NOTE: TimeAMD uses subgroup scope (it is not a real time clock).
+bool ReplaceTimeAMD(IRContext* ctx, Instruction* inst,
+                    const std::vector<const analysis::Constant*>&) {
+  InstructionBuilder ir_builder(
+      ctx, inst,
+      IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
+  ctx->AddExtension("SPV_KHR_shader_clock");
+  ctx->AddCapability(SpvCapabilityShaderClockKHR);
+
+  inst->SetOpcode(SpvOpReadClockKHR);
+  Instruction::OperandList args;
+  uint32_t subgroup_scope_id = ir_builder.GetUintConstantId(SpvScopeSubgroup);
+  args.push_back({SPV_OPERAND_TYPE_ID, {subgroup_scope_id}});
+  inst->SetInOperands(std::move(args));
+  ctx->UpdateDefUse(inst);
+
+  return true;
+}
+
 class AmdExtFoldingRules : public FoldingRules {
  public:
   explicit AmdExtFoldingRules(IRContext* ctx) : FoldingRules(ctx) {}
@@ -869,7 +894,7 @@ class AmdExtFoldingRules : public FoldingRules {
           ReplaceCubeFaceCoord);
       ext_rules_[{extension_id, CubeFaceIndexAMD}].push_back(
           ReplaceCubeFaceIndex);
-      ext_rules_[{extension_id, TimeAMD}].push_back(NotImplementedYet);
+      ext_rules_[{extension_id, TimeAMD}].push_back(ReplaceTimeAMD);
     }
   }
 };

+ 1 - 1
3rdparty/spirv-tools/source/opt/basic_block.h

@@ -214,7 +214,7 @@ class BasicBlock {
   void KillAllInsts(bool killLabel);
 
   // Splits this basic block into two. Returns a new basic block with label
-  // |labelId| containing the instructions from |iter| onwards. Instructions
+  // |label_id| containing the instructions from |iter| onwards. Instructions
   // prior to |iter| remain in this basic block.  The new block will be added
   // to the function immediately after the original block.
   BasicBlock* SplitBasicBlock(IRContext* context, uint32_t label_id,

+ 10 - 1
3rdparty/spirv-tools/source/opt/block_merge_util.cpp

@@ -171,8 +171,17 @@ void MergeWithSuccessor(IRContext* context, Function* func,
       // flow declaration.
       context->KillInst(merge_inst);
     } else {
+      // Move OpLine/OpNoLine information to merge_inst. This solves
+      // the validation error that OpLine is placed between OpLoopMerge
+      // and OpBranchConditional.
+      auto terminator = bi->terminator();
+      auto& vec = terminator->dbg_line_insts();
+      auto& new_vec = merge_inst->dbg_line_insts();
+      new_vec.insert(new_vec.end(), vec.begin(), vec.end());
+      terminator->clear_dbg_line_insts();
+
       // Move the merge instruction to just before the terminator.
-      merge_inst->InsertBefore(bi->terminator());
+      merge_inst->InsertBefore(terminator);
     }
   }
   context->ReplaceAllUsesWith(lab_id, bi->id());

+ 99 - 3
3rdparty/spirv-tools/source/opt/const_folding_rules.cpp

@@ -265,7 +265,10 @@ ConstantFoldingRule FoldFPUnaryOp(UnaryScalarFoldingRule scalar_rule) {
       return nullptr;
     }
 
-    if (constants[0] == nullptr) {
+    const analysis::Constant* arg =
+        (inst->opcode() == SpvOpExtInst) ? constants[1] : constants[0];
+
+    if (arg == nullptr) {
       return nullptr;
     }
 
@@ -273,7 +276,7 @@ ConstantFoldingRule FoldFPUnaryOp(UnaryScalarFoldingRule scalar_rule) {
       std::vector<const analysis::Constant*> a_components;
       std::vector<const analysis::Constant*> results_components;
 
-      a_components = constants[0]->GetVectorComponents(const_mgr);
+      a_components = arg->GetVectorComponents(const_mgr);
 
       // Fold each component of the vector.
       for (uint32_t i = 0; i < a_components.size(); ++i) {
@@ -291,7 +294,7 @@ ConstantFoldingRule FoldFPUnaryOp(UnaryScalarFoldingRule scalar_rule) {
       }
       return const_mgr->GetConstant(vector_type, ids);
     } else {
-      return scalar_rule(result_type, constants[0], const_mgr);
+      return scalar_rule(result_type, arg, const_mgr);
     }
   };
 }
@@ -1070,6 +1073,60 @@ const analysis::Constant* FoldClamp3(
   return nullptr;
 }
 
+UnaryScalarFoldingRule FoldFTranscendentalUnary(double (*fp)(double)) {
+  return
+      [fp](const analysis::Type* result_type, const analysis::Constant* a,
+           analysis::ConstantManager* const_mgr) -> const analysis::Constant* {
+        assert(result_type != nullptr && a != nullptr);
+        const analysis::Float* float_type = a->type()->AsFloat();
+        assert(float_type != nullptr);
+        assert(float_type == result_type->AsFloat());
+        if (float_type->width() == 32) {
+          float fa = a->GetFloat();
+          float res = static_cast<float>(fp(fa));
+          utils::FloatProxy<float> result(res);
+          std::vector<uint32_t> words = result.GetWords();
+          return const_mgr->GetConstant(result_type, words);
+        } else if (float_type->width() == 64) {
+          double fa = a->GetDouble();
+          double res = fp(fa);
+          utils::FloatProxy<double> result(res);
+          std::vector<uint32_t> words = result.GetWords();
+          return const_mgr->GetConstant(result_type, words);
+        }
+        return nullptr;
+      };
+}
+
+BinaryScalarFoldingRule FoldFTranscendentalBinary(double (*fp)(double,
+                                                               double)) {
+  return
+      [fp](const analysis::Type* result_type, const analysis::Constant* a,
+           const analysis::Constant* b,
+           analysis::ConstantManager* const_mgr) -> const analysis::Constant* {
+        assert(result_type != nullptr && a != nullptr);
+        const analysis::Float* float_type = a->type()->AsFloat();
+        assert(float_type != nullptr);
+        assert(float_type == result_type->AsFloat());
+        assert(float_type == b->type()->AsFloat());
+        if (float_type->width() == 32) {
+          float fa = a->GetFloat();
+          float fb = b->GetFloat();
+          float res = static_cast<float>(fp(fa, fb));
+          utils::FloatProxy<float> result(res);
+          std::vector<uint32_t> words = result.GetWords();
+          return const_mgr->GetConstant(result_type, words);
+        } else if (float_type->width() == 64) {
+          double fa = a->GetDouble();
+          double fb = b->GetDouble();
+          double res = fp(fa, fb);
+          utils::FloatProxy<double> result(res);
+          std::vector<uint32_t> words = result.GetWords();
+          return const_mgr->GetConstant(result_type, words);
+        }
+        return nullptr;
+      };
+}
 }  // namespace
 
 void ConstantFoldingRules::AddFoldingRules() {
@@ -1175,6 +1232,45 @@ void ConstantFoldingRules::AddFoldingRules() {
         FoldClamp2);
     ext_rules_[{ext_inst_glslstd450_id, GLSLstd450FClamp}].push_back(
         FoldClamp3);
+    ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Sin}].push_back(
+        FoldFPUnaryOp(FoldFTranscendentalUnary(std::sin)));
+    ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Cos}].push_back(
+        FoldFPUnaryOp(FoldFTranscendentalUnary(std::cos)));
+    ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Tan}].push_back(
+        FoldFPUnaryOp(FoldFTranscendentalUnary(std::tan)));
+    ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Asin}].push_back(
+        FoldFPUnaryOp(FoldFTranscendentalUnary(std::asin)));
+    ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Acos}].push_back(
+        FoldFPUnaryOp(FoldFTranscendentalUnary(std::acos)));
+    ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Atan}].push_back(
+        FoldFPUnaryOp(FoldFTranscendentalUnary(std::atan)));
+    ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Exp}].push_back(
+        FoldFPUnaryOp(FoldFTranscendentalUnary(std::exp)));
+    ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Log}].push_back(
+        FoldFPUnaryOp(FoldFTranscendentalUnary(std::log)));
+
+#ifdef __ANDROID__
+    // Android NDK r15c tageting ABI 15 doesn't have full support for C++11
+    // (no std::exp2/log2). ::exp2 is available from C99 but ::log2 isn't
+    // available up until ABI 18 so we use a shim
+    auto log2_shim = [](double v) -> double { return log(v) / log(2.0); };
+    ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Exp2}].push_back(
+        FoldFPUnaryOp(FoldFTranscendentalUnary(::exp2)));
+    ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Log2}].push_back(
+        FoldFPUnaryOp(FoldFTranscendentalUnary(log2_shim)));
+#else
+    ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Exp2}].push_back(
+        FoldFPUnaryOp(FoldFTranscendentalUnary(std::exp2)));
+    ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Log2}].push_back(
+        FoldFPUnaryOp(FoldFTranscendentalUnary(std::log2)));
+#endif
+
+    ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Sqrt}].push_back(
+        FoldFPUnaryOp(FoldFTranscendentalUnary(std::sqrt)));
+    ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Atan2}].push_back(
+        FoldFPBinaryOp(FoldFTranscendentalBinary(std::atan2)));
+    ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Pow}].push_back(
+        FoldFPBinaryOp(FoldFTranscendentalBinary(std::pow)));
   }
 }
 }  // namespace opt

+ 1 - 1
3rdparty/spirv-tools/source/opt/constants.cpp

@@ -156,7 +156,7 @@ Type* ConstantManager::GetType(const Instruction* inst) const {
 }
 
 std::vector<const Constant*> ConstantManager::GetOperandConstants(
-    Instruction* inst) const {
+    const Instruction* inst) const {
   std::vector<const Constant*> constants;
   for (uint32_t i = 0; i < inst->NumInOperands(); i++) {
     const Operand* operand = &inst->GetInOperand(i);

+ 2 - 1
3rdparty/spirv-tools/source/opt/constants.h

@@ -597,7 +597,8 @@ class ConstantManager {
 
   // Returns a vector of constants representing each in operand. If an operand
   // is not constant its entry is nullptr.
-  std::vector<const Constant*> GetOperandConstants(Instruction* inst) const;
+  std::vector<const Constant*> GetOperandConstants(
+      const Instruction* inst) const;
 
   // Records a mapping between |inst| and the constant value generated by it.
   // It returns true if a new Constant was successfully mapped, false if |inst|

+ 100 - 89
3rdparty/spirv-tools/source/opt/convert_to_half_pass.cpp

@@ -42,7 +42,7 @@ bool ConvertToHalfPass::IsFloat(Instruction* inst, uint32_t width) {
   return Pass::IsFloat(ty_id, width);
 }
 
-bool ConvertToHalfPass::IsRelaxed(Instruction* inst) {
+bool ConvertToHalfPass::IsDecoratedRelaxed(Instruction* inst) {
   uint32_t r_id = inst->result_id();
   for (auto r_inst : get_decoration_mgr()->GetDecorationsFor(r_id, false))
     if (r_inst->opcode() == SpvOpDecorate &&
@@ -51,6 +51,12 @@ bool ConvertToHalfPass::IsRelaxed(Instruction* inst) {
   return false;
 }
 
+bool ConvertToHalfPass::IsRelaxed(uint32_t id) {
+  return relaxed_ids_set_.count(id) > 0;
+}
+
+void ConvertToHalfPass::AddRelaxed(uint32_t id) { relaxed_ids_set_.insert(id); }
+
 analysis::Type* ConvertToHalfPass::FloatScalarType(uint32_t width) {
   analysis::Float float_ty(width);
   return context()->get_type_mgr()->GetRegisteredType(&float_ty);
@@ -87,16 +93,19 @@ uint32_t ConvertToHalfPass::EquivFloatTypeId(uint32_t ty_id, uint32_t width) {
 }
 
 void ConvertToHalfPass::GenConvert(uint32_t* val_idp, uint32_t width,
-                                   InstructionBuilder* builder) {
+                                   Instruction* inst) {
   Instruction* val_inst = get_def_use_mgr()->GetDef(*val_idp);
   uint32_t ty_id = val_inst->type_id();
   uint32_t nty_id = EquivFloatTypeId(ty_id, width);
   if (nty_id == ty_id) return;
   Instruction* cvt_inst;
+  InstructionBuilder builder(
+      context(), inst,
+      IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
   if (val_inst->opcode() == SpvOpUndef)
-    cvt_inst = builder->AddNullaryOp(nty_id, SpvOpUndef);
+    cvt_inst = builder.AddNullaryOp(nty_id, SpvOpUndef);
   else
-    cvt_inst = builder->AddUnaryOp(nty_id, SpvOpFConvert, *val_idp);
+    cvt_inst = builder.AddUnaryOp(nty_id, SpvOpFConvert, *val_idp);
   *val_idp = cvt_inst->result_id();
 }
 
@@ -153,17 +162,15 @@ bool ConvertToHalfPass::GenHalfArith(Instruction* inst) {
   bool modified = false;
   // Convert all float32 based operands to float16 equivalent and change
   // instruction type to float16 equivalent.
-  InstructionBuilder builder(
-      context(), inst,
-      IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
-  inst->ForEachInId([&builder, &modified, this](uint32_t* idp) {
+  inst->ForEachInId([&inst, &modified, this](uint32_t* idp) {
     Instruction* op_inst = get_def_use_mgr()->GetDef(*idp);
     if (!IsFloat(op_inst, 32)) return;
-    GenConvert(idp, 16, &builder);
+    GenConvert(idp, 16, inst);
     modified = true;
   });
   if (IsFloat(inst, 32)) {
     inst->SetResultType(EquivFloatTypeId(inst->type_id(), 16));
+    converted_ids_.insert(inst->result_id());
     modified = true;
   }
   if (modified) get_def_use_mgr()->AnalyzeInstUse(inst);
@@ -171,23 +178,10 @@ bool ConvertToHalfPass::GenHalfArith(Instruction* inst) {
 }
 
 bool ConvertToHalfPass::ProcessPhi(Instruction* inst) {
-  // Skip if not float32
-  if (!IsFloat(inst, 32)) return false;
-  // Skip if no relaxed operands.
-  bool relaxed_found = false;
-  uint32_t ocnt = 0;
-  inst->ForEachInId([&ocnt, &relaxed_found, this](uint32_t* idp) {
-    if (ocnt % 2 == 0) {
-      Instruction* val_inst = get_def_use_mgr()->GetDef(*idp);
-      if (IsRelaxed(val_inst)) relaxed_found = true;
-    }
-    ++ocnt;
-  });
-  if (!relaxed_found) return false;
   // Add float16 converts of any float32 operands and change type
   // of phi to float16 equivalent. Operand converts need to be added to
   // preceeding blocks.
-  ocnt = 0;
+  uint32_t ocnt = 0;
   uint32_t* prev_idp;
   inst->ForEachInId([&ocnt, &prev_idp, this](uint32_t* idp) {
     if (ocnt % 2 == 0) {
@@ -203,65 +197,32 @@ bool ConvertToHalfPass::ProcessPhi(Instruction* inst) {
               insert_before->opcode() != SpvOpLoopMerge)
             ++insert_before;
         }
-        InstructionBuilder builder(context(), &*insert_before,
-                                   IRContext::kAnalysisDefUse |
-                                       IRContext::kAnalysisInstrToBlockMapping);
-        GenConvert(prev_idp, 16, &builder);
+        GenConvert(prev_idp, 16, &*insert_before);
       }
     }
     ++ocnt;
   });
   inst->SetResultType(EquivFloatTypeId(inst->type_id(), 16));
   get_def_use_mgr()->AnalyzeInstUse(inst);
+  converted_ids_.insert(inst->result_id());
   return true;
 }
 
-bool ConvertToHalfPass::ProcessExtract(Instruction* inst) {
-  bool modified = false;
-  uint32_t comp_id = inst->GetSingleWordInOperand(0);
-  Instruction* comp_inst = get_def_use_mgr()->GetDef(comp_id);
-  // If extract is relaxed float32 based type and the composite is a relaxed
-  // float32 based type, convert it to float16 equivalent. This is slightly
-  // aggressive and pushes any likely conversion to apply to the whole
-  // composite rather than apply to each extracted component later. This
-  // can be a win if the platform can convert the entire composite in the same
-  // time as one component. It risks converting components that may not be
-  // used, although empirical data on a large set of real-world shaders seems
-  // to suggest this is not common and the composite convert is the best choice.
-  if (IsFloat(inst, 32) && IsRelaxed(inst) && IsFloat(comp_inst, 32) &&
-      IsRelaxed(comp_inst)) {
-    InstructionBuilder builder(
-        context(), inst,
-        IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
-    GenConvert(&comp_id, 16, &builder);
-    inst->SetInOperand(0, {comp_id});
-    comp_inst = get_def_use_mgr()->GetDef(comp_id);
-    modified = true;
-  }
-  // If the composite is a float16 based type, make sure the type of the
-  // extract agrees.
-  if (IsFloat(comp_inst, 16) && !IsFloat(inst, 16)) {
-    inst->SetResultType(EquivFloatTypeId(inst->type_id(), 16));
-    modified = true;
-  }
-  if (modified) get_def_use_mgr()->AnalyzeInstUse(inst);
-  return modified;
-}
-
 bool ConvertToHalfPass::ProcessConvert(Instruction* inst) {
   // If float32 and relaxed, change to float16 convert
-  if (IsFloat(inst, 32) && IsRelaxed(inst)) {
+  if (IsFloat(inst, 32) && IsRelaxed(inst->result_id())) {
     inst->SetResultType(EquivFloatTypeId(inst->type_id(), 16));
     get_def_use_mgr()->AnalyzeInstUse(inst);
+    converted_ids_.insert(inst->result_id());
   }
-  // If operand and result types are the same, replace result with operand
-  // and change convert to copy to keep validator happy; DCE will clean it up
+  // If operand and result types are the same, change FConvert to CopyObject to
+  // keep validator happy; simplification and DCE will clean it up
+  // One way this can happen is if an FConvert generated during this pass
+  // (likely by ProcessPhi) is later encountered here and its operand has been
+  // changed to half.
   uint32_t val_id = inst->GetSingleWordInOperand(0);
   Instruction* val_inst = get_def_use_mgr()->GetDef(val_id);
-  if (inst->type_id() == val_inst->type_id()) {
-    context()->ReplaceAllUsesWith(inst->result_id(), val_id);
-    inst->SetOpcode(SpvOpCopyObject);
-  }
+  if (inst->type_id() == val_inst->type_id()) inst->SetOpcode(SpvOpCopyObject);
   return true;  // modified
 }
 
@@ -270,12 +231,8 @@ bool ConvertToHalfPass::ProcessImageRef(Instruction* inst) {
   // If image reference, only need to convert dref args back to float32
   if (dref_image_ops_.count(inst->opcode()) != 0) {
     uint32_t dref_id = inst->GetSingleWordInOperand(kImageSampleDrefIdInIdx);
-    Instruction* dref_inst = get_def_use_mgr()->GetDef(dref_id);
-    if (IsFloat(dref_inst, 16) && IsRelaxed(dref_inst)) {
-      InstructionBuilder builder(
-          context(), inst,
-          IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
-      GenConvert(&dref_id, 32, &builder);
+    if (converted_ids_.count(dref_id) > 0) {
+      GenConvert(&dref_id, 32, inst);
       inst->SetInOperand(kImageSampleDrefIdInIdx, {dref_id});
       get_def_use_mgr()->AnalyzeInstUse(inst);
       modified = true;
@@ -288,32 +245,24 @@ bool ConvertToHalfPass::ProcessDefault(Instruction* inst) {
   bool modified = false;
   // If non-relaxed instruction has changed operands, need to convert
   // them back to float32
-  InstructionBuilder builder(
-      context(), inst,
-      IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
-  inst->ForEachInId([&builder, &modified, this](uint32_t* idp) {
-    Instruction* op_inst = get_def_use_mgr()->GetDef(*idp);
-    if (!IsFloat(op_inst, 16)) return;
-    if (!IsRelaxed(op_inst)) return;
+  inst->ForEachInId([&inst, &modified, this](uint32_t* idp) {
+    if (converted_ids_.count(*idp) == 0) return;
     uint32_t old_id = *idp;
-    GenConvert(idp, 32, &builder);
+    GenConvert(idp, 32, inst);
     if (*idp != old_id) modified = true;
   });
   if (modified) get_def_use_mgr()->AnalyzeInstUse(inst);
   return modified;
 }
 
-bool ConvertToHalfPass::GenHalfCode(Instruction* inst) {
+bool ConvertToHalfPass::GenHalfInst(Instruction* inst) {
   bool modified = false;
   // Remember id for later deletion of RelaxedPrecision decoration
-  bool inst_relaxed = IsRelaxed(inst);
-  if (inst_relaxed) relaxed_ids_.push_back(inst->result_id());
+  bool inst_relaxed = IsRelaxed(inst->result_id());
   if (IsArithmetic(inst) && inst_relaxed)
     modified = GenHalfArith(inst);
-  else if (inst->opcode() == SpvOpPhi)
+  else if (inst->opcode() == SpvOpPhi && inst_relaxed)
     modified = ProcessPhi(inst);
-  else if (inst->opcode() == SpvOpCompositeExtract)
-    modified = ProcessExtract(inst);
   else if (inst->opcode() == SpvOpFConvert)
     modified = ProcessConvert(inst);
   else if (image_ops_.count(inst->opcode()) != 0)
@@ -323,13 +272,62 @@ bool ConvertToHalfPass::GenHalfCode(Instruction* inst) {
   return modified;
 }
 
+bool ConvertToHalfPass::CloseRelaxInst(Instruction* inst) {
+  if (inst->result_id() == 0) return false;
+  if (IsRelaxed(inst->result_id())) return false;
+  if (!IsFloat(inst, 32)) return false;
+  if (IsDecoratedRelaxed(inst)) {
+    AddRelaxed(inst->result_id());
+    return true;
+  }
+  if (closure_ops_.count(inst->opcode()) == 0) return false;
+  // Can relax if all float operands are relaxed
+  bool relax = true;
+  inst->ForEachInId([&relax, this](uint32_t* idp) {
+    Instruction* op_inst = get_def_use_mgr()->GetDef(*idp);
+    if (!IsFloat(op_inst, 32)) return;
+    if (!IsRelaxed(*idp)) relax = false;
+  });
+  if (relax) {
+    AddRelaxed(inst->result_id());
+    return true;
+  }
+  // Can relax if all uses are relaxed
+  relax = true;
+  get_def_use_mgr()->ForEachUser(inst, [&relax, this](Instruction* uinst) {
+    if (uinst->result_id() == 0 || !IsFloat(uinst, 32) ||
+        (!IsDecoratedRelaxed(uinst) && !IsRelaxed(uinst->result_id()))) {
+      relax = false;
+      return;
+    }
+  });
+  if (relax) {
+    AddRelaxed(inst->result_id());
+    return true;
+  }
+  return false;
+}
+
 bool ConvertToHalfPass::ProcessFunction(Function* func) {
+  // Do a closure of Relaxed on composite and phi instructions
+  bool changed = true;
+  while (changed) {
+    changed = false;
+    cfg()->ForEachBlockInReversePostOrder(
+        func->entry().get(), [&changed, this](BasicBlock* bb) {
+          for (auto ii = bb->begin(); ii != bb->end(); ++ii)
+            changed |= CloseRelaxInst(&*ii);
+        });
+  }
+  // Do convert of relaxed instructions to half precision
   bool modified = false;
   cfg()->ForEachBlockInReversePostOrder(
       func->entry().get(), [&modified, this](BasicBlock* bb) {
         for (auto ii = bb->begin(); ii != bb->end(); ++ii)
-          modified |= GenHalfCode(&*ii);
+          modified |= GenHalfInst(&*ii);
       });
+  // Replace invalid converts of matrix into equivalent vector extracts,
+  // converts and finally a composite construct
   cfg()->ForEachBlockInReversePostOrder(
       func->entry().get(), [&modified, this](BasicBlock* bb) {
         for (auto ii = bb->begin(); ii != bb->end(); ++ii)
@@ -346,7 +344,7 @@ Pass::Status ConvertToHalfPass::ProcessImpl() {
   // If modified, make sure module has Float16 capability
   if (modified) context()->AddCapability(SpvCapabilityFloat16);
   // Remove all RelaxedPrecision decorations from instructions and globals
-  for (auto c_id : relaxed_ids_) RemoveRelaxedDecoration(c_id);
+  for (auto c_id : relaxed_ids_set_) RemoveRelaxedDecoration(c_id);
   for (auto& val : get_module()->types_values()) {
     uint32_t v_id = val.result_id();
     if (v_id != 0) RemoveRelaxedDecoration(v_id);
@@ -366,6 +364,7 @@ void ConvertToHalfPass::Initialize() {
       SpvOpVectorShuffle,
       SpvOpCompositeConstruct,
       SpvOpCompositeInsert,
+      SpvOpCompositeExtract,
       SpvOpCopyObject,
       SpvOpTranspose,
       SpvOpConvertSToF,
@@ -453,7 +452,19 @@ void ConvertToHalfPass::Initialize() {
       SpvOpImageSparseSampleProjDrefExplicitLod,
       SpvOpImageSparseDrefGather,
   };
-  relaxed_ids_.clear();
+  closure_ops_ = {
+      SpvOpVectorExtractDynamic,
+      SpvOpVectorInsertDynamic,
+      SpvOpVectorShuffle,
+      SpvOpCompositeConstruct,
+      SpvOpCompositeInsert,
+      SpvOpCompositeExtract,
+      SpvOpCopyObject,
+      SpvOpTranspose,
+      SpvOpPhi,
+  };
+  relaxed_ids_set_.clear();
+  converted_ids_.clear();
 }
 
 }  // namespace opt

+ 25 - 11
3rdparty/spirv-tools/source/opt/convert_to_half_pass.h

@@ -38,7 +38,8 @@ class ConvertToHalfPass : public Pass {
   const char* name() const override { return "convert-to-half-pass"; }
 
  private:
-  // Return true if |inst| is an arithmetic op that can be of type float16
+  // Return true if |inst| is an arithmetic, composite or phi op that can be
+  // of type float16
   bool IsArithmetic(Instruction* inst);
 
   // Return true if |inst| returns scalar, vector or matrix type with base
@@ -46,7 +47,13 @@ class ConvertToHalfPass : public Pass {
   bool IsFloat(Instruction* inst, uint32_t width);
 
   // Return true if |inst| is decorated with RelaxedPrecision
-  bool IsRelaxed(Instruction* inst);
+  bool IsDecoratedRelaxed(Instruction* inst);
+
+  // Return true if |id| has been added to the relaxed id set
+  bool IsRelaxed(uint32_t id);
+
+  // Add |id| to the relaxed id set
+  void AddRelaxed(uint32_t id);
 
   // Return type id for float with |width|
   analysis::Type* FloatScalarType(uint32_t width);
@@ -64,19 +71,23 @@ class ConvertToHalfPass : public Pass {
 
   // Append instructions to builder to convert value |*val_idp| to type
   // |ty_id| but with |width|. Set |*val_idp| to the new id.
-  void GenConvert(uint32_t* val_idp, uint32_t width,
-                  InstructionBuilder* builder);
+  void GenConvert(uint32_t* val_idp, uint32_t width, Instruction* inst);
 
   // Remove RelaxedPrecision decoration of |id|.
   void RemoveRelaxedDecoration(uint32_t id);
 
+  // Add |inst| to relaxed instruction set if warranted. Specifically, if
+  // it is float32 and either decorated relaxed or a composite or phi
+  // instruction where all operands are relaxed or all uses are relaxed.
+  bool CloseRelaxInst(Instruction* inst);
+
   // If |inst| is an arithmetic, phi, extract or convert instruction of float32
   // base type and decorated with RelaxedPrecision, change it to the equivalent
   // float16 based type instruction. Specifically, insert instructions to
   // convert all operands to float16 (if needed) and change its type to the
   // equivalent float16 type. Otherwise, insert instructions to convert its
   // operands back to their original types, if needed.
-  bool GenHalfCode(Instruction* inst);
+  bool GenHalfInst(Instruction* inst);
 
   // Gen code for relaxed arithmetic |inst|
   bool GenHalfArith(Instruction* inst);
@@ -84,9 +95,6 @@ class ConvertToHalfPass : public Pass {
   // Gen code for relaxed phi |inst|
   bool ProcessPhi(Instruction* inst);
 
-  // Gen code for relaxed extract |inst|
-  bool ProcessExtract(Instruction* inst);
-
   // Gen code for relaxed convert |inst|
   bool ProcessConvert(Instruction* inst);
 
@@ -98,11 +106,11 @@ class ConvertToHalfPass : public Pass {
 
   // If |inst| is an FConvert of a matrix type, decompose it to a series
   // of vector extracts, converts and inserts into an Undef. These are
-  // generated by GenHalfCode because they are easier to manipulate, but are
+  // generated by GenHalfInst because they are easier to manipulate, but are
   // invalid so we need to clean them up.
   bool MatConvertCleanup(Instruction* inst);
 
-  // Call GenHalfCode on every instruction in |func|.
+  // Call GenHalfInst on every instruction in |func|.
   // If code is generated for an instruction, replace the instruction
   // with the new instructions that are generated.
   bool ProcessFunction(Function* func);
@@ -124,8 +132,14 @@ class ConvertToHalfPass : public Pass {
   // Set of dref sample operations
   std::unordered_set<uint32_t> dref_image_ops_;
 
+  // Set of dref sample operations
+  std::unordered_set<uint32_t> closure_ops_;
+
+  // Set of ids of all relaxed instructions
+  std::unordered_set<uint32_t> relaxed_ids_set_;
+
   // Ids of all converted instructions
-  std::vector<uint32_t> relaxed_ids_;
+  std::unordered_set<uint32_t> converted_ids_;
 };
 
 }  // namespace opt

+ 13 - 7
3rdparty/spirv-tools/source/opt/dead_branch_elim_pass.cpp

@@ -167,7 +167,6 @@ bool DeadBranchElimPass::MarkLiveBlocks(
     }
 
     if (simplify) {
-      modified = true;
       conditions_to_simplify.push_back({block, live_lab_id});
       stack.push_back(GetParentBlock(live_lab_id));
     } else {
@@ -179,24 +178,29 @@ bool DeadBranchElimPass::MarkLiveBlocks(
     }
   }
 
-  // Traverse |conditions_to_simplify in reverse order.  This is done so that we
-  // simplify nested constructs before simplifying the constructs that contain
-  // them.
+  // Traverse |conditions_to_simplify| in reverse order.  This is done so that
+  // we simplify nested constructs before simplifying the constructs that
+  // contain them.
   for (auto b = conditions_to_simplify.rbegin();
        b != conditions_to_simplify.rend(); ++b) {
-    SimplifyBranch(b->first, b->second);
+    modified |= SimplifyBranch(b->first, b->second);
   }
 
   return modified;
 }
 
-void DeadBranchElimPass::SimplifyBranch(BasicBlock* block,
+bool DeadBranchElimPass::SimplifyBranch(BasicBlock* block,
                                         uint32_t live_lab_id) {
   Instruction* merge_inst = block->GetMergeInst();
   Instruction* terminator = block->terminator();
   if (merge_inst && merge_inst->opcode() == SpvOpSelectionMerge) {
     if (merge_inst->NextNode()->opcode() == SpvOpSwitch &&
         SwitchHasNestedBreak(block->id())) {
+      if (terminator->NumInOperands() == 2) {
+        // We cannot remove the branch, and it already has a single case, so no
+        // work to do.
+        return false;
+      }
       // We have to keep the switch because it has a nest break, so we
       // remove all cases except for the live one.
       Instruction::OperandList new_operands;
@@ -231,6 +235,7 @@ void DeadBranchElimPass::SimplifyBranch(BasicBlock* block,
     AddBranch(live_lab_id, block);
     context()->KillInst(terminator);
   }
+  return true;
 }
 
 void DeadBranchElimPass::MarkUnreachableStructuredTargets(
@@ -643,7 +648,8 @@ bool DeadBranchElimPass::SwitchHasNestedBreak(uint32_t switch_header_id) {
         if (bb->id() == switch_header_id) {
           return true;
         }
-        return (cfg_analysis->ContainingConstruct(inst) == switch_header_id);
+        return (cfg_analysis->ContainingConstruct(inst) == switch_header_id &&
+                bb->GetMergeInst() == nullptr);
       });
 }
 

+ 7 - 6
3rdparty/spirv-tools/source/opt/dead_branch_elim_pass.h

@@ -159,14 +159,15 @@ class DeadBranchElimPass : public MemPass {
       std::unordered_set<BasicBlock*>* blocks_with_back_edges);
 
   // Returns true if there is a brach to the merge node of the selection
-  // construct |switch_header_id| that is inside a nested selection construct.
+  // construct |switch_header_id| that is inside a nested selection construct or
+  // in the header of the nested selection construct.
   bool SwitchHasNestedBreak(uint32_t switch_header_id);
 
-  // Replaces the terminator of |block| with a branch to |live_lab_id|.  The
-  // merge instruction is deleted or moved as needed to maintain structured
-  // control flow.  Assumes that the StructuredCFGAnalysis is valid for the
-  // constructs containing |block|.
-  void SimplifyBranch(BasicBlock* block, uint32_t live_lab_id);
+  // Return true of the terminator of |block| is successfully replaced with a
+  // branch to |live_lab_id|.  The merge instruction is deleted or moved as
+  // needed to maintain structured control flow.  Assumes that the
+  // StructuredCFGAnalysis is valid for the constructs containing |block|.
+  bool SimplifyBranch(BasicBlock* block, uint32_t live_lab_id);
 };
 
 }  // namespace opt

+ 49 - 45
3rdparty/spirv-tools/source/opt/folding_rules.cpp

@@ -1501,60 +1501,64 @@ FoldingRule CompositeConstructFeedingExtract() {
   };
 }
 
-FoldingRule CompositeExtractFeedingConstruct() {
-  // If the OpCompositeConstruct is simply putting back together elements that
-  // where extracted from the same souce, we can simlpy reuse the source.
-  //
-  // This is a common code pattern because of the way that scalar replacement
-  // works.
-  return [](IRContext* context, Instruction* inst,
-            const std::vector<const analysis::Constant*>&) {
-    assert(inst->opcode() == SpvOpCompositeConstruct &&
-           "Wrong opcode.  Should be OpCompositeConstruct.");
-    analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
-    uint32_t original_id = 0;
+// If the OpCompositeConstruct is simply putting back together elements that
+// where extracted from the same source, we can simply reuse the source.
+//
+// This is a common code pattern because of the way that scalar replacement
+// works.
+bool CompositeExtractFeedingConstruct(
+    IRContext* context, Instruction* inst,
+    const std::vector<const analysis::Constant*>&) {
+  assert(inst->opcode() == SpvOpCompositeConstruct &&
+         "Wrong opcode.  Should be OpCompositeConstruct.");
+  analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
+  uint32_t original_id = 0;
 
-    // Check each element to make sure they are:
-    // - extractions
-    // - extracting the same position they are inserting
-    // - all extract from the same id.
-    for (uint32_t i = 0; i < inst->NumInOperands(); ++i) {
-      uint32_t element_id = inst->GetSingleWordInOperand(i);
-      Instruction* element_inst = def_use_mgr->GetDef(element_id);
+  if (inst->NumInOperands() == 0) {
+    // The struct being constructed has no members.
+    return false;
+  }
 
-      if (element_inst->opcode() != SpvOpCompositeExtract) {
-        return false;
-      }
+  // Check each element to make sure they are:
+  // - extractions
+  // - extracting the same position they are inserting
+  // - all extract from the same id.
+  for (uint32_t i = 0; i < inst->NumInOperands(); ++i) {
+    const uint32_t element_id = inst->GetSingleWordInOperand(i);
+    Instruction* element_inst = def_use_mgr->GetDef(element_id);
 
-      if (element_inst->NumInOperands() != 2) {
-        return false;
-      }
+    if (element_inst->opcode() != SpvOpCompositeExtract) {
+      return false;
+    }
 
-      if (element_inst->GetSingleWordInOperand(1) != i) {
-        return false;
-      }
+    if (element_inst->NumInOperands() != 2) {
+      return false;
+    }
 
-      if (i == 0) {
-        original_id =
-            element_inst->GetSingleWordInOperand(kExtractCompositeIdInIdx);
-      } else if (original_id != element_inst->GetSingleWordInOperand(
-                                    kExtractCompositeIdInIdx)) {
-        return false;
-      }
+    if (element_inst->GetSingleWordInOperand(1) != i) {
+      return false;
     }
 
-    // The last check it to see that the object being extracted from is the
-    // correct type.
-    Instruction* original_inst = def_use_mgr->GetDef(original_id);
-    if (original_inst->type_id() != inst->type_id()) {
+    if (i == 0) {
+      original_id =
+          element_inst->GetSingleWordInOperand(kExtractCompositeIdInIdx);
+    } else if (original_id !=
+               element_inst->GetSingleWordInOperand(kExtractCompositeIdInIdx)) {
       return false;
     }
+  }
 
-    // Simplify by using the original object.
-    inst->SetOpcode(SpvOpCopyObject);
-    inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {original_id}}});
-    return true;
-  };
+  // The last check it to see that the object being extracted from is the
+  // correct type.
+  Instruction* original_inst = def_use_mgr->GetDef(original_id);
+  if (original_inst->type_id() != inst->type_id()) {
+    return false;
+  }
+
+  // Simplify by using the original object.
+  inst->SetOpcode(SpvOpCopyObject);
+  inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {original_id}}});
+  return true;
 }
 
 FoldingRule InsertFeedingExtract() {
@@ -2419,7 +2423,7 @@ void FoldingRules::AddFoldingRules() {
   // Note that the order in which rules are added to the list matters. If a rule
   // applies to the instruction, the rest of the rules will not be attempted.
   // Take that into consideration.
-  rules_[SpvOpCompositeConstruct].push_back(CompositeExtractFeedingConstruct());
+  rules_[SpvOpCompositeConstruct].push_back(CompositeExtractFeedingConstruct);
 
   rules_[SpvOpCompositeExtract].push_back(InsertFeedingExtract());
   rules_[SpvOpCompositeExtract].push_back(CompositeConstructFeedingExtract());

+ 7 - 6
3rdparty/spirv-tools/source/opt/inst_bindless_check_pass.h

@@ -28,18 +28,19 @@ namespace opt {
 // external design may change as the layer evolves.
 class InstBindlessCheckPass : public InstrumentPass {
  public:
-  // For test harness only
-  InstBindlessCheckPass()
-      : InstrumentPass(7, 23, kInstValidationIdBindless, 1),
-        input_length_enabled_(true),
-        input_init_enabled_(true) {}
-  // For all other interfaces
+  // Deprecated interface
   InstBindlessCheckPass(uint32_t desc_set, uint32_t shader_id,
                         bool input_length_enable, bool input_init_enable,
                         uint32_t version)
       : InstrumentPass(desc_set, shader_id, kInstValidationIdBindless, version),
         input_length_enabled_(input_length_enable),
         input_init_enabled_(input_init_enable) {}
+  // Preferred Interface
+  InstBindlessCheckPass(uint32_t desc_set, uint32_t shader_id,
+                        bool input_length_enable, bool input_init_enable)
+      : InstrumentPass(desc_set, shader_id, kInstValidationIdBindless),
+        input_length_enabled_(input_length_enable),
+        input_init_enabled_(input_init_enable) {}
 
   ~InstBindlessCheckPass() override = default;
 

+ 4 - 4
3rdparty/spirv-tools/source/opt/inst_buff_addr_check_pass.h

@@ -28,13 +28,13 @@ namespace opt {
 // external design of this class may change as the layer evolves.
 class InstBuffAddrCheckPass : public InstrumentPass {
  public:
-  // For test harness only
-  InstBuffAddrCheckPass()
-      : InstrumentPass(7, 23, kInstValidationIdBuffAddr, 1) {}
-  // For all other interfaces
+  // Deprecated interface
   InstBuffAddrCheckPass(uint32_t desc_set, uint32_t shader_id, uint32_t version)
       : InstrumentPass(desc_set, shader_id, kInstValidationIdBuffAddr,
                        version) {}
+  // Preferred interface
+  InstBuffAddrCheckPass(uint32_t desc_set, uint32_t shader_id)
+      : InstrumentPass(desc_set, shader_id, kInstValidationIdBuffAddr) {}
 
   ~InstBuffAddrCheckPass() override = default;
 

+ 3 - 0
3rdparty/spirv-tools/source/opt/instruction.h

@@ -172,6 +172,9 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
     return dbg_line_insts_;
   }
 
+  // Clear line-related debug instructions attached to this instruction.
+  void clear_dbg_line_insts() { dbg_line_insts_.clear(); }
+
   // Same semantics as in the base class except the list the InstructionList
   // containing |pos| will now assume ownership of |this|.
   // inline void MoveBefore(Instruction* pos);

+ 51 - 56
3rdparty/spirv-tools/source/opt/instrument_pass.cpp

@@ -185,32 +185,12 @@ void InstrumentPass::GenStageStreamWriteCode(uint32_t stage_idx,
           GetUintId(), SpvOpCompositeExtract, load_id, 1);
       Instruction* z_inst = builder->AddIdLiteralOp(
           GetUintId(), SpvOpCompositeExtract, load_id, 2);
-      if (version_ == 1) {
-        // For version 1 format, as a stopgap, pack uvec3 into first word:
-        // x << 21 | y << 10 | z. Second word is unused. (DEPRECATED)
-        Instruction* x_shft_inst = builder->AddBinaryOp(
-            GetUintId(), SpvOpShiftLeftLogical, x_inst->result_id(),
-            builder->GetUintConstantId(21));
-        Instruction* y_shft_inst = builder->AddBinaryOp(
-            GetUintId(), SpvOpShiftLeftLogical, y_inst->result_id(),
-            builder->GetUintConstantId(10));
-        Instruction* x_or_y_inst = builder->AddBinaryOp(
-            GetUintId(), SpvOpBitwiseOr, x_shft_inst->result_id(),
-            y_shft_inst->result_id());
-        Instruction* x_or_y_or_z_inst =
-            builder->AddBinaryOp(GetUintId(), SpvOpBitwiseOr,
-                                 x_or_y_inst->result_id(), z_inst->result_id());
-        GenDebugOutputFieldCode(base_offset_id, kInstCompOutGlobalInvocationId,
-                                x_or_y_or_z_inst->result_id(), builder);
-      } else {
-        // For version 2 format, write all three words
-        GenDebugOutputFieldCode(base_offset_id, kInstCompOutGlobalInvocationIdX,
-                                x_inst->result_id(), builder);
-        GenDebugOutputFieldCode(base_offset_id, kInstCompOutGlobalInvocationIdY,
-                                y_inst->result_id(), builder);
-        GenDebugOutputFieldCode(base_offset_id, kInstCompOutGlobalInvocationIdZ,
-                                z_inst->result_id(), builder);
-      }
+      GenDebugOutputFieldCode(base_offset_id, kInstCompOutGlobalInvocationIdX,
+                              x_inst->result_id(), builder);
+      GenDebugOutputFieldCode(base_offset_id, kInstCompOutGlobalInvocationIdY,
+                              y_inst->result_id(), builder);
+      GenDebugOutputFieldCode(base_offset_id, kInstCompOutGlobalInvocationIdZ,
+                              z_inst->result_id(), builder);
     } break;
     case SpvExecutionModelGeometry: {
       // Load and store PrimitiveId and InvocationId.
@@ -231,30 +211,23 @@ void InstrumentPass::GenStageStreamWriteCode(uint32_t stage_idx,
           kInstTessCtlOutPrimitiveId, base_offset_id, builder);
     } break;
     case SpvExecutionModelTessellationEvaluation: {
-      if (version_ == 1) {
-        // For format version 1, load and store InvocationId.
-        GenBuiltinOutputCode(
-            context()->GetBuiltinInputVarId(SpvBuiltInInvocationId),
-            kInstTessOutInvocationId, base_offset_id, builder);
-      } else {
-        // For format version 2, load and store PrimitiveId and TessCoord.uv
-        GenBuiltinOutputCode(
-            context()->GetBuiltinInputVarId(SpvBuiltInPrimitiveId),
-            kInstTessEvalOutPrimitiveId, base_offset_id, builder);
-        uint32_t load_id = GenVarLoad(
-            context()->GetBuiltinInputVarId(SpvBuiltInTessCoord), builder);
-        Instruction* uvec3_cast_inst =
-            builder->AddUnaryOp(GetVec3UintId(), SpvOpBitcast, load_id);
-        uint32_t uvec3_cast_id = uvec3_cast_inst->result_id();
-        Instruction* u_inst = builder->AddIdLiteralOp(
-            GetUintId(), SpvOpCompositeExtract, uvec3_cast_id, 0);
-        Instruction* v_inst = builder->AddIdLiteralOp(
-            GetUintId(), SpvOpCompositeExtract, uvec3_cast_id, 1);
-        GenDebugOutputFieldCode(base_offset_id, kInstTessEvalOutTessCoordU,
-                                u_inst->result_id(), builder);
-        GenDebugOutputFieldCode(base_offset_id, kInstTessEvalOutTessCoordV,
-                                v_inst->result_id(), builder);
-      }
+      // Load and store PrimitiveId and TessCoord.uv
+      GenBuiltinOutputCode(
+          context()->GetBuiltinInputVarId(SpvBuiltInPrimitiveId),
+          kInstTessEvalOutPrimitiveId, base_offset_id, builder);
+      uint32_t load_id = GenVarLoad(
+          context()->GetBuiltinInputVarId(SpvBuiltInTessCoord), builder);
+      Instruction* uvec3_cast_inst =
+          builder->AddUnaryOp(GetVec3UintId(), SpvOpBitcast, load_id);
+      uint32_t uvec3_cast_id = uvec3_cast_inst->result_id();
+      Instruction* u_inst = builder->AddIdLiteralOp(
+          GetUintId(), SpvOpCompositeExtract, uvec3_cast_id, 0);
+      Instruction* v_inst = builder->AddIdLiteralOp(
+          GetUintId(), SpvOpCompositeExtract, uvec3_cast_id, 1);
+      GenDebugOutputFieldCode(base_offset_id, kInstTessEvalOutTessCoordU,
+                              u_inst->result_id(), builder);
+      GenDebugOutputFieldCode(base_offset_id, kInstTessEvalOutTessCoordV,
+                              v_inst->result_id(), builder);
     } break;
     case SpvExecutionModelFragment: {
       // Load FragCoord and convert to Uint
@@ -671,8 +644,7 @@ uint32_t InstrumentPass::GetStreamWriteFunctionId(uint32_t stage_idx,
         context(), &*new_blk_ptr,
         IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
     // Gen test if debug output buffer size will not be exceeded.
-    uint32_t val_spec_offset =
-        (version_ == 1) ? kInstStageOutCnt : kInst2StageOutCnt;
+    uint32_t val_spec_offset = kInstStageOutCnt;
     uint32_t obuf_record_sz = val_spec_offset + val_spec_param_cnt;
     uint32_t buf_id = GetOutputBufferId();
     uint32_t buf_uint_ptr_id = GetOutputBufferPtrId();
@@ -892,6 +864,14 @@ bool InstrumentPass::InstProcessCallTreeFromRoots(InstProcessFunction& pfn,
 }
 
 bool InstrumentPass::InstProcessEntryPointCallTree(InstProcessFunction& pfn) {
+  // Check that format version 2 requested
+  if (version_ != 2u) {
+    if (consumer()) {
+      std::string message = "Unsupported instrumentation format requested";
+      consumer()(SPV_MSG_ERROR, 0, {0, 0, 0}, message.c_str());
+    }
+    return false;
+  }
   // Make sure all entry points have the same execution model. Do not
   // instrument if they do not.
   // TODO(greg-lunarg): Handle mixed stages. Technically, a shader module
@@ -905,12 +885,17 @@ bool InstrumentPass::InstProcessEntryPointCallTree(InstProcessFunction& pfn) {
   for (auto& e : get_module()->entry_points()) {
     if (ecnt == 0)
       stage = e.GetSingleWordInOperand(kEntryPointExecutionModelInIdx);
-    else if (e.GetSingleWordInOperand(kEntryPointExecutionModelInIdx) != stage)
+    else if (e.GetSingleWordInOperand(kEntryPointExecutionModelInIdx) !=
+             stage) {
+      if (consumer()) {
+        std::string message = "Mixed stage shader module not supported";
+        consumer()(SPV_MSG_ERROR, 0, {0, 0, 0}, message.c_str());
+      }
       return false;
+    }
     ++ecnt;
   }
-  // Only supporting vertex, fragment and compute shaders at the moment.
-  // TODO(greg-lunarg): Handle all stages.
+  // Check for supported stages
   if (stage != SpvExecutionModelVertex && stage != SpvExecutionModelFragment &&
       stage != SpvExecutionModelGeometry &&
       stage != SpvExecutionModelGLCompute &&
@@ -920,8 +905,14 @@ bool InstrumentPass::InstProcessEntryPointCallTree(InstProcessFunction& pfn) {
       stage != SpvExecutionModelIntersectionNV &&
       stage != SpvExecutionModelAnyHitNV &&
       stage != SpvExecutionModelClosestHitNV &&
-      stage != SpvExecutionModelMissNV && stage != SpvExecutionModelCallableNV)
+      stage != SpvExecutionModelMissNV &&
+      stage != SpvExecutionModelCallableNV) {
+    if (consumer()) {
+      std::string message = "Stage not supported by instrumentation";
+      consumer()(SPV_MSG_ERROR, 0, {0, 0, 0}, message.c_str());
+    }
     return false;
+  }
   // Add together the roots of all entry points
   std::queue<uint32_t> roots;
   for (auto& e : get_module()->entry_points()) {
@@ -997,6 +988,10 @@ void InstrumentPass::InitializeInstrument() {
     (void)i;
     ++module_offset;
   }
+  for (auto& i : module->ext_inst_debuginfo()) {
+    (void)i;
+    ++module_offset;
+  }
   for (auto& i : module->annotations()) {
     (void)i;
     ++module_offset;

+ 10 - 1
3rdparty/spirv-tools/source/opt/instrument_pass.h

@@ -81,7 +81,16 @@ class InstrumentPass : public Pass {
  protected:
   // Create instrumentation pass for |validation_id| which utilizes descriptor
   // set |desc_set| for debug input and output buffers and writes |shader_id|
-  // into debug output records with format |version|.
+  // into debug output records.
+  InstrumentPass(uint32_t desc_set, uint32_t shader_id, uint32_t validation_id)
+      : Pass(),
+        desc_set_(desc_set),
+        shader_id_(shader_id),
+        validation_id_(validation_id),
+        version_(2u) {}
+  // Create instrumentation pass for |validation_id| which utilizes descriptor
+  // set |desc_set| for debug input and output buffers and writes |shader_id|
+  // into debug output records with format |version|. Deprecated.
   InstrumentPass(uint32_t desc_set, uint32_t shader_id, uint32_t validation_id,
                  uint32_t version)
       : Pass(),

+ 31 - 0
3rdparty/spirv-tools/source/opt/ir_context.h

@@ -187,6 +187,14 @@ class IRContext {
   inline IteratorRange<Module::inst_iterator> debugs3();
   inline IteratorRange<Module::const_inst_iterator> debugs3() const;
 
+  // Iterators for debug info instructions (excluding OpLine & OpNoLine)
+  // contained in this module.  These are OpExtInst for OpenCL.DebugInfo.100
+  // or DebugInfo extension placed between section 9 and 10.
+  inline Module::inst_iterator ext_inst_debuginfo_begin();
+  inline Module::inst_iterator ext_inst_debuginfo_end();
+  inline IteratorRange<Module::inst_iterator> ext_inst_debuginfo();
+  inline IteratorRange<Module::const_inst_iterator> ext_inst_debuginfo() const;
+
   // Add |capability| to the module, if it is not already enabled.
   inline void AddCapability(SpvCapability capability);
 
@@ -215,6 +223,8 @@ class IRContext {
   // Appends a debug 3 instruction (OpModuleProcessed) to this module.
   // This is due to decision by the SPIR Working Group, pending publication.
   inline void AddDebug3Inst(std::unique_ptr<Instruction>&& d);
+  // Appends a OpExtInst for DebugInfo to this module.
+  inline void AddExtInstDebugInfo(std::unique_ptr<Instruction>&& d);
   // Appends an annotation instruction to this module.
   inline void AddAnnotationInst(std::unique_ptr<Instruction>&& a);
   // Appends a type-declaration instruction to this module.
@@ -925,6 +935,23 @@ IteratorRange<Module::const_inst_iterator> IRContext::debugs3() const {
   return ((const Module*)module_.get())->debugs3();
 }
 
+Module::inst_iterator IRContext::ext_inst_debuginfo_begin() {
+  return module()->ext_inst_debuginfo_begin();
+}
+
+Module::inst_iterator IRContext::ext_inst_debuginfo_end() {
+  return module()->ext_inst_debuginfo_end();
+}
+
+IteratorRange<Module::inst_iterator> IRContext::ext_inst_debuginfo() {
+  return module()->ext_inst_debuginfo();
+}
+
+IteratorRange<Module::const_inst_iterator> IRContext::ext_inst_debuginfo()
+    const {
+  return ((const Module*)module_.get())->ext_inst_debuginfo();
+}
+
 void IRContext::AddCapability(SpvCapability capability) {
   if (!get_feature_mgr()->HasCapability(capability)) {
     std::unique_ptr<Instruction> capability_inst(new Instruction(
@@ -1018,6 +1045,10 @@ void IRContext::AddDebug3Inst(std::unique_ptr<Instruction>&& d) {
   module()->AddDebug3Inst(std::move(d));
 }
 
+void IRContext::AddExtInstDebugInfo(std::unique_ptr<Instruction>&& d) {
+  module()->AddExtInstDebugInfo(std::move(d));
+}
+
 void IRContext::AddAnnotationInst(std::unique_ptr<Instruction>&& a) {
   if (AreAnalysesValid(kAnalysisDecorations)) {
     get_decoration_mgr()->AddDecoration(a.get());

+ 44 - 2
3rdparty/spirv-tools/source/opt/ir_loader.cpp

@@ -16,6 +16,9 @@
 
 #include <utility>
 
+#include "DebugInfo.h"
+#include "OpenCLDebugInfo100.h"
+#include "source/ext_inst.h"
 #include "source/opt/log.h"
 #include "source/opt/reflect.h"
 #include "source/util/make_unique.h"
@@ -113,11 +116,17 @@ bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) {
       } else if (IsTypeInst(opcode)) {
         module_->AddType(std::move(spv_inst));
       } else if (IsConstantInst(opcode) || opcode == SpvOpVariable ||
-                 opcode == SpvOpUndef) {
+                 opcode == SpvOpUndef ||
+                 (opcode == SpvOpExtInst &&
+                  spvExtInstIsNonSemantic(inst->ext_inst_type))) {
         module_->AddGlobalValue(std::move(spv_inst));
+      } else if (opcode == SpvOpExtInst &&
+                 spvExtInstIsDebugInfo(inst->ext_inst_type)) {
+        module_->AddExtInstDebugInfo(std::move(spv_inst));
       } else {
         Errorf(consumer_, src, loc,
-               "Unhandled inst type (opcode: %d) found outside function definition.",
+               "Unhandled inst type (opcode: %d) found outside function "
+               "definition.",
                opcode);
         return false;
       }
@@ -132,6 +141,39 @@ bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) {
         }
         function_->AddParameter(std::move(spv_inst));
       } else {
+        if (opcode == SpvOpExtInst &&
+            spvExtInstIsDebugInfo(inst->ext_inst_type)) {
+          const uint32_t ext_inst_index = inst->words[4];
+          if (inst->ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) {
+            const OpenCLDebugInfo100Instructions ext_inst_key =
+                OpenCLDebugInfo100Instructions(ext_inst_index);
+            if (ext_inst_key != OpenCLDebugInfo100DebugScope &&
+                ext_inst_key != OpenCLDebugInfo100DebugNoScope &&
+                ext_inst_key != OpenCLDebugInfo100DebugDeclare &&
+                ext_inst_key != OpenCLDebugInfo100DebugValue) {
+              Errorf(consumer_, src, loc,
+                     "Debug info extension instruction other than DebugScope, "
+                     "DebugNoScope, DebugDeclare, and DebugValue found inside "
+                     "function",
+                     opcode);
+              return false;
+            }
+          } else {
+            const DebugInfoInstructions ext_inst_key =
+                DebugInfoInstructions(ext_inst_index);
+            if (ext_inst_key != DebugInfoDebugScope &&
+                ext_inst_key != DebugInfoDebugNoScope &&
+                ext_inst_key != DebugInfoDebugDeclare &&
+                ext_inst_key != DebugInfoDebugValue) {
+              Errorf(consumer_, src, loc,
+                     "Debug info extension instruction other than DebugScope, "
+                     "DebugNoScope, DebugDeclare, and DebugValue found inside "
+                     "function",
+                     opcode);
+              return false;
+            }
+          }
+        }
         block_->AddInstruction(std::move(spv_inst));
       }
     }

+ 57 - 69
3rdparty/spirv-tools/source/opt/merge_return_pass.cpp

@@ -69,6 +69,32 @@ Pass::Status MergeReturnPass::Process() {
   return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
 }
 
+void MergeReturnPass::GenerateState(BasicBlock* block) {
+  if (Instruction* mergeInst = block->GetMergeInst()) {
+    if (mergeInst->opcode() == SpvOpLoopMerge) {
+      // If new loop, break to this loop merge block
+      state_.emplace_back(mergeInst, mergeInst);
+    } else {
+      auto branchInst = mergeInst->NextNode();
+      if (branchInst->opcode() == SpvOpSwitch) {
+        // If switch inside of loop, break to innermost loop merge block.
+        // Otherwise need to break to this switch merge block.
+        auto lastMergeInst = state_.back().BreakMergeInst();
+        if (lastMergeInst && lastMergeInst->opcode() == SpvOpLoopMerge)
+          state_.emplace_back(lastMergeInst, mergeInst);
+        else
+          state_.emplace_back(mergeInst, mergeInst);
+      } else {
+        // If branch conditional inside loop, always break to innermost
+        // loop merge block. If branch conditional inside switch, break to
+        // innermost switch merge block.
+        auto lastMergeInst = state_.back().BreakMergeInst();
+        state_.emplace_back(lastMergeInst, mergeInst);
+      }
+    }
+  }
+}
+
 bool MergeReturnPass::ProcessStructured(
     Function* function, const std::vector<BasicBlock*>& return_blocks) {
   if (HasNontrivialUnreachableBlocks(function)) {
@@ -82,7 +108,7 @@ bool MergeReturnPass::ProcessStructured(
   }
 
   RecordImmediateDominators(function);
-  AddDummyLoopAroundFunction();
+  AddDummySwitchAroundFunction();
 
   std::list<BasicBlock*> order;
   cfg()->ComputeStructuredOrder(function, &*function->begin(), &order);
@@ -103,12 +129,8 @@ bool MergeReturnPass::ProcessStructured(
 
     ProcessStructuredBlock(block);
 
-    // Generate state for next block
-    if (Instruction* mergeInst = block->GetMergeInst()) {
-      Instruction* loopMergeInst = block->GetLoopMergeInst();
-      if (!loopMergeInst) loopMergeInst = state_.back().LoopMergeInst();
-      state_.emplace_back(loopMergeInst, mergeInst);
-    }
+    // Generate state for next block if warranted
+    GenerateState(block);
   }
 
   state_.clear();
@@ -133,12 +155,8 @@ bool MergeReturnPass::ProcessStructured(
       }
     }
 
-    // Generate state for next block
-    if (Instruction* mergeInst = block->GetMergeInst()) {
-      Instruction* loopMergeInst = block->GetLoopMergeInst();
-      if (!loopMergeInst) loopMergeInst = state_.back().LoopMergeInst();
-      state_.emplace_back(loopMergeInst, mergeInst);
-    }
+    // Generate state for next block if warranted
+    GenerateState(block);
   }
 
   // We have not kept the dominator tree up-to-date.
@@ -202,8 +220,8 @@ void MergeReturnPass::ProcessStructuredBlock(BasicBlock* block) {
 
   if (tail_opcode == SpvOpReturn || tail_opcode == SpvOpReturnValue ||
       tail_opcode == SpvOpUnreachable) {
-    assert(CurrentState().InLoop() && "Should be in the dummy loop.");
-    BranchToBlock(block, CurrentState().LoopMergeId());
+    assert(CurrentState().InBreakable() && "Should be in the dummy construct.");
+    BranchToBlock(block, CurrentState().BreakMergeId());
     return_blocks_.insert(block->id());
   }
 }
@@ -337,8 +355,8 @@ bool MergeReturnPass::PredicateBlocks(
   std::unordered_set<BasicBlock*> seen;
   if (block->id() == state->CurrentMergeId()) {
     state++;
-  } else if (block->id() == state->LoopMergeId()) {
-    while (state->LoopMergeId() == block->id()) {
+  } else if (block->id() == state->BreakMergeId()) {
+    while (state->BreakMergeId() == block->id()) {
       state++;
     }
   }
@@ -346,15 +364,14 @@ bool MergeReturnPass::PredicateBlocks(
   while (block != nullptr && block != final_return_block_) {
     if (!predicated->insert(block).second) break;
     // Skip structured subgraphs.
-    assert(state->InLoop() && "Should be in the dummy loop at the very least.");
-    Instruction* current_loop_merge_inst = state->LoopMergeInst();
-    uint32_t merge_block_id =
-        current_loop_merge_inst->GetSingleWordInOperand(0);
-    while (state->LoopMergeId() == merge_block_id) {
+    assert(state->InBreakable() &&
+           "Should be in the dummy construct at the very least.");
+    Instruction* break_merge_inst = state->BreakMergeInst();
+    uint32_t merge_block_id = break_merge_inst->GetSingleWordInOperand(0);
+    while (state->BreakMergeId() == merge_block_id) {
       state++;
     }
-    if (!BreakFromConstruct(block, predicated, order,
-                            current_loop_merge_inst)) {
+    if (!BreakFromConstruct(block, predicated, order, break_merge_inst)) {
       return false;
     }
     block = context()->get_instr_block(merge_block_id);
@@ -364,9 +381,7 @@ bool MergeReturnPass::PredicateBlocks(
 
 bool MergeReturnPass::BreakFromConstruct(
     BasicBlock* block, std::unordered_set<BasicBlock*>* predicated,
-    std::list<BasicBlock*>* order, Instruction* loop_merge_inst) {
-  assert(loop_merge_inst->opcode() == SpvOpLoopMerge &&
-         "loop_merge_inst must be a loop merge instruction.");
+    std::list<BasicBlock*>* order, Instruction* break_merge_inst) {
   // Make sure the CFG is build here.  If we don't then it becomes very hard
   // to know which new blocks need to be updated.
   context()->BuildInvalidAnalyses(IRContext::kAnalysisCFG);
@@ -388,7 +403,7 @@ bool MergeReturnPass::BreakFromConstruct(
     }
   }
 
-  uint32_t merge_block_id = loop_merge_inst->GetSingleWordInOperand(0);
+  uint32_t merge_block_id = break_merge_inst->GetSingleWordInOperand(0);
   BasicBlock* merge_block = context()->get_instr_block(merge_block_id);
   if (merge_block->GetLoopMergeInst()) {
     cfg()->SplitLoopHeader(merge_block);
@@ -416,9 +431,10 @@ bool MergeReturnPass::BreakFromConstruct(
 
   // If |block| was a continue target for a loop |old_body| is now the correct
   // continue target.
-  if (loop_merge_inst->GetSingleWordInOperand(1) == block->id()) {
-    loop_merge_inst->SetInOperand(1, {old_body->id()});
-    context()->UpdateDefUse(loop_merge_inst);
+  if (break_merge_inst->opcode() == SpvOpLoopMerge &&
+      break_merge_inst->GetSingleWordInOperand(1) == block->id()) {
+    break_merge_inst->SetInOperand(1, {old_body->id()});
+    context()->UpdateDefUse(break_merge_inst);
   }
 
   // Update |order| so old_block will be traversed.
@@ -430,8 +446,8 @@ bool MergeReturnPass::BreakFromConstruct(
   // 3. Update OpPhi instructions in |merge_block|.
   // 4. Update the CFG.
   //
-  // Sine we are branching to the merge block of the current construct, there is
-  // no need for an OpSelectionMerge.
+  // Since we are branching to the merge block of the current construct, there
+  // is no need for an OpSelectionMerge.
 
   InstructionBuilder builder(
       context(), block,
@@ -710,7 +726,7 @@ void MergeReturnPass::InsertAfterElement(BasicBlock* element,
   list->insert(pos, new_element);
 }
 
-void MergeReturnPass::AddDummyLoopAroundFunction() {
+void MergeReturnPass::AddDummySwitchAroundFunction() {
   CreateReturnBlock();
   CreateReturn(final_return_block_);
 
@@ -718,7 +734,7 @@ void MergeReturnPass::AddDummyLoopAroundFunction() {
     cfg()->RegisterBlock(final_return_block_);
   }
 
-  CreateDummyLoop(final_return_block_);
+  CreateDummySwitch(final_return_block_);
 }
 
 BasicBlock* MergeReturnPass::CreateContinueTarget(uint32_t header_label_id) {
@@ -753,14 +769,8 @@ BasicBlock* MergeReturnPass::CreateContinueTarget(uint32_t header_label_id) {
   return new_block;
 }
 
-void MergeReturnPass::CreateDummyLoop(BasicBlock* merge_target) {
-  std::unique_ptr<Instruction> label(
-      new Instruction(context(), SpvOpLabel, 0u, TakeNextId(), {}));
-
-  // Create the new basic block
-  std::unique_ptr<BasicBlock> block(new BasicBlock(std::move(label)));
-
-  // Insert the new block before any code is run.  We have to split the entry
+void MergeReturnPass::CreateDummySwitch(BasicBlock* merge_target) {
+  // Insert the switch before any code is run.  We have to split the entry
   // block to make sure the OpVariable instructions remain in the entry block.
   BasicBlock* start_block = &*function_->begin();
   auto split_pos = start_block->begin();
@@ -771,38 +781,16 @@ void MergeReturnPass::CreateDummyLoop(BasicBlock* merge_target) {
   BasicBlock* old_block =
       start_block->SplitBasicBlock(context(), TakeNextId(), split_pos);
 
-  // The new block must be inserted after the entry block.  We cannot make the
-  // entry block the header for the dummy loop because it is not valid to have a
-  // branch to the entry block, and the continue target must branch back to the
-  // loop header.
-  auto pos = function_->begin();
-  pos++;
-  BasicBlock* header_block = &*pos.InsertBefore(std::move(block));
-  context()->AnalyzeDefUse(header_block->GetLabelInst());
-  header_block->SetParent(function_);
-
-  // We have to create the continue block before OpLoopMerge instruction.
-  // Otherwise the def-use manager will compalain that there is a use without a
-  // definition.
-  uint32_t continue_target = CreateContinueTarget(header_block->id())->id();
-
-  // Add the code the the header block.
+  // Add the switch to the end of the entry block.
   InstructionBuilder builder(
-      context(), header_block,
-      IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
-
-  builder.AddLoopMerge(merge_target->id(), continue_target);
-  builder.AddBranch(old_block->id());
-
-  // Fix up the entry block by adding a branch to the loop header.
-  InstructionBuilder builder2(
       context(), start_block,
       IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
-  builder2.AddBranch(header_block->id());
+
+  builder.AddSwitch(builder.GetUintConstantId(0u), old_block->id(), {},
+                    merge_target->id());
 
   if (context()->AreAnalysesValid(IRContext::kAnalysisCFG)) {
     cfg()->RegisterBlock(old_block);
-    cfg()->RegisterBlock(header_block);
     cfg()->AddEdges(start_block);
   }
 }

+ 38 - 38
3rdparty/spirv-tools/source/opt/merge_return_pass.h

@@ -33,7 +33,8 @@ namespace opt {
  * Structured control flow guarantees that the CFG will converge at a given
  * point (the merge block). Within structured control flow, all blocks must be
  * post-dominated by the merge block, except return blocks and break blocks.
- * A break block is a block that branches to the innermost loop's merge block.
+ * A break block is a block that branches to a containing construct's merge
+ * block.
  *
  * Beyond this, we further assume that all unreachable blocks have been
  * cleaned up.  This means that the only unreachable blocks are those necessary
@@ -46,13 +47,14 @@ namespace opt {
  * with a branch. If current block is not within structured control flow, this
  * is the final return. This block should branch to the new return block (its
  * direct successor). If the current block is within structured control flow,
- * the branch destination should be the innermost loop's merge.  This loop will
- * always exist because a dummy loop is added around the entire function.
- * If the merge block produces any live values it will need to be predicated.
- * While the merge is nested in structured control flow, the predication path
- *should branch to the merge block of the inner-most loop it is contained in.
- *Once structured control flow has been exited, it will be at the merge of the
- *dummy loop, with will simply return.
+ * the branch destination should be the innermost construct's merge.  This
+ * merge will always exist because a dummy switch is added around the
+ * entire function. If the merge block produces any live values it will need to
+ * be predicated. While the merge is nested in structured control flow, the
+ * predication path should branch to the merge block of the inner-most loop
+ * (or switch if no loop) it is contained in. Once structured control flow has
+ * been exited, it will be at the merge of the dummy switch, which will simply
+ * return.
  *
  * In the final return block, the return value should be loaded and returned.
  * Memory promotion passes should be able to promote the newly introduced
@@ -71,7 +73,7 @@ namespace opt {
  *         ||
  *         \/
  *
- *          0 (dummy loop header)
+ *          0 (dummy switch header)
  *          |
  *          1 (loop header)
  *         / \
@@ -81,11 +83,11 @@ namespace opt {
  *        / \
  *        |  3 (original code in 3)
  *        \ /
- *   (ret) 4 (dummy loop merge)
+ *   (ret) 4 (dummy switch merge)
  *
  * In the above (simple) example, the return originally in |2| is passed through
- * the merge. That merge is predicated such that the old body of the block is
- * the else branch. The branch condition is based on the value of the "has
+ * the loop merge. That merge is predicated such that the old body of the block
+ * is the else branch. The branch condition is based on the value of the "has
  * returned" variable.
  *
  ******************************************************************************/
@@ -108,17 +110,17 @@ class MergeReturnPass : public MemPass {
   }
 
  private:
-  // This class is used to store the a loop merge instruction and a selection
-  // merge instruction.  The intended use is that is represent the inner most
-  // contain selection construct and the inner most loop construct.
+  // This class is used to store the a break merge instruction and a current
+  // merge instruction.  The intended use is to keep track of the block to
+  // break to and the current innermost control flow construct merge block.
   class StructuredControlState {
    public:
-    StructuredControlState(Instruction* loop, Instruction* merge)
-        : loop_merge_(loop), current_merge_(merge) {}
+    StructuredControlState(Instruction* break_merge, Instruction* merge)
+        : break_merge_(break_merge), current_merge_(merge) {}
 
     StructuredControlState(const StructuredControlState&) = default;
 
-    bool InLoop() const { return loop_merge_; }
+    bool InBreakable() const { return break_merge_; }
     bool InStructuredFlow() const { return CurrentMergeId() != 0; }
 
     uint32_t CurrentMergeId() const {
@@ -132,20 +134,14 @@ class MergeReturnPass : public MemPass {
                             : 0;
     }
 
-    uint32_t LoopMergeId() const {
-      return loop_merge_ ? loop_merge_->GetSingleWordInOperand(0u) : 0u;
+    uint32_t BreakMergeId() const {
+      return break_merge_ ? break_merge_->GetSingleWordInOperand(0u) : 0u;
     }
 
-    uint32_t CurrentLoopHeader() const {
-      return loop_merge_
-                 ? loop_merge_->context()->get_instr_block(loop_merge_)->id()
-                 : 0;
-    }
-
-    Instruction* LoopMergeInst() const { return loop_merge_; }
+    Instruction* BreakMergeInst() const { return break_merge_; }
 
    private:
-    Instruction* loop_merge_;
+    Instruction* break_merge_;
     Instruction* current_merge_;
   };
 
@@ -159,6 +155,9 @@ class MergeReturnPass : public MemPass {
   void MergeReturnBlocks(Function* function,
                          const std::vector<BasicBlock*>& returnBlocks);
 
+  // Generate and push new control flow state if |block| contains a merge.
+  void GenerateState(BasicBlock* block);
+
   // Merges the return instruction in |function| so that it has a single return
   // statement.  It is assumed that |function| has structured control flow, and
   // that |return_blocks| is a list of all of the basic blocks in |function|
@@ -219,9 +218,9 @@ class MergeReturnPass : public MemPass {
                        std::list<BasicBlock*>* order);
 
   // Add a conditional branch at the start of |block| that either jumps to
-  // the merge block of |loop_merge_inst| or the original code in |block|
+  // the merge block of |break_merge_inst| or the original code in |block|
   // depending on the value in |return_flag_|.  The continue target in
-  // |loop_merge_inst| will be updated if needed.
+  // |break_merge_inst| will be updated if needed.
   //
   // If new blocks that are created will be added to |order|.  This way a call
   // can traverse these new block in structured order.
@@ -230,7 +229,7 @@ class MergeReturnPass : public MemPass {
   bool BreakFromConstruct(BasicBlock* block,
                           std::unordered_set<BasicBlock*>* predicated,
                           std::list<BasicBlock*>* order,
-                          Instruction* loop_merge_inst);
+                          Instruction* break_merge_inst);
 
   // Add an |OpReturn| or |OpReturnValue| to the end of |block|.  If an
   // |OpReturnValue| is needed, the return value is loaded from |return_value_|.
@@ -274,27 +273,28 @@ class MergeReturnPass : public MemPass {
   void InsertAfterElement(BasicBlock* element, BasicBlock* new_element,
                           std::list<BasicBlock*>* list);
 
-  // Creates a single iteration loop around all of the exectuable code of the
-  // current function and returns after the loop is done. Sets
+  // Creates a single case switch around all of the exectuable code of the
+  // current function where the switch and case value are both zero and the
+  // default is the merge block. Returns after the switch is executed. Sets
   // |final_return_block_|.
-  void AddDummyLoopAroundFunction();
+  void AddDummySwitchAroundFunction();
 
   // Creates a new basic block that branches to |header_label_id|.  Returns the
   // new basic block.  The block will be the second last basic block in the
   // function.
   BasicBlock* CreateContinueTarget(uint32_t header_label_id);
 
-  // Creates a loop around the executable code of the function with
+  // Creates a one case switch around the executable code of the function with
   // |merge_target| as the merge node.
-  void CreateDummyLoop(BasicBlock* merge_target);
+  void CreateDummySwitch(BasicBlock* merge_target);
 
   // Returns true if |function| has an unreachable block that is not a continue
   // target that simply branches back to the header, or a merge block containing
   // 1 instruction which is OpUnreachable.
   bool HasNontrivialUnreachableBlocks(Function* function);
 
-  // A stack used to keep track of the innermost contain loop and selection
-  // constructs.
+  // A stack used to keep track of the break and current control flow construct
+  // merge blocks.
   std::vector<StructuredControlState> state_;
 
   // The current function being transformed.

+ 2 - 0
3rdparty/spirv-tools/source/opt/module.cpp

@@ -95,6 +95,7 @@ void Module::ForEachInst(const std::function<void(Instruction*)>& f,
   DELEGATE(debugs1_);
   DELEGATE(debugs2_);
   DELEGATE(debugs3_);
+  DELEGATE(ext_inst_debuginfo_);
   DELEGATE(annotations_);
   DELEGATE(types_values_);
   for (auto& i : functions_) i->ForEachInst(f, run_on_debug_line_insts);
@@ -117,6 +118,7 @@ void Module::ForEachInst(const std::function<void(const Instruction*)>& f,
   for (auto& i : debugs3_) DELEGATE(i);
   for (auto& i : annotations_) DELEGATE(i);
   for (auto& i : types_values_) DELEGATE(i);
+  for (auto& i : ext_inst_debuginfo_) DELEGATE(i);
   for (auto& i : functions_) {
     static_cast<const Function*>(i.get())->ForEachInst(f,
                                                        run_on_debug_line_insts);

+ 33 - 0
3rdparty/spirv-tools/source/opt/module.h

@@ -102,6 +102,10 @@ class Module {
   // This is due to decision by the SPIR Working Group, pending publication.
   inline void AddDebug3Inst(std::unique_ptr<Instruction> d);
 
+  // Appends a debug info extension (OpenCL.DebugInfo.100 or DebugInfo)
+  // instruction to this module.
+  inline void AddExtInstDebugInfo(std::unique_ptr<Instruction> d);
+
   // Appends an annotation instruction to this module.
   inline void AddAnnotationInst(std::unique_ptr<Instruction> a);
 
@@ -182,6 +186,14 @@ class Module {
   inline IteratorRange<inst_iterator> debugs3();
   inline IteratorRange<const_inst_iterator> debugs3() const;
 
+  // Iterators for debug info instructions (excluding OpLine & OpNoLine)
+  // contained in this module.  These are OpExtInst for OpenCL.DebugInfo.100
+  // or DebugInfo extension placed between section 9 and 10.
+  inline inst_iterator ext_inst_debuginfo_begin();
+  inline inst_iterator ext_inst_debuginfo_end();
+  inline IteratorRange<inst_iterator> ext_inst_debuginfo();
+  inline IteratorRange<const_inst_iterator> ext_inst_debuginfo() const;
+
   // Iterators for entry point instructions contained in this module
   inline IteratorRange<inst_iterator> entry_points();
   inline IteratorRange<const_inst_iterator> entry_points() const;
@@ -274,6 +286,7 @@ class Module {
   InstructionList debugs1_;
   InstructionList debugs2_;
   InstructionList debugs3_;
+  InstructionList ext_inst_debuginfo_;
   InstructionList annotations_;
   // Type declarations, constants, and global variable declarations.
   InstructionList types_values_;
@@ -323,6 +336,10 @@ inline void Module::AddDebug3Inst(std::unique_ptr<Instruction> d) {
   debugs3_.push_back(std::move(d));
 }
 
+inline void Module::AddExtInstDebugInfo(std::unique_ptr<Instruction> d) {
+  ext_inst_debuginfo_.push_back(std::move(d));
+}
+
 inline void Module::AddAnnotationInst(std::unique_ptr<Instruction> a) {
   annotations_.push_back(std::move(a));
 }
@@ -403,6 +420,22 @@ inline IteratorRange<Module::const_inst_iterator> Module::debugs3() const {
   return make_range(debugs3_.begin(), debugs3_.end());
 }
 
+inline Module::inst_iterator Module::ext_inst_debuginfo_begin() {
+  return ext_inst_debuginfo_.begin();
+}
+inline Module::inst_iterator Module::ext_inst_debuginfo_end() {
+  return ext_inst_debuginfo_.end();
+}
+
+inline IteratorRange<Module::inst_iterator> Module::ext_inst_debuginfo() {
+  return make_range(ext_inst_debuginfo_.begin(), ext_inst_debuginfo_.end());
+}
+
+inline IteratorRange<Module::const_inst_iterator> Module::ext_inst_debuginfo()
+    const {
+  return make_range(ext_inst_debuginfo_.begin(), ext_inst_debuginfo_.end());
+}
+
 inline IteratorRange<Module::inst_iterator> Module::entry_points() {
   return make_range(entry_points_.begin(), entry_points_.end());
 }

+ 56 - 4
3rdparty/spirv-tools/source/opt/strip_debug_info_pass.cpp

@@ -19,14 +19,63 @@ namespace spvtools {
 namespace opt {
 
 Pass::Status StripDebugInfoPass::Process() {
-  bool modified = !context()->debugs1().empty() ||
-                  !context()->debugs2().empty() ||
-                  !context()->debugs3().empty();
+  bool uses_non_semantic_info = false;
+  for (auto& inst : context()->module()->extensions()) {
+    const char* ext_name =
+        reinterpret_cast<const char*>(&inst.GetInOperand(0).words[0]);
+    if (0 == std::strcmp(ext_name, "SPV_KHR_non_semantic_info")) {
+      uses_non_semantic_info = true;
+    }
+  }
 
   std::vector<Instruction*> to_kill;
-  for (auto& dbg : context()->debugs1()) to_kill.push_back(&dbg);
+
+  // if we use non-semantic info, it may reference OpString. Do a more
+  // expensive pass checking the uses of the OpString to see if any are
+  // OpExtInst on a non-semantic instruction set. If we're not using the
+  // extension then we can do a simpler pass and kill all debug1 instructions
+  if (uses_non_semantic_info) {
+    for (auto& inst : context()->module()->debugs1()) {
+      switch (inst.opcode()) {
+        case SpvOpString: {
+          analysis::DefUseManager* def_use = context()->get_def_use_mgr();
+
+          // see if this string is used anywhere by a non-semantic instruction
+          bool no_nonsemantic_use =
+              def_use->WhileEachUser(&inst, [def_use](Instruction* use) {
+                if (use->opcode() == SpvOpExtInst) {
+                  auto ext_inst_set =
+                      def_use->GetDef(use->GetSingleWordInOperand(0u));
+                  const char* extension_name = reinterpret_cast<const char*>(
+                      &ext_inst_set->GetInOperand(0).words[0]);
+                  if (0 == std::strncmp(extension_name, "NonSemantic.", 12)) {
+                    // found a non-semantic use, return false as we cannot
+                    // remove this OpString
+                    return false;
+                  }
+                }
+
+                // other instructions can't be a non-semantic use
+                return true;
+              });
+
+          if (no_nonsemantic_use) to_kill.push_back(&inst);
+
+          break;
+        }
+
+        default:
+          to_kill.push_back(&inst);
+          break;
+      }
+    }
+  } else {
+    for (auto& dbg : context()->debugs1()) to_kill.push_back(&dbg);
+  }
+
   for (auto& dbg : context()->debugs2()) to_kill.push_back(&dbg);
   for (auto& dbg : context()->debugs3()) to_kill.push_back(&dbg);
+  for (auto& dbg : context()->ext_inst_debuginfo()) to_kill.push_back(&dbg);
 
   // OpName must come first, since they may refer to other debug instructions.
   // If they are after the instructions that refer to, then they will be killed
@@ -38,8 +87,11 @@ Pass::Status StripDebugInfoPass::Process() {
               return false;
             });
 
+  bool modified = !to_kill.empty();
+
   for (auto* inst : to_kill) context()->KillInst(inst);
 
+  // clear OpLine information
   context()->module()->ForEachInst([&modified](Instruction* inst) {
     modified |= !inst->dbg_line_insts().empty();
     inst->dbg_line_insts().clear();

Some files were not shown because too many files changed in this diff