Enable HLSL legalization

Also added known-good mechanism to fetch latest validated spirv-tools.
Also added -Od and -Os to disable optimizer and optimize for size.

Fetching spirv-tools is optional for both glsl and hlsl. Legalization
of hlsl is done by default if spirv-opt is present at cmake time.
Optimization for glsl is currently done through the option -Os.

Legalization testing is currently only done on four existing shaders.
A separate baseLegalResults directory holds those results. All previous
testing is done with the optimizer disabled.
diff --git a/SPIRV/GlslangToSpv.cpp b/SPIRV/GlslangToSpv.cpp
index 54dc61f..a0a1c83 100755
--- a/SPIRV/GlslangToSpv.cpp
+++ b/SPIRV/GlslangToSpv.cpp
@@ -52,6 +52,16 @@
 #endif
 }
 
+#ifdef ENABLE_OPT
+    #include "spirv-tools/optimizer.hpp"
+    #include "message.h"
+    #include "SPVRemapper.h"
+#endif
+
+#ifdef ENABLE_OPT
+using namespace spvtools;
+#endif
+
 // Glslang includes
 #include "../glslang/MachineIndependent/localintermediate.h"
 #include "../glslang/MachineIndependent/SymbolTable.h"
@@ -5960,6 +5970,12 @@
     out.close();
 }
 
+#ifdef ENABLE_OPT
+void errHandler(const std::string& str) {
+    std::cerr << str << std::endl;
+}
+#endif
+
 //
 // Set up the glslang traversal
 //
@@ -5988,6 +6004,49 @@
     it.finishSpv();
     it.dumpSpv(spirv);
 
+#ifdef ENABLE_OPT
+    // If from HLSL, run spirv-opt to "legalize" the SPIR-V for Vulkan
+    // eg. forward and remove memory writes of opaque types.
+    if ((intermediate.getSource() == EShSourceHlsl ||
+                options->optimizeSize) &&
+            !options->disableOptimizer) {
+        spv_target_env target_env = SPV_ENV_UNIVERSAL_1_2;
+
+        spvtools::Optimizer optimizer(target_env);
+        optimizer.SetMessageConsumer([](spv_message_level_t level,
+                                         const char* source,
+                                         const spv_position_t& position,
+                                         const char* message) {
+            std::cerr << StringifyMessage(level, source, position, message)
+                      << std::endl;
+        });
+
+        optimizer.RegisterPass(CreateInlineExhaustivePass());
+        optimizer.RegisterPass(CreateLocalAccessChainConvertPass());
+        optimizer.RegisterPass(CreateLocalSingleBlockLoadStoreElimPass());
+        optimizer.RegisterPass(CreateLocalSingleStoreElimPass());
+        optimizer.RegisterPass(CreateInsertExtractElimPass());
+        optimizer.RegisterPass(CreateAggressiveDCEPass());
+        optimizer.RegisterPass(CreateDeadBranchElimPass());
+        optimizer.RegisterPass(CreateBlockMergePass());
+        optimizer.RegisterPass(CreateLocalMultiStoreElimPass());
+        optimizer.RegisterPass(CreateInsertExtractElimPass());
+        optimizer.RegisterPass(CreateAggressiveDCEPass());
+        // TODO(greg-lunarg): Add this when AMD driver issues are resolved
+        // if (options->optimizeSize)
+        //     optimizer.RegisterPass(CreateCommonUniformElimPass());
+
+        if (!optimizer.Run(spirv.data(), spirv.size(), &spirv))
+            return;
+
+        // Remove dead module-level objects: functions, types, vars
+        // TODO(greg-lunarg): Switch to spirv-opt versions when available
+        spv::spirvbin_t Remapper(0);
+        Remapper.registerErrorHandler(errHandler);
+        Remapper.remap(spirv, spv::spirvbin_t::DCE_ALL);
+    }
+#endif
+
     glslang::GetThreadPoolAllocator().pop();
 }