ExtractInsert: Handle rudimentary CompositeConstruct and ConstantComposite

This optimizes a single index extract whose composite value terminates with a
CompositeConstruct (or ConstantComposite) by evaluating to the correct
component. This was needed for opaque legalization.

This highlights the need/opportunity to improve this optimization to deal
with more complex composite expressions including currently handled ops
plus Null ops and special vector composition. A TODO has been added.
diff --git a/source/opt/insert_extract_elim.cpp b/source/opt/insert_extract_elim.cpp
index c543644..4ccaac7 100644
--- a/source/opt/insert_extract_elim.cpp
+++ b/source/opt/insert_extract_elim.cpp
@@ -55,6 +55,11 @@
   return true;
 }
 
+bool InsertExtractElimPass::IsVectorType(uint32_t typeId) {
+  ir::Instruction* typeInst = def_use_mgr_->GetDef(typeId);
+  return typeInst->opcode() == SpvOpTypeVector;
+}
+
 bool InsertExtractElimPass::EliminateInsertExtract(ir::Function* func) {
   bool modified = false;
   for (auto bi = func->begin(); bi != func->end(); ++bi) {
@@ -74,6 +79,33 @@
             cid = cinst->GetSingleWordInOperand(kInsertCompositeIdInIdx);
             cinst = def_use_mgr_->GetDef(cid);
           }
+          // If search ended with CompositeConstruct or ConstantComposite
+          // and the extract has one index, return the appropriate component.
+          // If a vector CompositeConstruct we make sure all preceding
+          // components are of component type (not vector composition).
+          // TODO(greg-lunarg): Handle multiple-indices, ConstantNull, special
+          // vector composition, and additional CompositeInsert.
+          if ((cinst->opcode() == SpvOpCompositeConstruct ||
+               cinst->opcode() == SpvOpConstantComposite) &&
+              (*ii).NumInOperands() == 2) {
+            uint32_t compIdx = (*ii).GetSingleWordInOperand(1);
+            if (IsVectorType(cinst->type_id())) {
+              if (compIdx < cinst->NumInOperands()) {
+                uint32_t i = 0;
+                for (; i <= compIdx; i++) {
+                  uint32_t compId = cinst->GetSingleWordInOperand(i);
+                  ir::Instruction* compInst = def_use_mgr_->GetDef(compId);
+                  if (compInst->type_id() != (*ii).type_id())
+                    break;
+                }
+                if (i > compIdx)
+                  replId = cinst->GetSingleWordInOperand(compIdx);
+              }
+            }
+            else {
+              replId = cinst->GetSingleWordInOperand(compIdx);
+            }
+          }
           if (replId != 0) {
             const uint32_t extId = ii->result_id();
             (void)def_use_mgr_->ReplaceAllUsesWith(extId, replId);
diff --git a/source/opt/insert_extract_elim.h b/source/opt/insert_extract_elim.h
index 52213db..cf3be23 100644
--- a/source/opt/insert_extract_elim.h
+++ b/source/opt/insert_extract_elim.h
@@ -52,6 +52,9 @@
   bool ExtInsConflict(
     const ir::Instruction* extInst, const ir::Instruction* insInst) const;
 
+  // Return true if |typeId| is a vector type
+  bool IsVectorType(uint32_t typeId);
+
   // Look for OpExtract on sequence of OpInserts in |func|. If there is an
   // insert with identical indices, replace the extract with the value
   // that is inserted if possible. Specifically, replace if there is no