HLSL: Add location offsets per resource type

This PR adds the ability to offset sampler, texture, and UBO bindings
from provided base bindings, and to auto-number bindings that are not
provided with explicit register numbers. The mechanism works as
follows:

- Offsets may be given on the command line for all stages, or
  individually for one or more single stages, in which case the
  offset will be auto-selected according to the stage being
  compiled. There is also an API to set them. The new command line
  options are --shift-sampler-binding, --shift-texture-binding, and
  --shift-UBO-binding.

- Uniforms which are not given explicit bindings in the source code
  are auto-numbered if and only if they are in live code as
  determined by the algorithm used to build the reflection
  database, and the --auto-map-bindings option is given. This auto-numbering
  avoids using any binding slots which were explicitly provided in
  the code, whether or not that explicit use was live. E.g, "uniform
  Texture1D foo : register(t3);" with --shift-texture-binding 10 will
  reserve binding 13, whether or not foo is used in live code.

- Shorter synonyms for the command line options are available.  See
  the --help output.

The testing infrastructure is slightly extended to allow use of the
binding offset API, and two new tests spv.register.(no)autoassign.frag are
added for comparing the resulting SPIR-V.
diff --git a/StandAlone/StandAlone.cpp b/StandAlone/StandAlone.cpp
index 606f247..063e416 100644
--- a/StandAlone/StandAlone.cpp
+++ b/StandAlone/StandAlone.cpp
@@ -1,6 +1,6 @@
 //
 //Copyright (C) 2002-2005  3Dlabs Inc. Ltd.
-//Copyright (C) 2013 LunarG, Inc.
+//Copyright (C) 2013-2016 LunarG, Inc.
 //
 //All rights reserved.
 //
@@ -48,7 +48,9 @@
 #include "../SPIRV/disassemble.h"
 #include <cstring>
 #include <cstdlib>
+#include <cctype>
 #include <cmath>
+#include <array>
 
 #include "../glslang/OSDependent/osinclude.h"
 
@@ -78,6 +80,7 @@
     EOptionOutputHexadecimal  = (1 << 16),
     EOptionReadHlsl           = (1 << 17),
     EOptionCascadingErrors    = (1 << 18),
+    EOptionAutoMapBindings    = (1 << 19),
 };
 
 //
@@ -96,7 +99,7 @@
 //
 // Forward declarations.
 //
-EShLanguage FindLanguage(const std::string& name);
+EShLanguage FindLanguage(const std::string& name, bool parseSuffix=true);
 void CompileFile(const char* fileName, ShHandle);
 void usage();
 void FreeFileData(char** data);
@@ -157,6 +160,10 @@
 const char* entryPointName = nullptr;
 const char* shaderStageName = nullptr;
 
+std::array<unsigned int, EShLangCount> baseSamplerBinding;
+std::array<unsigned int, EShLangCount> baseTextureBinding;
+std::array<unsigned int, EShLangCount> baseUboBinding;
+
 //
 // Create the default name for saving a binary if -o is not provided.
 //
@@ -205,6 +212,35 @@
 }
 
 //
+// Process an optional binding base of the form:
+//   --argname [stage] base
+// Where stage is one of the forms accepted by FindLanguage, and base is an integer
+//
+void ProcessBindingBase(int& argc, char**& argv, std::array<unsigned int, EShLangCount>& base)
+{
+    if (argc < 2)
+        usage();
+
+    if (!isdigit(argv[1][0])) {
+        if (argc < 3) // this form needs one more argument
+            usage();
+    
+        // Parse form: --argname stage base
+        const EShLanguage lang = FindLanguage(argv[1], false);
+        base[lang] = atoi(argv[2]);
+        argc-= 2;
+        argv+= 2;
+    } else {
+        // Parse form: --argname base
+        for (int lang=0; lang<EShLangCount; ++lang)
+            base[lang] = atoi(argv[1]);
+
+        argc--;
+        argv++;
+    }
+}
+
+//
 // Do all command-line argument parsing.  This includes building up the work-items
 // to be processed later, and saving all the command-line options.
 //
@@ -212,6 +248,10 @@
 //
 void ProcessArguments(int argc, char* argv[])
 {
+    baseSamplerBinding.fill(0);
+    baseTextureBinding.fill(0);
+    baseUboBinding.fill(0);
+
     ExecutableName = argv[0];
     NumWorkItems = argc;  // will include some empties where the '-' options were, but it doesn't matter, they'll be 0
     Work = new glslang::TWorkItem*[NumWorkItems];
@@ -223,6 +263,33 @@
     for (; argc >= 1; argc--, argv++) {
         if (argv[0][0] == '-') {
             switch (argv[0][1]) {
+            case '-':
+                {
+                    std::string lowerword(argv[0]+2);
+                    std::transform(lowerword.begin(), lowerword.end(), lowerword.begin(), ::tolower);
+
+                    // handle --word style options
+                    if (lowerword == "shift-sampler-bindings" || // synonyms
+                        lowerword == "shift-sampler-binding"  ||
+                        lowerword == "ssb") {
+                        ProcessBindingBase(argc, argv, baseSamplerBinding);
+                    } else if (lowerword == "shift-texture-bindings" ||  // synonyms
+                               lowerword == "shift-texture-binding"  ||
+                               lowerword == "stb") {
+                        ProcessBindingBase(argc, argv, baseTextureBinding);
+                    } else if (lowerword == "shift-ubo-bindings" ||  // synonyms
+                               lowerword == "shift-ubo-binding"  ||
+                               lowerword == "sub") {
+                        ProcessBindingBase(argc, argv, baseUboBinding);
+                    } else if (lowerword == "auto-map-bindings" ||  // synonyms
+                               lowerword == "auto-map-binding"  || 
+                               lowerword == "amb") {
+                        Options |= EOptionAutoMapBindings;
+                    } else {
+                        usage();
+                    }
+                }
+                break;
             case 'H':
                 Options |= EOptionHumanReadableSpv;
                 if ((Options & EOptionSpv) == 0) {
@@ -461,6 +528,14 @@
         shader->setStringsWithLengthsAndNames(compUnit.text, NULL, compUnit.fileNameList, 1);
         if (entryPointName) // HLSL todo: this needs to be tracked per compUnits
             shader->setEntryPoint(entryPointName);
+
+        shader->setShiftSamplerBinding(baseSamplerBinding[compUnit.stage]);
+        shader->setShiftTextureBinding(baseTextureBinding[compUnit.stage]);
+        shader->setShiftUboBinding(baseUboBinding[compUnit.stage]);
+
+        if (Options & EOptionAutoMapBindings)
+            shader->setAutoMapBindings(true);
+                
         shaders.push_back(shader);
 
         const int defaultVersion = Options & EOptionDefaultDesktop? 110: 100;
@@ -506,6 +581,10 @@
         PutsIfNonEmpty(program.getInfoDebugLog());
     }
 
+    // Map IO
+    if (Options & EOptionSpv)
+        program.mapIO();
+    
     // Reflect
     if (Options & EOptionDumpReflection) {
         program.buildReflection();
@@ -705,15 +784,22 @@
 //   .frag = fragment
 //   .comp = compute
 //
-EShLanguage FindLanguage(const std::string& name)
+EShLanguage FindLanguage(const std::string& name, bool parseSuffix)
 {
-    size_t ext = name.rfind('.');
-    if (ext == std::string::npos) {
-        usage();
-        return EShLangVertex;
+    size_t ext = 0;
+
+    // Search for a suffix on a filename: e.g, "myfile.frag".  If given
+    // the suffix directly, we skip looking the '.'
+    if (parseSuffix) {
+        ext = name.rfind('.');
+        if (ext == std::string::npos) {
+            usage();
+            return EShLangVertex;
+        }
+        ++ext;
     }
 
-    std::string suffix = name.substr(ext + 1, std::string::npos);
+    std::string suffix = name.substr(ext, std::string::npos);
     if (shaderStageName)
         suffix = shaderStageName;
 
@@ -831,6 +917,19 @@
            "  -v          print version strings\n"
            "  -w          suppress warnings (except as required by #extension : warn)\n"
            "  -x          save 32-bit hexadecimal numbers as text, requires a binary option (e.g., -V)\n"
+           "\n"
+           "  --shift-sampler-binding [stage] num     set base binding number for samplers\n"
+           "  --ssb [stage] num                       synonym for --shift-sampler-binding\n"
+           "\n"
+           "  --shift-texture-binding [stage] num     set base binding number for textures\n"
+           "  --stb [stage] num                       synonym for --shift-texture-binding\n"
+           "\n"
+           "  --shift-UBO-binding [stage] num         set base binding number for UBOs\n"
+           "  --sub [stage] num                       synonym for --shift-UBO-binding\n"
+           "\n"
+           "  --auto-map-bindings                     automatically bind uniform variables without\n"
+           "                                          explicit bindings.\n"
+           "  --amb                                   synonym for --auto-map-bindings\n"
            );
 
     exit(EFailUsage);