diff --git a/glslang/MachineIndependent/iomapper.cpp b/glslang/MachineIndependent/iomapper.cpp
index 9585627086edfabb601b40a7947db2b3d64cdceb..fc85e97898c76f79d336747954e208df85d47206 100644
--- a/glslang/MachineIndependent/iomapper.cpp
+++ b/glslang/MachineIndependent/iomapper.cpp
@@ -423,14 +423,14 @@ struct TDefaultIoResolverBase : public glslang::TIoMapResolver
             return -1;
 
         // no locations added if already present, or a built-in variable
-        if (type.getQualifier().hasLocation() || type.getQualifier().builtIn != EbvNone)
+        if (type.getQualifier().hasLocation() || type.isBuiltIn())
             return -1;
 
         // no locations on blocks of built-in variables
         if (type.isStruct()) {
             if (type.getStruct()->size() < 1)
                 return -1;
-            if ((*type.getStruct())[0].type->getQualifier().builtIn != EbvNone)
+            if ((*type.getStruct())[0].type->isBuiltIn())
                 return -1;
         }
 
diff --git a/hlsl/hlslGrammar.cpp b/hlsl/hlslGrammar.cpp
index 05c95d36ae167a07fc9f11769b68fef959ddd146..0e8614cf37f36f7c133a1ffa575e3ecb6ff3809a 100755
--- a/hlsl/hlslGrammar.cpp
+++ b/hlsl/hlslGrammar.cpp
@@ -616,7 +616,7 @@ bool HlslGrammar::acceptFullySpecifiedType(TType& type, TIntermNode*& nodeList)
             qualifier.readonly     = type.getQualifier().readonly;
         }
 
-        if (type.getQualifier().builtIn != EbvNone)
+        if (type.isBuiltIn())
             qualifier.builtIn = type.getQualifier().builtIn;
 
         type.getQualifier()    = qualifier;
diff --git a/hlsl/hlslParseHelper.cpp b/hlsl/hlslParseHelper.cpp
index 32d106791d60a4ffd204dd433a1aa306a5868607..9915de5026bca40cb37564c45427f64f805cf526 100755
--- a/hlsl/hlslParseHelper.cpp
+++ b/hlsl/hlslParseHelper.cpp
@@ -1121,58 +1121,51 @@ bool HlslParseContext::isBuiltInMethod(const TSourceLoc&, TIntermTyped* base, co
         return false;
 }
 
+// Independently establish a built-in that is a member of a structure.
+// 'arraySizes' are what's desired for the independent built-in, whatever
+// the higher-level source/expression of them was.
+void HlslParseContext::splitBuiltIn(const TString& baseName, const TType& memberType, const TArraySizes* arraySizes,
+                                    const TQualifier& outerQualifier)
+{
+    TVariable* ioVar = makeInternalVariable(baseName + (baseName.empty() ? "" : "_") + memberType.getFieldName(), memberType);
+
+    if (arraySizes)
+        ioVar->getWritableType().newArraySizes(*arraySizes);
+
+    fixBuiltInIoType(ioVar->getWritableType());
+
+    splitBuiltIns[tInterstageIoData(memberType.getQualifier().builtIn, outerQualifier.storage)] = ioVar;
+    if (!isClipOrCullDistance(ioVar->getType()))
+        trackLinkage(*ioVar);
+
+    // Merge qualifier from the user structure
+    mergeQualifiers(ioVar->getWritableType().getQualifier(), outerQualifier);
+}
+
 // Split a type into
 //   1. a struct of non-I/O members
-//   2. a collection of flattened I/O variables
+//   2. a collection of independent I/O variables
 void HlslParseContext::split(const TVariable& variable)
 {
     // Create a new variable:
-    TType& splitType = split(*variable.getType().clone(), variable.getName());
+    const TType& clonedType = *variable.getType().clone();
+    const TType& splitType = split(clonedType, variable.getName(), clonedType.getQualifier());
     splitNonIoVars[variable.getUniqueId()] = makeInternalVariable(variable.getName(), splitType);
 }
 
 // Recursive implementation of split().
 // Returns reference to the modified type.
-TType& HlslParseContext::split(TType& type, TString name, const TType* outerStructType)
+const TType& HlslParseContext::split(const TType& type, const TString& name, const TQualifier& outerQualifier)
 {
-    const TArraySizes* arraySizes = nullptr;
-
-    // At the outer-most scope, remember the struct type so we can examine its storage class
-    // at deeper levels.
-    if (outerStructType == nullptr)
-        outerStructType = &type;
-
-    if (type.isArray())
-        arraySizes = &type.getArraySizes();
-
-    // We can ignore arrayness: it's uninvolved.
     if (type.isStruct()) {
         TTypeList* userStructure = type.getWritableStruct();
         for (auto ioType = userStructure->begin(); ioType != userStructure->end(); ) {
-            if (ioType->type->getQualifier().builtIn != EbvNone) {
-                // split out built-in interstage IO
-                const TType& memberType = *ioType->type;
-                TVariable* ioVar = makeInternalVariable(name + (name.empty() ? "" : "_") + memberType.getFieldName(),
-                                                        memberType);
-
-                if (arraySizes)
-                    ioVar->getWritableType().newArraySizes(*arraySizes);
-
-                fixBuiltInIoType(ioVar->getWritableType());
-
-                splitBuiltIns[tInterstageIoData(memberType, *outerStructType)] = ioVar;
-                if (!isClipOrCullDistance(ioVar->getType()))
-                    trackLinkage(*ioVar);
-
-                // Merge qualifier from the user structure
-                mergeQualifiers(ioVar->getWritableType().getQualifier(), outerStructType->getQualifier());
-
-                // Erase the IO vars from the user structure.
+            if (ioType->type->isBuiltIn()) {
+                // move out the built-in
+                splitBuiltIn(name, *ioType->type, type.getArraySizes(), outerQualifier);
                 ioType = userStructure->erase(ioType);
             } else {
-                split(*ioType->type,
-                      name + (name.empty() ? "" : "_") + ioType->type->getFieldName(),
-                      outerStructType);
+                split(*ioType->type, name + (name.empty() ? "" : "_") + ioType->type->getFieldName(), outerQualifier);
                 ++ioType;
             }
         }
@@ -1258,10 +1251,9 @@ int HlslParseContext::addFlattenedMember(const TVariable& variable, const TType&
         if (flattenData.nextBinding != TQualifier::layoutBindingEnd)
             memberVariable->getWritableType().getQualifier().layoutBinding = flattenData.nextBinding++;
 
-        if (memberVariable->getType().getQualifier().builtIn == EbvNone) {
+        if (!memberVariable->getType().isBuiltIn()) {
             // inherited locations must be auto bumped, not replicated
-            if (flattenData.nextLocation != TQualifier::layoutLocationEnd &&
-                memberVariable->getType().getQualifier().builtIn == EbvNone) {
+            if (flattenData.nextLocation != TQualifier::layoutLocationEnd) {
                 memberVariable->getWritableType().getQualifier().layoutLocation = flattenData.nextLocation;
                 flattenData.nextLocation += intermediate.computeTypeLocationSize(memberVariable->getType());
                 nextOutLocation = std::max(nextOutLocation, flattenData.nextLocation);
@@ -2530,8 +2522,10 @@ TIntermTyped* HlslParseContext::handleAssign(const TSourceLoc& loc, TOperator op
         if (split && derefType.isBuiltIn()) {
             // copy from interstage IO built-in if needed
             const TIntermTyped* outer = isLeft ? outerLeft : outerRight;
-            subTree = intermediate.addSymbol(*splitBuiltIns.find(
-                                             HlslParseContext::tInterstageIoData(derefType, outer->getType()))->second);
+            subTree = intermediate.addSymbol(
+                        *splitBuiltIns.find(HlslParseContext::tInterstageIoData(
+                                                derefType.getQualifier().builtIn,
+                                                outer->getType().getQualifier().storage))->second);
 
             // Arrayness of builtIn symbols isn't handled by the normal recursion:
             // it's been extracted and moved to the built-in.
diff --git a/hlsl/hlslParseHelper.h b/hlsl/hlslParseHelper.h
index b615a13e9c7f35f128363bbab2ae3e8287b7f2f1..9a30e0ae348948934241279635eb52bd1668dc3e 100755
--- a/hlsl/hlslParseHelper.h
+++ b/hlsl/hlslParseHelper.h
@@ -252,8 +252,9 @@ protected:
     bool isFinalFlattening(const TType& type) const { return !(type.isStruct() || type.isArray()); }
 
     // Structure splitting (splits interstage built-in types into its own struct)
-    TType& split(TType& type, TString name, const TType* outerStructType = nullptr);
     void split(const TVariable&);
+    void splitBuiltIn(const TString& baseName, const TType& memberType, const TArraySizes*, const TQualifier&);
+    const TType& split(const TType& type, const TString& name, const TQualifier&);
     bool wasSplit(const TIntermTyped* node) const;
     bool wasSplit(int id) const { return splitNonIoVars.find(id) != splitNonIoVars.end(); }
     TVariable* getSplitNonIoVar(int id) const;
@@ -393,10 +394,6 @@ protected:
         tInterstageIoData(TBuiltInVariable bi, TStorageQualifier q) :
             builtIn(bi), storage(q) { }
 
-        tInterstageIoData(const TType& memberType, const TType& storageType) :
-            builtIn(memberType.getQualifier().builtIn),
-            storage(storageType.getQualifier().storage) { }
-
         TBuiltInVariable  builtIn;
         TStorageQualifier storage;