Stats analyzer aggregates OpConstant usage
diff --git a/source/spirv_stats.cpp b/source/spirv_stats.cpp
index ef80229..2186e0d 100644
--- a/source/spirv_stats.cpp
+++ b/source/spirv_stats.cpp
@@ -73,6 +73,7 @@
     ProcessOpcode();
     ProcessCapability();
     ProcessExtension();
+    ProcessConstant();
 
     return SPV_SUCCESS;
   }
@@ -108,6 +109,49 @@
     }
   }
 
+  // Collects OpConstant statistics.
+  void ProcessConstant() {
+    const Instruction& inst = GetCurrentInstruction();
+    if (inst.opcode() != SpvOpConstant) return;
+    const uint32_t type_id = inst.GetOperandAs<uint32_t>(0);
+    const auto type_decl_it = vstate_->all_definitions().find(type_id);
+    assert(type_decl_it != vstate_->all_definitions().end());
+    const Instruction& type_decl_inst = *type_decl_it->second;
+    const SpvOp type_op = type_decl_inst.opcode();
+    if (type_op == SpvOpTypeInt) {
+      const uint32_t bit_width = type_decl_inst.GetOperandAs<uint32_t>(1);
+      const uint32_t is_signed = type_decl_inst.GetOperandAs<uint32_t>(2);
+      assert(is_signed == 0 || is_signed == 1);
+      if (bit_width == 16) {
+        if (is_signed)
+          ++stats_->s16_constant_hist[inst.GetOperandAs<int16_t>(2)];
+        else
+          ++stats_->u16_constant_hist[inst.GetOperandAs<uint16_t>(2)];
+      } else if (bit_width == 32) {
+        if (is_signed)
+          ++stats_->s32_constant_hist[inst.GetOperandAs<int32_t>(2)];
+        else
+          ++stats_->u32_constant_hist[inst.GetOperandAs<uint32_t>(2)];
+      } else if (bit_width == 64) {
+        if (is_signed)
+          ++stats_->s64_constant_hist[inst.GetOperandAs<int64_t>(2)];
+        else
+          ++stats_->u64_constant_hist[inst.GetOperandAs<uint64_t>(2)];
+      } else {
+        assert(false && "TypeInt bit width is not 16, 32 or 64");
+      }
+    } else if (type_op == SpvOpTypeFloat) {
+      const uint32_t bit_width = type_decl_inst.GetOperandAs<uint32_t>(1);
+      if (bit_width == 32) {
+        ++stats_->f32_constant_hist[inst.GetOperandAs<float>(2)];
+      } else if (bit_width == 64) {
+        ++stats_->f64_constant_hist[inst.GetOperandAs<double>(2)];
+      } else {
+        assert(bit_width == 16);
+      }
+    }
+  }
+
   SpirvStats* stats() {
     return stats_;
   }
diff --git a/source/spirv_stats.h b/source/spirv_stats.h
index d639924..9c7a41a 100644
--- a/source/spirv_stats.h
+++ b/source/spirv_stats.h
@@ -39,6 +39,30 @@
   // Opcode histogram, SpvOpXXX -> count.
   std::unordered_map<uint32_t, uint32_t> opcode_hist;
 
+  // OpConstant u16 histogram, value -> count.
+  std::unordered_map<uint16_t, uint32_t> u16_constant_hist;
+
+  // OpConstant u32 histogram, value -> count.
+  std::unordered_map<uint32_t, uint32_t> u32_constant_hist;
+
+  // OpConstant u64 histogram, value -> count.
+  std::unordered_map<uint64_t, uint32_t> u64_constant_hist;
+
+  // OpConstant s16 histogram, value -> count.
+  std::unordered_map<int16_t, uint32_t> s16_constant_hist;
+
+  // OpConstant s32 histogram, value -> count.
+  std::unordered_map<int32_t, uint32_t> s32_constant_hist;
+
+  // OpConstant s64 histogram, value -> count.
+  std::unordered_map<int64_t, uint32_t> s64_constant_hist;
+
+  // OpConstant f32 histogram, value -> count.
+  std::unordered_map<float, uint32_t> f32_constant_hist;
+
+  // OpConstant f64 histogram, value -> count.
+  std::unordered_map<double, uint32_t> f64_constant_hist;
+
   // Used to collect statistics on opcodes triggering other opcodes.
   // Container scheme: gap between instructions -> cue opcode -> later opcode
   // -> count.
diff --git a/source/val/instruction.h b/source/val/instruction.h
index 7cef7e3..31b463a 100644
--- a/source/val/instruction.h
+++ b/source/val/instruction.h
@@ -15,8 +15,8 @@
 #ifndef LIBSPIRV_VAL_INSTRUCTION_H_
 #define LIBSPIRV_VAL_INSTRUCTION_H_
 
+#include <cassert>
 #include <cstdint>
-
 #include <functional>
 #include <utility>
 #include <vector>
@@ -76,6 +76,15 @@
     return inst_;
   }
 
+  // Casts the words belonging to the operand under |index| to |T| and returns.
+  template <typename T>
+  T GetOperandAs(size_t index) const {
+    const spv_parsed_operand_t& operand = operands_.at(index);
+    assert(operand.num_words * 4 >= sizeof(T));
+    assert(operand.offset + operand.num_words <= inst_.num_words);
+    return *reinterpret_cast<const T*>(&words_[operand.offset]);
+  }
+
  private:
   const std::vector<uint32_t> words_;
   const std::vector<spv_parsed_operand_t> operands_;
diff --git a/test/stats/stats_aggregate_test.cpp b/test/stats/stats_aggregate_test.cpp
index 8b23504..43026e8 100644
--- a/test/stats/stats_aggregate_test.cpp
+++ b/test/stats/stats_aggregate_test.cpp
@@ -339,4 +339,98 @@
       1u, stats.opcode_markov_hist[1].at(SpvOpTypeInt).at(SpvOpTypeFloat));
 }
 
+TEST(AggregateStats, ConstantLiteralsHistogram) {
+  const std::string code1 = R"(
+OpCapability Addresses
+OpCapability Kernel
+OpCapability GenericPointer
+OpCapability Linkage
+OpCapability Float64
+OpCapability Int16
+OpCapability Int64
+OpMemoryModel Physical32 OpenCL
+%u16 = OpTypeInt 16 0
+%u32 = OpTypeInt 32 0
+%u64 = OpTypeInt 64 0
+%f32 = OpTypeFloat 32
+%f64 = OpTypeFloat 64
+%1 = OpConstant %f32 0.1
+%2 = OpConstant %f32 -2
+%3 = OpConstant %f64 -2
+%4 = OpConstant %u16 16
+%5 = OpConstant %u16 2
+%6 = OpConstant %u32 32
+%7 = OpConstant %u64 64
+)";
+
+  const std::string code2 = R"(
+OpCapability Shader
+OpCapability Linkage
+OpCapability Int16
+OpCapability Int64
+OpMemoryModel Logical GLSL450
+%f32 = OpTypeFloat 32
+%u16 = OpTypeInt 16 0
+%s16 = OpTypeInt 16 1
+%u32 = OpTypeInt 32 0
+%s32 = OpTypeInt 32 1
+%u64 = OpTypeInt 64 0
+%s64 = OpTypeInt 64 1
+%1 = OpConstant %f32 0.1
+%2 = OpConstant %f32 -2
+%3 = OpConstant %u16 1
+%4 = OpConstant %u16 16
+%5 = OpConstant %u16 2
+%6 = OpConstant %s16 -16
+%7 = OpConstant %u32 32
+%8 = OpConstant %s32 2
+%9 = OpConstant %s32 -32
+%10 = OpConstant %u64 64
+%11 = OpConstant %s64 -64
+)";
+
+  SpirvStats stats;
+
+  CompileAndAggregateStats(code1, &stats);
+  EXPECT_EQ(2u, stats.f32_constant_hist.size());
+  EXPECT_EQ(1u, stats.f64_constant_hist.size());
+  EXPECT_EQ(1u, stats.f32_constant_hist.at(0.1f));
+  EXPECT_EQ(1u, stats.f32_constant_hist.at(-2.f));
+  EXPECT_EQ(1u, stats.f64_constant_hist.at(-2));
+
+  EXPECT_EQ(2u, stats.u16_constant_hist.size());
+  EXPECT_EQ(0u, stats.s16_constant_hist.size());
+  EXPECT_EQ(1u, stats.u32_constant_hist.size());
+  EXPECT_EQ(0u, stats.s32_constant_hist.size());
+  EXPECT_EQ(1u, stats.u64_constant_hist.size());
+  EXPECT_EQ(0u, stats.s64_constant_hist.size());
+  EXPECT_EQ(1u, stats.u16_constant_hist.at(16));
+  EXPECT_EQ(1u, stats.u16_constant_hist.at(2));
+  EXPECT_EQ(1u, stats.u32_constant_hist.at(32));
+  EXPECT_EQ(1u, stats.u64_constant_hist.at(64));
+
+  CompileAndAggregateStats(code2, &stats);
+  EXPECT_EQ(2u, stats.f32_constant_hist.size());
+  EXPECT_EQ(1u, stats.f64_constant_hist.size());
+  EXPECT_EQ(2u, stats.f32_constant_hist.at(0.1f));
+  EXPECT_EQ(2u, stats.f32_constant_hist.at(-2.f));
+  EXPECT_EQ(1u, stats.f64_constant_hist.at(-2));
+
+  EXPECT_EQ(3u, stats.u16_constant_hist.size());
+  EXPECT_EQ(1u, stats.s16_constant_hist.size());
+  EXPECT_EQ(1u, stats.u32_constant_hist.size());
+  EXPECT_EQ(2u, stats.s32_constant_hist.size());
+  EXPECT_EQ(1u, stats.u64_constant_hist.size());
+  EXPECT_EQ(1u, stats.s64_constant_hist.size());
+  EXPECT_EQ(2u, stats.u16_constant_hist.at(16));
+  EXPECT_EQ(2u, stats.u16_constant_hist.at(2));
+  EXPECT_EQ(1u, stats.u16_constant_hist.at(1));
+  EXPECT_EQ(1u, stats.s16_constant_hist.at(-16));
+  EXPECT_EQ(2u, stats.u32_constant_hist.at(32));
+  EXPECT_EQ(1u, stats.s32_constant_hist.at(2));
+  EXPECT_EQ(1u, stats.s32_constant_hist.at(-32));
+  EXPECT_EQ(2u, stats.u64_constant_hist.at(64));
+  EXPECT_EQ(1u, stats.s64_constant_hist.at(-64));
+}
+
 }  // namespace
diff --git a/tools/stats/stats.cpp b/tools/stats/stats.cpp
index 40e8f28..51d6183 100644
--- a/tools/stats/stats.cpp
+++ b/tools/stats/stats.cpp
@@ -151,5 +151,8 @@
   out << std::endl;
   analyzer.WriteOpcodeMarkov(out);
 
+  out << std::endl;
+  analyzer.WriteConstantLiterals(out);
+
   return 0;
 }
diff --git a/tools/stats/stats_analyzer.cpp b/tools/stats/stats_analyzer.cpp
index 428265c..9e248a4 100644
--- a/tools/stats/stats_analyzer.cpp
+++ b/tools/stats/stats_analyzer.cpp
@@ -47,8 +47,11 @@
   return libspirv::CapabilityToString(static_cast<SpvCapability>(word));
 }
 
-std::string KeyIsLabel(std::string key) {
-  return key;
+template <class T>
+std::string KeyIsLabel(T key) {
+  std::stringstream ss;
+  ss << key;
+  return ss.str();
 }
 
 template <class Key>
@@ -81,7 +84,7 @@
 // |label_from_key| is used to convert |Key| to label.
 template <class Key>
 void WriteFreq(std::ostream& out, const std::unordered_map<Key, double>& freq,
-               std::string (*label_from_key)(Key)) {
+               std::string (*label_from_key)(Key), double threshold = 0.001) {
   std::vector<std::pair<Key, double>> sorted_freq(freq.begin(), freq.end());
   std::sort(sorted_freq.begin(), sorted_freq.end(),
             [](const std::pair<Key, double>& left,
@@ -90,6 +93,8 @@
             });
 
   for (const auto& pair : sorted_freq) {
+    if (pair.second < threshold)
+      break;
     out << label_from_key(pair.first) << " " << pair.second * 100.0
         << "%" << std::endl;
   }
@@ -151,6 +156,34 @@
   WriteFreq(out, opcode_freq_, GetOpcodeString);
 }
 
+void StatsAnalyzer::WriteConstantLiterals(std::ostream& out) {
+  out << "Constant literals" << std::endl;
+
+  out << "Float 32" << std::endl;
+  WriteFreq(out, GetPrevalence(stats_.f32_constant_hist), KeyIsLabel);
+
+  out << std::endl << "Float 64" << std::endl;
+  WriteFreq(out, GetPrevalence(stats_.f64_constant_hist), KeyIsLabel);
+
+  out << std::endl << "Unsigned int 16" << std::endl;
+  WriteFreq(out, GetPrevalence(stats_.u16_constant_hist), KeyIsLabel);
+
+  out << std::endl << "Signed int 16" << std::endl;
+  WriteFreq(out, GetPrevalence(stats_.s16_constant_hist), KeyIsLabel);
+
+  out << std::endl << "Unsigned int 32" << std::endl;
+  WriteFreq(out, GetPrevalence(stats_.u32_constant_hist), KeyIsLabel);
+
+  out << std::endl << "Signed int 32" << std::endl;
+  WriteFreq(out, GetPrevalence(stats_.s32_constant_hist), KeyIsLabel);
+
+  out << std::endl << "Unsigned int 64" << std::endl;
+  WriteFreq(out, GetPrevalence(stats_.u64_constant_hist), KeyIsLabel);
+
+  out << std::endl << "Signed int 64" << std::endl;
+  WriteFreq(out, GetPrevalence(stats_.s64_constant_hist), KeyIsLabel);
+}
+
 void StatsAnalyzer::WriteOpcodeMarkov(std::ostream& out) {
   if (stats_.opcode_markov_hist.empty())
     return;
diff --git a/tools/stats/stats_analyzer.h b/tools/stats/stats_analyzer.h
index 809cde2..c1ff187 100644
--- a/tools/stats/stats_analyzer.h
+++ b/tools/stats/stats_analyzer.h
@@ -29,6 +29,7 @@
   void WriteCapability(std::ostream& out);
   void WriteExtension(std::ostream& out);
   void WriteOpcode(std::ostream& out);
+  void WriteConstantLiterals(std::ostream& out);
 
   // Writes first order Markov analysis to |out|.
   // stats_.opcode_markov_hist needs to contain raw data for at least one