Add constant == uniform facts. (#2660)

Adds a new (and first) kind of fact to the fact manager, which is that
a specific uniform value is guaranteed to be equal to a specific
constant.  The point of this is that such information (if known to be
true by some external source) can be used by spirv-fuzz to transform
the module in interesting ways that a static compiler cannot reverse
via compile-time analysis.

This change introduces protobuf messages for the fact, and adds
capabilities to the fact manager to store this kind of fact and
provide information about it.
diff --git a/source/fuzz/CMakeLists.txt b/source/fuzz/CMakeLists.txt
index c1c0c1f..e5e48a8 100644
--- a/source/fuzz/CMakeLists.txt
+++ b/source/fuzz/CMakeLists.txt
@@ -30,8 +30,8 @@
         fuzzer.h
         fuzzer_context.h
         fuzzer_pass.h
-        fuzzer_pass_add_useful_constructs.h
         fuzzer_pass_add_dead_breaks.h
+        fuzzer_pass_add_useful_constructs.h
         fuzzer_pass_permute_blocks.h
         fuzzer_pass_split_blocks.h
         fuzzer_util.h
@@ -48,6 +48,7 @@
         transformation_move_block_down.h
         transformation_replace_boolean_constant_with_constant_binary.h
         transformation_split_block.h
+        uniform_buffer_element_descriptor.h
         ${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.h
 
         fact_manager.cpp
@@ -71,6 +72,7 @@
         transformation_move_block_down.cpp
         transformation_replace_boolean_constant_with_constant_binary.cpp
         transformation_split_block.cpp
+        uniform_buffer_element_descriptor.cpp
         ${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.cc
         )
 
diff --git a/source/fuzz/fact_manager.cpp b/source/fuzz/fact_manager.cpp
index 1c19287..3716941 100644
--- a/source/fuzz/fact_manager.cpp
+++ b/source/fuzz/fact_manager.cpp
@@ -12,15 +12,242 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <utility>
-
 #include "source/fuzz/fact_manager.h"
+
+#include "source/fuzz/uniform_buffer_element_descriptor.h"
 #include "source/opt/ir_context.h"
 
 namespace spvtools {
 namespace fuzz {
 
-FactManager::FactManager() = default;
+// The purpose of this struct is to group the fields and data used to represent
+// facts about uniform constants.
+struct FactManager::ConstantUniformFacts {
+  // See method in FactManager which delegates to this method.
+  bool AddFact(const protobufs::FactConstantUniform& fact,
+               opt::IRContext* context);
+
+  // See method in FactManager which delegates to this method.
+  std::vector<uint32_t> GetConstantsAvailableFromUniformsForType(
+      opt::IRContext* ir_context, uint32_t type_id) const;
+
+  // See method in FactManager which delegates to this method.
+  const std::vector<protobufs::UniformBufferElementDescriptor>
+  GetUniformDescriptorsForConstant(opt::IRContext* ir_context,
+                                   uint32_t constant_id) const;
+
+  // See method in FactManager which delegates to this method.
+  uint32_t GetConstantFromUniformDescriptor(
+      opt::IRContext* context,
+      const protobufs::UniformBufferElementDescriptor& uniform_descriptor)
+      const;
+
+  // See method in FactManager which delegates to this method.
+  std::vector<uint32_t> GetTypesForWhichUniformValuesAreKnown() const;
+
+  // Returns true if and only if the words associated with
+  // |constant_instruction| exactly match the words for the constant associated
+  // with |constant_uniform_fact|.
+  bool DataMatches(
+      const opt::Instruction& constant_instruction,
+      const protobufs::FactConstantUniform& constant_uniform_fact) const;
+
+  // Yields the constant words associated with |constant_uniform_fact|.
+  std::vector<uint32_t> GetConstantWords(
+      const protobufs::FactConstantUniform& constant_uniform_fact) const;
+
+  // Yields the id of a constant of type |type_id| whose data matches the
+  // constant data in |constant_uniform_fact|, or 0 if no such constant is
+  // declared.
+  uint32_t GetConstantId(
+      opt::IRContext* context,
+      const protobufs::FactConstantUniform& constant_uniform_fact,
+      uint32_t type_id) const;
+
+  std::vector<std::pair<protobufs::FactConstantUniform, uint32_t>>
+      facts_and_type_ids;
+};
+
+uint32_t FactManager::ConstantUniformFacts::GetConstantId(
+    opt::IRContext* context,
+    const protobufs::FactConstantUniform& constant_uniform_fact,
+    uint32_t type_id) const {
+  auto type = context->get_type_mgr()->GetType(type_id);
+  assert(type != nullptr && "Unknown type id.");
+  auto constant = context->get_constant_mgr()->GetConstant(
+      type, GetConstantWords(constant_uniform_fact));
+  return context->get_constant_mgr()->FindDeclaredConstant(constant, type_id);
+}
+
+std::vector<uint32_t> FactManager::ConstantUniformFacts::GetConstantWords(
+    const protobufs::FactConstantUniform& constant_uniform_fact) const {
+  std::vector<uint32_t> result;
+  for (auto constant_word : constant_uniform_fact.constant_word()) {
+    result.push_back(constant_word);
+  }
+  return result;
+}
+
+bool FactManager::ConstantUniformFacts::DataMatches(
+    const opt::Instruction& constant_instruction,
+    const protobufs::FactConstantUniform& constant_uniform_fact) const {
+  assert(constant_instruction.opcode() == SpvOpConstant);
+  std::vector<uint32_t> data_in_constant;
+  for (uint32_t i = 0; i < constant_instruction.NumInOperands(); i++) {
+    data_in_constant.push_back(constant_instruction.GetSingleWordInOperand(i));
+  }
+  return data_in_constant == GetConstantWords(constant_uniform_fact);
+}
+
+std::vector<uint32_t>
+FactManager::ConstantUniformFacts::GetConstantsAvailableFromUniformsForType(
+    opt::IRContext* ir_context, uint32_t type_id) const {
+  std::vector<uint32_t> result;
+  std::set<uint32_t> already_seen;
+  for (auto& fact_and_type_id : facts_and_type_ids) {
+    if (fact_and_type_id.second != type_id) {
+      continue;
+    }
+    if (auto constant_id =
+            GetConstantId(ir_context, fact_and_type_id.first, type_id)) {
+      if (already_seen.find(constant_id) == already_seen.end()) {
+        result.push_back(constant_id);
+        already_seen.insert(constant_id);
+      }
+    }
+  }
+  return result;
+}
+
+const std::vector<protobufs::UniformBufferElementDescriptor>
+FactManager::ConstantUniformFacts::GetUniformDescriptorsForConstant(
+    opt::IRContext* ir_context, uint32_t constant_id) const {
+  std::vector<protobufs::UniformBufferElementDescriptor> result;
+  auto constant_inst = ir_context->get_def_use_mgr()->GetDef(constant_id);
+  assert(constant_inst->opcode() == SpvOpConstant &&
+         "The given id must be that of a constant");
+  auto type_id = constant_inst->type_id();
+  for (auto& fact_and_type_id : facts_and_type_ids) {
+    if (fact_and_type_id.second != type_id) {
+      continue;
+    }
+    if (DataMatches(*constant_inst, fact_and_type_id.first)) {
+      result.emplace_back(
+          fact_and_type_id.first.uniform_buffer_element_descriptor());
+    }
+  }
+  return result;
+}
+
+uint32_t FactManager::ConstantUniformFacts::GetConstantFromUniformDescriptor(
+    opt::IRContext* context,
+    const protobufs::UniformBufferElementDescriptor& uniform_descriptor) const {
+  // Consider each fact.
+  for (auto& fact_and_type : facts_and_type_ids) {
+    // Check whether the uniform descriptor associated with the fact matches
+    // |uniform_descriptor|.
+    if (UniformBufferElementDescriptorEquals()(
+            &uniform_descriptor,
+            &fact_and_type.first.uniform_buffer_element_descriptor())) {
+      return GetConstantId(context, fact_and_type.first, fact_and_type.second);
+    }
+  }
+  // No fact associated with the given uniform descriptor was found.
+  return 0;
+}
+
+std::vector<uint32_t>
+FactManager::ConstantUniformFacts::GetTypesForWhichUniformValuesAreKnown()
+    const {
+  std::vector<uint32_t> result;
+  for (auto& fact_and_type : facts_and_type_ids) {
+    if (std::find(result.begin(), result.end(), fact_and_type.second) ==
+        result.end()) {
+      result.push_back(fact_and_type.second);
+    }
+  }
+  return result;
+}
+
+bool FactManager::ConstantUniformFacts::AddFact(
+    const protobufs::FactConstantUniform& fact, opt::IRContext* context) {
+  auto should_be_uniform_variable = context->get_def_use_mgr()->GetDef(
+      fact.uniform_buffer_element_descriptor().uniform_variable_id());
+  if (!should_be_uniform_variable) {
+    return false;
+  }
+  if (SpvOpVariable != should_be_uniform_variable->opcode()) {
+    return false;
+  }
+  if (SpvStorageClassUniform !=
+      should_be_uniform_variable->GetSingleWordInOperand(0)) {
+    return false;
+  }
+  auto should_be_uniform_pointer_type =
+      context->get_type_mgr()->GetType(should_be_uniform_variable->type_id());
+  if (!should_be_uniform_pointer_type->AsPointer()) {
+    return false;
+  }
+  if (should_be_uniform_pointer_type->AsPointer()->storage_class() !=
+      SpvStorageClassUniform) {
+    return false;
+  }
+  auto should_be_uniform_pointer_instruction =
+      context->get_def_use_mgr()->GetDef(should_be_uniform_variable->type_id());
+  auto element_type =
+      should_be_uniform_pointer_instruction->GetSingleWordInOperand(1);
+
+  for (auto index : fact.uniform_buffer_element_descriptor().index()) {
+    auto should_be_composite_type =
+        context->get_def_use_mgr()->GetDef(element_type);
+    if (SpvOpTypeStruct == should_be_composite_type->opcode()) {
+      if (index >= should_be_composite_type->NumInOperands()) {
+        return false;
+      }
+      element_type = should_be_composite_type->GetSingleWordInOperand(index);
+    } else if (SpvOpTypeArray == should_be_composite_type->opcode()) {
+      auto array_length_constant =
+          context->get_constant_mgr()
+              ->GetConstantFromInst(context->get_def_use_mgr()->GetDef(
+                  should_be_composite_type->GetSingleWordInOperand(1)))
+              ->AsIntConstant();
+      if (array_length_constant->words().size() != 1) {
+        return false;
+      }
+      auto array_length = array_length_constant->GetU32();
+      if (index >= array_length) {
+        return false;
+      }
+      element_type = should_be_composite_type->GetSingleWordInOperand(0);
+    } else if (SpvOpTypeVector == should_be_composite_type->opcode()) {
+      auto vector_length = should_be_composite_type->GetSingleWordInOperand(1);
+      if (index >= vector_length) {
+        return false;
+      }
+      element_type = should_be_composite_type->GetSingleWordInOperand(0);
+    } else {
+      return false;
+    }
+  }
+  auto final_element_type = context->get_type_mgr()->GetType(element_type);
+  if (!(final_element_type->AsFloat() || final_element_type->AsInteger())) {
+    return false;
+  }
+  auto width = final_element_type->AsFloat()
+                   ? final_element_type->AsFloat()->width()
+                   : final_element_type->AsInteger()->width();
+  auto required_words = (width + 32 - 1) / 32;
+  if (static_cast<uint32_t>(fact.constant_word().size()) != required_words) {
+    return false;
+  }
+  facts_and_type_ids.emplace_back(
+      std::pair<protobufs::FactConstantUniform, uint32_t>(fact, element_type));
+  return true;
+}
+
+FactManager::FactManager() {
+  uniform_constant_facts_ = MakeUnique<ConstantUniformFacts>();
+}
 
 FactManager::~FactManager() = default;
 
@@ -36,11 +263,46 @@
   return true;
 }
 
-bool FactManager::AddFact(const spvtools::fuzz::protobufs::Fact&,
-                          spvtools::opt::IRContext*) {
-  assert(0 && "No facts are yet supported.");
+bool FactManager::AddFact(const spvtools::fuzz::protobufs::Fact& fact,
+                          spvtools::opt::IRContext* context) {
+  assert(fact.fact_case() == protobufs::Fact::kConstantUniformFact &&
+         "Right now this is the only fact.");
+  if (!uniform_constant_facts_->AddFact(fact.constant_uniform_fact(),
+                                        context)) {
+    return false;
+  }
   return true;
 }
 
+std::vector<uint32_t> FactManager::GetConstantsAvailableFromUniformsForType(
+    opt::IRContext* ir_context, uint32_t type_id) const {
+  return uniform_constant_facts_->GetConstantsAvailableFromUniformsForType(
+      ir_context, type_id);
+}
+
+const std::vector<protobufs::UniformBufferElementDescriptor>
+FactManager::GetUniformDescriptorsForConstant(opt::IRContext* ir_context,
+                                              uint32_t constant_id) const {
+  return uniform_constant_facts_->GetUniformDescriptorsForConstant(ir_context,
+                                                                   constant_id);
+}
+
+uint32_t FactManager::GetConstantFromUniformDescriptor(
+    opt::IRContext* context,
+    const protobufs::UniformBufferElementDescriptor& uniform_descriptor) const {
+  return uniform_constant_facts_->GetConstantFromUniformDescriptor(
+      context, uniform_descriptor);
+}
+
+std::vector<uint32_t> FactManager::GetTypesForWhichUniformValuesAreKnown()
+    const {
+  return uniform_constant_facts_->GetTypesForWhichUniformValuesAreKnown();
+}
+
+const std::vector<std::pair<protobufs::FactConstantUniform, uint32_t>>&
+FactManager::GetConstantUniformFactsAndTypes() const {
+  return uniform_constant_facts_->facts_and_type_ids;
+}
+
 }  // namespace fuzz
 }  // namespace spvtools
diff --git a/source/fuzz/fact_manager.h b/source/fuzz/fact_manager.h
index 49b6d6d..2eee9ab 100644
--- a/source/fuzz/fact_manager.h
+++ b/source/fuzz/fact_manager.h
@@ -15,7 +15,9 @@
 #ifndef SOURCE_FUZZ_FACT_MANAGER_H_
 #define SOURCE_FUZZ_FACT_MANAGER_H_
 
+#include <memory>
 #include <utility>
+#include <vector>
 
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
 #include "source/opt/constants.h"
@@ -45,6 +47,60 @@
   // Adds |fact| to the fact manager, checking it for validity with respect to
   // |context|. Returns true if and only if the fact is valid.
   bool AddFact(const protobufs::Fact& fact, opt::IRContext* context);
+
+  // The fact manager will ultimately be responsible for managing a few distinct
+  // categories of facts. In principle there could be different fact managers
+  // for each kind of fact, but in practice providing one 'go to' place for
+  // facts will be convenient.  To keep some separation, the public methods of
+  // the fact manager should be grouped according to the kind of fact to which
+  // they relate.  At present we only have one kind of fact: facts about
+  // uniform variables.
+
+  //==============================
+  // Querying facts about uniform constants
+
+  // Provides the distinct type ids for which at least one  "constant ==
+  // uniform element" fact is known.
+  std::vector<uint32_t> GetTypesForWhichUniformValuesAreKnown() const;
+
+  // Provides distinct constant ids with type |type_id| for which at least one
+  // "constant == uniform element" fact is known.  If multiple identically-
+  // valued constants are relevant, only one will appear in the sequence.
+  std::vector<uint32_t> GetConstantsAvailableFromUniformsForType(
+      opt::IRContext* ir_context, uint32_t type_id) const;
+
+  // Provides details of all uniform elements that are known to be equal to the
+  // constant associated with |constant_id| in |ir_context|.
+  const std::vector<protobufs::UniformBufferElementDescriptor>
+  GetUniformDescriptorsForConstant(opt::IRContext* ir_context,
+                                   uint32_t constant_id) const;
+
+  // Returns the id of a constant whose value is known to match that of
+  // |uniform_descriptor|, and whose type matches the type of the uniform
+  // element.  If multiple such constant is exist, the one that is returned
+  // is arbitrary.  Returns 0 if no such constant id exists.
+  uint32_t GetConstantFromUniformDescriptor(
+      opt::IRContext* context,
+      const protobufs::UniformBufferElementDescriptor& uniform_descriptor)
+      const;
+
+  // Returns all "constant == uniform element" facts known to the fact
+  // manager, pairing each fact with id of the type that is associated with
+  // both the constant and the uniform element.
+  const std::vector<std::pair<protobufs::FactConstantUniform, uint32_t>>&
+  GetConstantUniformFactsAndTypes() const;
+
+  // End of uniform constant facts
+  //==============================
+
+ private:
+  // For each distinct kind of fact to be managed, we use a separate opaque
+  // struct type.
+
+  struct ConstantUniformFacts;  // Opaque struct for holding data about uniform
+                                // buffer elements.
+  std::unique_ptr<ConstantUniformFacts>
+      uniform_constant_facts_;  // Unique pointer to internal data.
 };
 
 }  // namespace fuzz
diff --git a/source/fuzz/protobufs/spvtoolsfuzz.proto b/source/fuzz/protobufs/spvtoolsfuzz.proto
index f00c518..4bf8252 100644
--- a/source/fuzz/protobufs/spvtoolsfuzz.proto
+++ b/source/fuzz/protobufs/spvtoolsfuzz.proto
@@ -57,12 +57,63 @@
 
 }
 
+message UniformBufferElementDescriptor {
+
+  // Represents a data element inside a uniform buffer.  The element is
+  // specified via (a) the result id of a uniform variable in which the element
+  // is contained, and (b) a series of indices that need to be followed to get
+  // to the element (via fields and array/vector indices).
+  //
+  // Example: suppose %42 is the id of a uniform variable, and that the uniform
+  // variable has the following type (using GLSL-like syntax):
+  //
+  // struct S {
+  //   float f;
+  //   vec3 g;
+  //   int4 h[10];
+  // };
+  //
+  // Then:
+  // - 42[0] describes the 'f' field.
+  // - 42[1,1] describes the y component of the 'g' field.
+  // - 42[2,7,3] describes the w component of element 7 of the 'h' field
+
+  // The result id of a uniform variable.
+  uint32 uniform_variable_id = 1;
+
+  // An ordered sequence of indices through composite structures in the
+  // uniform buffer.
+  repeated uint32 index = 2;
+
+}
+
 message FactSequence {
   repeated Fact fact = 1;
 }
 
 message Fact {
-  // Currently there are no facts.
+  oneof fact {
+    // Order the fact options by numeric id (rather than alphabetically).
+    FactConstantUniform constant_uniform_fact = 1;
+  }
+}
+
+// Keep fact message types in alphabetical order:
+
+message FactConstantUniform {
+
+  // Records the fact that a uniform buffer element is guaranteed to be equal
+  // to a particular constant value.  spirv-fuzz can use such guarantees to
+  // obfuscate code, e.g. to manufacture an expression that will (due to the
+  // guarantee) evaluate to a particular value at runtime but in a manner that
+  // cannot be predicted at compile-time.
+
+  // An element of a uniform buffer
+  UniformBufferElementDescriptor uniform_buffer_element_descriptor = 1;
+
+  // The words of the associated constant
+  repeated uint32 constant_word = 2;
+
 }
 
 message TransformationSequence {
diff --git a/source/fuzz/uniform_buffer_element_descriptor.cpp b/source/fuzz/uniform_buffer_element_descriptor.cpp
new file mode 100644
index 0000000..cd840d3
--- /dev/null
+++ b/source/fuzz/uniform_buffer_element_descriptor.cpp
@@ -0,0 +1,39 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/uniform_buffer_element_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+
+protobufs::UniformBufferElementDescriptor MakeUniformBufferElementDescriptor(
+    uint32_t uniform_variable_id, std::vector<uint32_t>&& indices) {
+  protobufs::UniformBufferElementDescriptor result;
+  result.set_uniform_variable_id(uniform_variable_id);
+  for (auto index : indices) {
+    result.add_index(index);
+  }
+  return result;
+}
+
+bool UniformBufferElementDescriptorEquals::operator()(
+    const protobufs::UniformBufferElementDescriptor* first,
+    const protobufs::UniformBufferElementDescriptor* second) const {
+  return first->uniform_variable_id() == second->uniform_variable_id() &&
+         std::equal(first->index().begin(), first->index().end(),
+                    second->index().begin());
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/source/fuzz/uniform_buffer_element_descriptor.h b/source/fuzz/uniform_buffer_element_descriptor.h
new file mode 100644
index 0000000..b2e5962
--- /dev/null
+++ b/source/fuzz/uniform_buffer_element_descriptor.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_UNIFORM_BUFFER_ELEMENT_DESCRIPTOR_H_
+#define SOURCE_FUZZ_UNIFORM_BUFFER_ELEMENT_DESCRIPTOR_H_
+
+#include <algorithm>
+#include <vector>
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Factory method to create a uniform buffer element descriptor message from an
+// id and list of indices.
+protobufs::UniformBufferElementDescriptor MakeUniformBufferElementDescriptor(
+    uint32_t uniform_variable_id, std::vector<uint32_t>&& indices);
+
+// Equality function for uniform buffer element descriptors.
+struct UniformBufferElementDescriptorEquals {
+  bool operator()(
+      const protobufs::UniformBufferElementDescriptor* first,
+      const protobufs::UniformBufferElementDescriptor* second) const;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // #define SOURCE_FUZZ_UNIFORM_BUFFER_ELEMENT_DESCRIPTOR_H_
diff --git a/test/fuzz/CMakeLists.txt b/test/fuzz/CMakeLists.txt
index 88635c7..d4feaa7 100644
--- a/test/fuzz/CMakeLists.txt
+++ b/test/fuzz/CMakeLists.txt
@@ -18,6 +18,7 @@
           fuzz_test_util.h
 
           fuzzer_test.cpp
+          fact_manager_test.cpp
           fuzz_test_util.cpp
           transformation_add_constant_boolean_test.cpp
           transformation_add_constant_scalar_test.cpp
diff --git a/test/fuzz/fact_manager_test.cpp b/test/fuzz/fact_manager_test.cpp
new file mode 100644
index 0000000..0209a60
--- /dev/null
+++ b/test/fuzz/fact_manager_test.cpp
@@ -0,0 +1,534 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/uniform_buffer_element_descriptor.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+using opt::analysis::BoolConstant;
+using opt::analysis::FloatConstant;
+using opt::analysis::IntConstant;
+using opt::analysis::ScalarConstant;
+
+using opt::analysis::Bool;
+using opt::analysis::Float;
+using opt::analysis::Integer;
+using opt::analysis::Type;
+
+bool AddFactHelper(
+    FactManager* fact_manager, opt::IRContext* context,
+    std::vector<uint32_t>&& words,
+    const protobufs::UniformBufferElementDescriptor& descriptor) {
+  protobufs::FactConstantUniform constant_uniform_fact;
+  for (auto word : words) {
+    constant_uniform_fact.add_constant_word(word);
+  }
+  *constant_uniform_fact.mutable_uniform_buffer_element_descriptor() =
+      descriptor;
+  protobufs::Fact fact;
+  *fact.mutable_constant_uniform_fact() = constant_uniform_fact;
+  return fact_manager->AddFact(fact, context);
+}
+
+TEST(FactManagerTest, ConstantsAvailableViaUniforms) {
+  std::string shader = R"(
+               OpCapability Shader
+               OpCapability Int64
+               OpCapability Float64
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource GLSL 450
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+         %10 = OpTypeInt 32 0
+         %11 = OpTypeInt 32 1
+         %12 = OpTypeInt 64 0
+         %13 = OpTypeInt 64 1
+         %15 = OpTypeFloat 32
+         %16 = OpTypeFloat 64
+         %17 = OpConstant %11 5
+         %18 = OpConstant %11 20
+         %19 = OpTypeVector %10 4
+         %20 = OpConstant %11 6
+         %21 = OpTypeVector %12 4
+         %22 = OpConstant %11 10
+         %23 = OpTypeVector %11 4
+
+        %102 = OpTypeStruct %10 %10 %23
+        %101 = OpTypePointer Uniform %102
+        %100 = OpVariable %101 Uniform
+
+        %203 = OpTypeArray %23 %17
+        %202 = OpTypeArray %203 %18
+        %201 = OpTypePointer Uniform %202
+        %200 = OpVariable %201 Uniform
+
+        %305 = OpTypeStruct %16 %16 %16 %11 %16
+        %304 = OpTypeStruct %16 %16 %305
+        %303 = OpTypeStruct %304
+        %302 = OpTypeStruct %10 %303
+        %301 = OpTypePointer Uniform %302
+        %300 = OpVariable %301 Uniform
+
+        %400 = OpVariable %101 Uniform
+
+        %500 = OpVariable %201 Uniform
+
+        %604 = OpTypeArray %13 %20
+        %603 = OpTypeArray %604 %20
+        %602 = OpTypeArray %603 %20
+        %601 = OpTypePointer Uniform %602
+        %600 = OpVariable %601 Uniform
+
+        %703 = OpTypeArray %13 %20
+        %702 = OpTypeArray %703 %20
+        %701 = OpTypePointer Uniform %702
+        %700 = OpVariable %701 Uniform
+
+        %802 = OpTypeStruct %702 %602 %19 %202 %302
+        %801 = OpTypePointer Uniform %802
+        %800 = OpVariable %801 Uniform
+
+        %902 = OpTypeStruct %702 %802 %19 %202 %302
+        %901 = OpTypePointer Uniform %902
+        %900 = OpVariable %901 Uniform
+
+       %1003 = OpTypeStruct %802
+       %1002 = OpTypeArray %1003 %20
+       %1001 = OpTypePointer Uniform %1002
+       %1000 = OpVariable %1001 Uniform
+
+       %1101 = OpTypePointer Uniform %21
+       %1100 = OpVariable %1101 Uniform
+
+       %1202 = OpTypeArray %21 %20
+       %1201 = OpTypePointer Uniform %1202
+       %1200 = OpVariable %1201 Uniform
+
+       %1302 = OpTypeArray %21 %20
+       %1301 = OpTypePointer Uniform %1302
+       %1300 = OpVariable %1301 Uniform
+
+       %1402 = OpTypeArray %15 %22
+       %1401 = OpTypePointer Uniform %1402
+       %1400 = OpVariable %1401 Uniform
+
+       %1501 = OpTypePointer Uniform %1402
+       %1500 = OpVariable %1501 Uniform
+
+       %1602 = OpTypeArray %1402 %22
+       %1601 = OpTypePointer Uniform %1602
+       %1600 = OpVariable %1601 Uniform
+
+       %1704 = OpTypeStruct %16 %16 %16
+       %1703 = OpTypeArray %1704 %22
+       %1702 = OpTypeArray %1703 %22
+       %1701 = OpTypePointer Uniform %1702
+       %1700 = OpVariable %1701 Uniform
+
+       %1800 = OpVariable %1701 Uniform
+
+       %1906 = OpTypeStruct %16
+       %1905 = OpTypeStruct %1906
+       %1904 = OpTypeStruct %1905
+       %1903 = OpTypeStruct %1904
+       %1902 = OpTypeStruct %1903
+       %1901 = OpTypePointer Uniform %1902
+       %1900 = OpVariable %1901 Uniform
+
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  uint32_t buffer_int32_min[1];
+  uint32_t buffer_int64_1[2];
+  uint32_t buffer_int64_max[2];
+  uint32_t buffer_uint64_1[2];
+  uint32_t buffer_uint64_max[2];
+  uint32_t buffer_float_10[1];
+  uint32_t buffer_double_10[2];
+  uint32_t buffer_double_20[2];
+
+  {
+    int32_t temp = std::numeric_limits<int32_t>::min();
+    std::memcpy(&buffer_int32_min, &temp, sizeof(temp));
+  }
+
+  {
+    int64_t temp = 1;
+    std::memcpy(&buffer_int64_1, &temp, sizeof(temp));
+  }
+
+  {
+    int64_t temp = std::numeric_limits<int64_t>::max();
+    std::memcpy(&buffer_int64_max, &temp, sizeof(temp));
+  }
+
+  {
+    uint64_t temp = 1;
+    std::memcpy(&buffer_uint64_1, &temp, sizeof(temp));
+  }
+
+  {
+    uint64_t temp = std::numeric_limits<uint64_t>::max();
+    std::memcpy(&buffer_uint64_max, &temp, sizeof(temp));
+  }
+
+  {
+    float temp = 10.0f;
+    std::memcpy(&buffer_float_10, &temp, sizeof(float));
+  }
+
+  {
+    double temp = 10.0;
+    std::memcpy(&buffer_double_10, &temp, sizeof(temp));
+  }
+
+  {
+    double temp = 20.0;
+    std::memcpy(&buffer_double_20, &temp, sizeof(temp));
+  }
+
+  FactManager fact_manager;
+
+  uint32_t type_int32_id = 11;
+  uint32_t type_int64_id = 13;
+  uint32_t type_uint32_id = 10;
+  uint32_t type_uint64_id = 12;
+  uint32_t type_float_id = 15;
+  uint32_t type_double_id = 16;
+
+  // Initially there should be no facts about uniforms.
+  ASSERT_TRUE(fact_manager
+                  .GetConstantsAvailableFromUniformsForType(context.get(),
+                                                            type_uint32_id)
+                  .empty());
+
+  // 100[2][3] == int(1)
+  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), {1},
+                            MakeUniformBufferElementDescriptor(100, {2, 3})));
+
+  // 200[1][2][3] == int(1)
+  ASSERT_TRUE(
+      AddFactHelper(&fact_manager, context.get(), {1},
+                    MakeUniformBufferElementDescriptor(200, {1, 2, 3})));
+
+  // 300[1][0][2][3] == int(1)
+  ASSERT_TRUE(
+      AddFactHelper(&fact_manager, context.get(), {1},
+                    MakeUniformBufferElementDescriptor(300, {1, 0, 2, 3})));
+
+  // 400[2][3] = int32_min
+  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), {buffer_int32_min[0]},
+                            MakeUniformBufferElementDescriptor(400, {2, 3})));
+
+  // 500[1][2][3] = int32_min
+  ASSERT_TRUE(
+      AddFactHelper(&fact_manager, context.get(), {buffer_int32_min[0]},
+                    MakeUniformBufferElementDescriptor(500, {1, 2, 3})));
+
+  // 600[1][2][3] = int64_max
+  ASSERT_TRUE(AddFactHelper(
+      &fact_manager, context.get(), {buffer_int64_max[0], buffer_int64_max[1]},
+      MakeUniformBufferElementDescriptor(600, {1, 2, 3})));
+
+  // 700[1][1] = int64_max
+  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(),
+                            {buffer_int64_max[0], buffer_int64_max[1]},
+                            MakeUniformBufferElementDescriptor(700, {1, 1})));
+
+  // 800[2][3] = uint(1)
+  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), {1},
+                            MakeUniformBufferElementDescriptor(800, {2, 3})));
+
+  // 900[1][2][3] = uint(1)
+  ASSERT_TRUE(
+      AddFactHelper(&fact_manager, context.get(), {1},
+                    MakeUniformBufferElementDescriptor(900, {1, 2, 3})));
+
+  // 1000[1][0][2][3] = uint(1)
+  ASSERT_TRUE(
+      AddFactHelper(&fact_manager, context.get(), {1},
+                    MakeUniformBufferElementDescriptor(1000, {1, 0, 2, 3})));
+
+  // 1100[0] = uint64(1)
+  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(),
+                            {buffer_uint64_1[0], buffer_uint64_1[1]},
+                            MakeUniformBufferElementDescriptor(1100, {0})));
+
+  // 1200[0][0] = uint64_max
+  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(),
+                            {buffer_uint64_max[0], buffer_uint64_max[1]},
+                            MakeUniformBufferElementDescriptor(1200, {0, 0})));
+
+  // 1300[1][0] = uint64_max
+  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(),
+                            {buffer_uint64_max[0], buffer_uint64_max[1]},
+                            MakeUniformBufferElementDescriptor(1300, {1, 0})));
+
+  // 1400[6] = float(10.0)
+  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), {buffer_float_10[0]},
+                            MakeUniformBufferElementDescriptor(1400, {6})));
+
+  // 1500[7] = float(10.0)
+  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), {buffer_float_10[0]},
+                            MakeUniformBufferElementDescriptor(1500, {7})));
+
+  // 1600[9][9] = float(10.0)
+  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), {buffer_float_10[0]},
+                            MakeUniformBufferElementDescriptor(1600, {9, 9})));
+
+  // 1700[9][9][1] = double(10.0)
+  ASSERT_TRUE(AddFactHelper(
+      &fact_manager, context.get(), {buffer_double_10[0], buffer_double_10[1]},
+      MakeUniformBufferElementDescriptor(1700, {9, 9, 1})));
+
+  // 1800[9][9][2] = double(10.0)
+  ASSERT_TRUE(AddFactHelper(
+      &fact_manager, context.get(), {buffer_double_10[0], buffer_double_10[1]},
+      MakeUniformBufferElementDescriptor(1800, {9, 9, 2})));
+
+  // 1900[0][0][0][0][0] = double(20.0)
+  ASSERT_TRUE(AddFactHelper(
+      &fact_manager, context.get(), {buffer_double_20[0], buffer_double_20[1]},
+      MakeUniformBufferElementDescriptor(1900, {0, 0, 0, 0, 0})));
+
+  opt::Instruction::OperandList operands = {
+      {SPV_OPERAND_TYPE_LITERAL_INTEGER, {1}}};
+  context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
+      context.get(), SpvOpConstant, type_int32_id, 50, operands));
+  operands = {{SPV_OPERAND_TYPE_LITERAL_INTEGER, {buffer_int32_min[0]}}};
+  context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
+      context.get(), SpvOpConstant, type_int32_id, 51, operands));
+  operands = {{SPV_OPERAND_TYPE_LITERAL_INTEGER, {buffer_int64_max[0]}},
+              {SPV_OPERAND_TYPE_LITERAL_INTEGER, {buffer_int64_max[1]}}};
+  context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
+      context.get(), SpvOpConstant, type_int64_id, 52, operands));
+  operands = {{SPV_OPERAND_TYPE_LITERAL_INTEGER, {1}}};
+  context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
+      context.get(), SpvOpConstant, type_uint32_id, 53, operands));
+  operands = {{SPV_OPERAND_TYPE_LITERAL_INTEGER, {buffer_uint64_1[0]}},
+              {SPV_OPERAND_TYPE_LITERAL_INTEGER, {buffer_uint64_1[1]}}};
+  context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
+      context.get(), SpvOpConstant, type_uint64_id, 54, operands));
+  operands = {{SPV_OPERAND_TYPE_LITERAL_INTEGER, {buffer_uint64_max[0]}},
+              {SPV_OPERAND_TYPE_LITERAL_INTEGER, {buffer_uint64_max[1]}}};
+  context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
+      context.get(), SpvOpConstant, type_uint64_id, 55, operands));
+  operands = {{SPV_OPERAND_TYPE_LITERAL_INTEGER, {buffer_float_10[0]}}};
+  context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
+      context.get(), SpvOpConstant, type_float_id, 56, operands));
+  operands = {{SPV_OPERAND_TYPE_LITERAL_INTEGER, {buffer_double_10[0]}},
+              {SPV_OPERAND_TYPE_LITERAL_INTEGER, {buffer_double_10[1]}}};
+  context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
+      context.get(), SpvOpConstant, type_double_id, 57, operands));
+  operands = {{SPV_OPERAND_TYPE_LITERAL_INTEGER, {buffer_double_20[0]}},
+              {SPV_OPERAND_TYPE_LITERAL_INTEGER, {buffer_double_20[1]}}};
+  context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
+      context.get(), SpvOpConstant, type_double_id, 58, operands));
+
+  // A duplicate of the constant with id 59.
+  operands = {{SPV_OPERAND_TYPE_LITERAL_INTEGER, {1}}};
+  context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
+      context.get(), SpvOpConstant, type_int32_id, 59, operands));
+
+  context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+
+  // Constants 1 and int32_min are available.
+  ASSERT_EQ(2, fact_manager
+                   .GetConstantsAvailableFromUniformsForType(context.get(),
+                                                             type_int32_id)
+                   .size());
+  // Constant int64_max is available.
+  ASSERT_EQ(1, fact_manager
+                   .GetConstantsAvailableFromUniformsForType(context.get(),
+                                                             type_int64_id)
+                   .size());
+  // Constant 1u is available.
+  ASSERT_EQ(1, fact_manager
+                   .GetConstantsAvailableFromUniformsForType(context.get(),
+                                                             type_uint32_id)
+                   .size());
+  // Constants 1u and uint64_max are available.
+  ASSERT_EQ(2, fact_manager
+                   .GetConstantsAvailableFromUniformsForType(context.get(),
+                                                             type_uint64_id)
+                   .size());
+  // Constant 10.0 is available.
+  ASSERT_EQ(1, fact_manager
+                   .GetConstantsAvailableFromUniformsForType(context.get(),
+                                                             type_float_id)
+                   .size());
+  // Constants 10.0 and 20.0 are available.
+  ASSERT_EQ(2, fact_manager
+                   .GetConstantsAvailableFromUniformsForType(context.get(),
+                                                             type_double_id)
+                   .size());
+
+  ASSERT_EQ(std::numeric_limits<int64_t>::max(),
+            context->get_constant_mgr()
+                ->FindDeclaredConstant(
+                    fact_manager.GetConstantsAvailableFromUniformsForType(
+                        context.get(), type_int64_id)[0])
+                ->AsIntConstant()
+                ->GetS64());
+  ASSERT_EQ(1, context->get_constant_mgr()
+                   ->FindDeclaredConstant(
+                       fact_manager.GetConstantsAvailableFromUniformsForType(
+                           context.get(), type_uint32_id)[0])
+                   ->AsIntConstant()
+                   ->GetU32());
+  ASSERT_EQ(10.0f,
+            context->get_constant_mgr()
+                ->FindDeclaredConstant(
+                    fact_manager.GetConstantsAvailableFromUniformsForType(
+                        context.get(), type_float_id)[0])
+                ->AsFloatConstant()
+                ->GetFloat());
+  const std::vector<uint32_t>& double_constant_ids =
+      fact_manager.GetConstantsAvailableFromUniformsForType(context.get(),
+                                                            type_double_id);
+  ASSERT_EQ(10.0, context->get_constant_mgr()
+                      ->FindDeclaredConstant(double_constant_ids[0])
+                      ->AsFloatConstant()
+                      ->GetDouble());
+  ASSERT_EQ(20.0, context->get_constant_mgr()
+                      ->FindDeclaredConstant(double_constant_ids[1])
+                      ->AsFloatConstant()
+                      ->GetDouble());
+
+  const std::vector<protobufs::UniformBufferElementDescriptor>
+      descriptors_for_double_10 = fact_manager.GetUniformDescriptorsForConstant(
+          context.get(), double_constant_ids[0]);
+  ASSERT_EQ(2, descriptors_for_double_10.size());
+  {
+    auto temp = MakeUniformBufferElementDescriptor(1700, {9, 9, 1});
+    ASSERT_TRUE(UniformBufferElementDescriptorEquals()(
+        &temp, &descriptors_for_double_10[0]));
+  }
+  {
+    auto temp = MakeUniformBufferElementDescriptor(1800, {9, 9, 2});
+    ASSERT_TRUE(UniformBufferElementDescriptorEquals()(
+        &temp, &descriptors_for_double_10[1]));
+  }
+  const std::vector<protobufs::UniformBufferElementDescriptor>
+      descriptors_for_double_20 = fact_manager.GetUniformDescriptorsForConstant(
+          context.get(), double_constant_ids[1]);
+  ASSERT_EQ(1, descriptors_for_double_20.size());
+  {
+    auto temp = MakeUniformBufferElementDescriptor(1900, {0, 0, 0, 0, 0});
+    ASSERT_TRUE(UniformBufferElementDescriptorEquals()(
+        &temp, &descriptors_for_double_20[0]));
+  }
+
+  auto constant_1_id = fact_manager.GetConstantFromUniformDescriptor(
+      context.get(), MakeUniformBufferElementDescriptor(1800, {9, 9, 2}));
+  ASSERT_TRUE(constant_1_id);
+
+  auto constant_2_id = fact_manager.GetConstantFromUniformDescriptor(
+      context.get(), MakeUniformBufferElementDescriptor(1900, {0, 0, 0, 0, 0}));
+  ASSERT_TRUE(constant_2_id);
+
+  ASSERT_EQ(double_constant_ids[0], constant_1_id);
+
+  ASSERT_EQ(double_constant_ids[1], constant_2_id);
+}
+
+TEST(FactManagerTest, TwoConstantsWithSameValue) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "x"
+               OpName %10 "buf"
+               OpMemberName %10 0 "a"
+               OpName %12 ""
+               OpDecorate %8 RelaxedPrecision
+               OpMemberDecorate %10 0 RelaxedPrecision
+               OpMemberDecorate %10 0 Offset 0
+               OpDecorate %10 Block
+               OpDecorate %12 DescriptorSet 0
+               OpDecorate %12 Binding 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 1
+         %20 = OpConstant %6 1
+         %10 = OpTypeStruct %6
+         %11 = OpTypePointer Uniform %10
+         %12 = OpVariable %11 Uniform
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+               OpStore %8 %9
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  auto uniform_buffer_element_descriptor =
+      MakeUniformBufferElementDescriptor(12, {0});
+
+  // 12[0] = int(1)
+  ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), {1},
+                            uniform_buffer_element_descriptor));
+  auto constants =
+      fact_manager.GetConstantsAvailableFromUniformsForType(context.get(), 6);
+  ASSERT_EQ(1, constants.size());
+  ASSERT_TRUE(constants[0] == 9 || constants[0] == 20);
+
+  auto constant = fact_manager.GetConstantFromUniformDescriptor(
+      context.get(), uniform_buffer_element_descriptor);
+  ASSERT_TRUE(constant == 9 || constant == 20);
+
+  // Because the constants with ids 9 and 20 are equal, we should get the same
+  // single uniform buffer element descriptor when we look up the descriptors
+  // for either one of them.
+  for (auto constant_id : {9u, 20u}) {
+    auto descriptors = fact_manager.GetUniformDescriptorsForConstant(
+        context.get(), constant_id);
+    ASSERT_EQ(1, descriptors.size());
+    ASSERT_TRUE(UniformBufferElementDescriptorEquals()(
+        &uniform_buffer_element_descriptor, &descriptors[0]));
+  }
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools