fact_manager.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. // Copyright (c) 2019 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #include "source/fuzz/fact_manager.h"
  15. #include <map>
  16. #include <sstream>
  17. #include "source/fuzz/equivalence_relation.h"
  18. #include "source/fuzz/fuzzer_util.h"
  19. #include "source/fuzz/uniform_buffer_element_descriptor.h"
  20. #include "source/opt/ir_context.h"
  21. namespace spvtools {
  22. namespace fuzz {
  23. namespace {
  24. std::string ToString(const protobufs::Fact& fact) {
  25. assert(fact.fact_case() == protobufs::Fact::kConstantUniformFact &&
  26. "Right now this is the only fact.");
  27. std::stringstream stream;
  28. stream << "("
  29. << fact.constant_uniform_fact()
  30. .uniform_buffer_element_descriptor()
  31. .descriptor_set()
  32. << ", "
  33. << fact.constant_uniform_fact()
  34. .uniform_buffer_element_descriptor()
  35. .binding()
  36. << ")[";
  37. bool first = true;
  38. for (auto index : fact.constant_uniform_fact()
  39. .uniform_buffer_element_descriptor()
  40. .index()) {
  41. if (first) {
  42. first = false;
  43. } else {
  44. stream << ", ";
  45. }
  46. stream << index;
  47. }
  48. stream << "] == [";
  49. first = true;
  50. for (auto constant_word : fact.constant_uniform_fact().constant_word()) {
  51. if (first) {
  52. first = false;
  53. } else {
  54. stream << ", ";
  55. }
  56. stream << constant_word;
  57. }
  58. stream << "]";
  59. return stream.str();
  60. }
  61. } // namespace
  62. //=======================
  63. // Constant uniform facts
  64. // The purpose of this struct is to group the fields and data used to represent
  65. // facts about uniform constants.
  66. struct FactManager::ConstantUniformFacts {
  67. // See method in FactManager which delegates to this method.
  68. bool AddFact(const protobufs::FactConstantUniform& fact,
  69. opt::IRContext* context);
  70. // See method in FactManager which delegates to this method.
  71. std::vector<uint32_t> GetConstantsAvailableFromUniformsForType(
  72. opt::IRContext* ir_context, uint32_t type_id) const;
  73. // See method in FactManager which delegates to this method.
  74. const std::vector<protobufs::UniformBufferElementDescriptor>
  75. GetUniformDescriptorsForConstant(opt::IRContext* ir_context,
  76. uint32_t constant_id) const;
  77. // See method in FactManager which delegates to this method.
  78. uint32_t GetConstantFromUniformDescriptor(
  79. opt::IRContext* context,
  80. const protobufs::UniformBufferElementDescriptor& uniform_descriptor)
  81. const;
  82. // See method in FactManager which delegates to this method.
  83. std::vector<uint32_t> GetTypesForWhichUniformValuesAreKnown() const;
  84. // Returns true if and only if the words associated with
  85. // |constant_instruction| exactly match the words for the constant associated
  86. // with |constant_uniform_fact|.
  87. bool DataMatches(
  88. const opt::Instruction& constant_instruction,
  89. const protobufs::FactConstantUniform& constant_uniform_fact) const;
  90. // Yields the constant words associated with |constant_uniform_fact|.
  91. std::vector<uint32_t> GetConstantWords(
  92. const protobufs::FactConstantUniform& constant_uniform_fact) const;
  93. // Yields the id of a constant of type |type_id| whose data matches the
  94. // constant data in |constant_uniform_fact|, or 0 if no such constant is
  95. // declared.
  96. uint32_t GetConstantId(
  97. opt::IRContext* context,
  98. const protobufs::FactConstantUniform& constant_uniform_fact,
  99. uint32_t type_id) const;
  100. // Checks that the width of a floating-point constant is supported, and that
  101. // the constant is finite.
  102. bool FloatingPointValueIsSuitable(const protobufs::FactConstantUniform& fact,
  103. uint32_t width) const;
  104. std::vector<std::pair<protobufs::FactConstantUniform, uint32_t>>
  105. facts_and_type_ids;
  106. };
  107. uint32_t FactManager::ConstantUniformFacts::GetConstantId(
  108. opt::IRContext* context,
  109. const protobufs::FactConstantUniform& constant_uniform_fact,
  110. uint32_t type_id) const {
  111. auto type = context->get_type_mgr()->GetType(type_id);
  112. assert(type != nullptr && "Unknown type id.");
  113. auto constant = context->get_constant_mgr()->GetConstant(
  114. type, GetConstantWords(constant_uniform_fact));
  115. return context->get_constant_mgr()->FindDeclaredConstant(constant, type_id);
  116. }
  117. std::vector<uint32_t> FactManager::ConstantUniformFacts::GetConstantWords(
  118. const protobufs::FactConstantUniform& constant_uniform_fact) const {
  119. std::vector<uint32_t> result;
  120. for (auto constant_word : constant_uniform_fact.constant_word()) {
  121. result.push_back(constant_word);
  122. }
  123. return result;
  124. }
  125. bool FactManager::ConstantUniformFacts::DataMatches(
  126. const opt::Instruction& constant_instruction,
  127. const protobufs::FactConstantUniform& constant_uniform_fact) const {
  128. assert(constant_instruction.opcode() == SpvOpConstant);
  129. std::vector<uint32_t> data_in_constant;
  130. for (uint32_t i = 0; i < constant_instruction.NumInOperands(); i++) {
  131. data_in_constant.push_back(constant_instruction.GetSingleWordInOperand(i));
  132. }
  133. return data_in_constant == GetConstantWords(constant_uniform_fact);
  134. }
  135. std::vector<uint32_t>
  136. FactManager::ConstantUniformFacts::GetConstantsAvailableFromUniformsForType(
  137. opt::IRContext* ir_context, uint32_t type_id) const {
  138. std::vector<uint32_t> result;
  139. std::set<uint32_t> already_seen;
  140. for (auto& fact_and_type_id : facts_and_type_ids) {
  141. if (fact_and_type_id.second != type_id) {
  142. continue;
  143. }
  144. if (auto constant_id =
  145. GetConstantId(ir_context, fact_and_type_id.first, type_id)) {
  146. if (already_seen.find(constant_id) == already_seen.end()) {
  147. result.push_back(constant_id);
  148. already_seen.insert(constant_id);
  149. }
  150. }
  151. }
  152. return result;
  153. }
  154. const std::vector<protobufs::UniformBufferElementDescriptor>
  155. FactManager::ConstantUniformFacts::GetUniformDescriptorsForConstant(
  156. opt::IRContext* ir_context, uint32_t constant_id) const {
  157. std::vector<protobufs::UniformBufferElementDescriptor> result;
  158. auto constant_inst = ir_context->get_def_use_mgr()->GetDef(constant_id);
  159. assert(constant_inst->opcode() == SpvOpConstant &&
  160. "The given id must be that of a constant");
  161. auto type_id = constant_inst->type_id();
  162. for (auto& fact_and_type_id : facts_and_type_ids) {
  163. if (fact_and_type_id.second != type_id) {
  164. continue;
  165. }
  166. if (DataMatches(*constant_inst, fact_and_type_id.first)) {
  167. result.emplace_back(
  168. fact_and_type_id.first.uniform_buffer_element_descriptor());
  169. }
  170. }
  171. return result;
  172. }
  173. uint32_t FactManager::ConstantUniformFacts::GetConstantFromUniformDescriptor(
  174. opt::IRContext* context,
  175. const protobufs::UniformBufferElementDescriptor& uniform_descriptor) const {
  176. // Consider each fact.
  177. for (auto& fact_and_type : facts_and_type_ids) {
  178. // Check whether the uniform descriptor associated with the fact matches
  179. // |uniform_descriptor|.
  180. if (UniformBufferElementDescriptorEquals()(
  181. &uniform_descriptor,
  182. &fact_and_type.first.uniform_buffer_element_descriptor())) {
  183. return GetConstantId(context, fact_and_type.first, fact_and_type.second);
  184. }
  185. }
  186. // No fact associated with the given uniform descriptor was found.
  187. return 0;
  188. }
  189. std::vector<uint32_t>
  190. FactManager::ConstantUniformFacts::GetTypesForWhichUniformValuesAreKnown()
  191. const {
  192. std::vector<uint32_t> result;
  193. for (auto& fact_and_type : facts_and_type_ids) {
  194. if (std::find(result.begin(), result.end(), fact_and_type.second) ==
  195. result.end()) {
  196. result.push_back(fact_and_type.second);
  197. }
  198. }
  199. return result;
  200. }
  201. bool FactManager::ConstantUniformFacts::FloatingPointValueIsSuitable(
  202. const protobufs::FactConstantUniform& fact, uint32_t width) const {
  203. const uint32_t kFloatWidth = 32;
  204. const uint32_t kDoubleWidth = 64;
  205. if (width != kFloatWidth && width != kDoubleWidth) {
  206. // Only 32- and 64-bit floating-point types are handled.
  207. return false;
  208. }
  209. std::vector<uint32_t> words = GetConstantWords(fact);
  210. if (width == 32) {
  211. float value;
  212. memcpy(&value, words.data(), sizeof(float));
  213. if (!std::isfinite(value)) {
  214. return false;
  215. }
  216. } else {
  217. double value;
  218. memcpy(&value, words.data(), sizeof(double));
  219. if (!std::isfinite(value)) {
  220. return false;
  221. }
  222. }
  223. return true;
  224. }
  225. bool FactManager::ConstantUniformFacts::AddFact(
  226. const protobufs::FactConstantUniform& fact, opt::IRContext* context) {
  227. // Try to find a unique instruction that declares a variable such that the
  228. // variable is decorated with the descriptor set and binding associated with
  229. // the constant uniform fact.
  230. opt::Instruction* uniform_variable = FindUniformVariable(
  231. fact.uniform_buffer_element_descriptor(), context, true);
  232. if (!uniform_variable) {
  233. return false;
  234. }
  235. assert(SpvOpVariable == uniform_variable->opcode());
  236. assert(SpvStorageClassUniform == uniform_variable->GetSingleWordInOperand(0));
  237. auto should_be_uniform_pointer_type =
  238. context->get_type_mgr()->GetType(uniform_variable->type_id());
  239. if (!should_be_uniform_pointer_type->AsPointer()) {
  240. return false;
  241. }
  242. if (should_be_uniform_pointer_type->AsPointer()->storage_class() !=
  243. SpvStorageClassUniform) {
  244. return false;
  245. }
  246. auto should_be_uniform_pointer_instruction =
  247. context->get_def_use_mgr()->GetDef(uniform_variable->type_id());
  248. auto composite_type =
  249. should_be_uniform_pointer_instruction->GetSingleWordInOperand(1);
  250. auto final_element_type_id = fuzzerutil::WalkCompositeTypeIndices(
  251. context, composite_type,
  252. fact.uniform_buffer_element_descriptor().index());
  253. if (!final_element_type_id) {
  254. return false;
  255. }
  256. auto final_element_type =
  257. context->get_type_mgr()->GetType(final_element_type_id);
  258. assert(final_element_type &&
  259. "There should be a type corresponding to this id.");
  260. if (!(final_element_type->AsFloat() || final_element_type->AsInteger())) {
  261. return false;
  262. }
  263. auto width = final_element_type->AsFloat()
  264. ? final_element_type->AsFloat()->width()
  265. : final_element_type->AsInteger()->width();
  266. if (final_element_type->AsFloat() &&
  267. !FloatingPointValueIsSuitable(fact, width)) {
  268. return false;
  269. }
  270. auto required_words = (width + 32 - 1) / 32;
  271. if (static_cast<uint32_t>(fact.constant_word().size()) != required_words) {
  272. return false;
  273. }
  274. facts_and_type_ids.emplace_back(
  275. std::pair<protobufs::FactConstantUniform, uint32_t>(
  276. fact, final_element_type_id));
  277. return true;
  278. }
  279. // End of uniform constant facts
  280. //==============================
  281. //==============================
  282. // Data synonym facts
  283. // The purpose of this struct is to group the fields and data used to represent
  284. // facts about data synonyms.
  285. struct FactManager::DataSynonymFacts {
  286. // See method in FactManager which delegates to this method.
  287. void AddFact(const protobufs::FactDataSynonym& fact);
  288. // See method in FactManager which delegates to this method.
  289. bool IsSynonymous(const protobufs::DataDescriptor& data_descriptor1,
  290. const protobufs::DataDescriptor& data_descriptor2) const;
  291. EquivalenceRelation<protobufs::DataDescriptor, DataDescriptorHash,
  292. DataDescriptorEquals>
  293. synonymous;
  294. };
  295. void FactManager::DataSynonymFacts::AddFact(
  296. const protobufs::FactDataSynonym& fact) {
  297. synonymous.MakeEquivalent(fact.data1(), fact.data2());
  298. }
  299. bool FactManager::DataSynonymFacts::IsSynonymous(
  300. const protobufs::DataDescriptor& data_descriptor1,
  301. const protobufs::DataDescriptor& data_descriptor2) const {
  302. return synonymous.Exists(data_descriptor1) &&
  303. synonymous.Exists(data_descriptor2) &&
  304. synonymous.IsEquivalent(data_descriptor1, data_descriptor2);
  305. }
  306. // End of data synonym facts
  307. //==============================
  308. FactManager::FactManager()
  309. : uniform_constant_facts_(MakeUnique<ConstantUniformFacts>()),
  310. data_synonym_facts_(MakeUnique<DataSynonymFacts>()) {}
  311. FactManager::~FactManager() = default;
  312. void FactManager::AddFacts(const MessageConsumer& message_consumer,
  313. const protobufs::FactSequence& initial_facts,
  314. opt::IRContext* context) {
  315. for (auto& fact : initial_facts.fact()) {
  316. if (!AddFact(fact, context)) {
  317. message_consumer(
  318. SPV_MSG_WARNING, nullptr, {},
  319. ("Invalid fact " + ToString(fact) + " ignored.").c_str());
  320. }
  321. }
  322. }
  323. bool FactManager::AddFact(const fuzz::protobufs::Fact& fact,
  324. opt::IRContext* context) {
  325. switch (fact.fact_case()) {
  326. case protobufs::Fact::kConstantUniformFact:
  327. return uniform_constant_facts_->AddFact(fact.constant_uniform_fact(),
  328. context);
  329. case protobufs::Fact::kDataSynonymFact:
  330. data_synonym_facts_->AddFact(fact.data_synonym_fact());
  331. return true;
  332. default:
  333. assert(false && "Unknown fact type.");
  334. return false;
  335. }
  336. }
  337. void FactManager::AddFactDataSynonym(const protobufs::DataDescriptor& data1,
  338. const protobufs::DataDescriptor& data2,
  339. opt::IRContext* /*unused*/) {
  340. protobufs::FactDataSynonym fact;
  341. *fact.mutable_data1() = data1;
  342. *fact.mutable_data2() = data2;
  343. data_synonym_facts_->AddFact(fact);
  344. }
  345. std::vector<uint32_t> FactManager::GetConstantsAvailableFromUniformsForType(
  346. opt::IRContext* ir_context, uint32_t type_id) const {
  347. return uniform_constant_facts_->GetConstantsAvailableFromUniformsForType(
  348. ir_context, type_id);
  349. }
  350. const std::vector<protobufs::UniformBufferElementDescriptor>
  351. FactManager::GetUniformDescriptorsForConstant(opt::IRContext* ir_context,
  352. uint32_t constant_id) const {
  353. return uniform_constant_facts_->GetUniformDescriptorsForConstant(ir_context,
  354. constant_id);
  355. }
  356. uint32_t FactManager::GetConstantFromUniformDescriptor(
  357. opt::IRContext* context,
  358. const protobufs::UniformBufferElementDescriptor& uniform_descriptor) const {
  359. return uniform_constant_facts_->GetConstantFromUniformDescriptor(
  360. context, uniform_descriptor);
  361. }
  362. std::vector<uint32_t> FactManager::GetTypesForWhichUniformValuesAreKnown()
  363. const {
  364. return uniform_constant_facts_->GetTypesForWhichUniformValuesAreKnown();
  365. }
  366. const std::vector<std::pair<protobufs::FactConstantUniform, uint32_t>>&
  367. FactManager::GetConstantUniformFactsAndTypes() const {
  368. return uniform_constant_facts_->facts_and_type_ids;
  369. }
  370. std::vector<uint32_t> FactManager::GetIdsForWhichSynonymsAreKnown() const {
  371. std::vector<uint32_t> result;
  372. for (auto& data_descriptor :
  373. data_synonym_facts_->synonymous.GetAllKnownValues()) {
  374. if (data_descriptor->index().empty()) {
  375. result.push_back(data_descriptor->object());
  376. }
  377. }
  378. return result;
  379. }
  380. std::vector<const protobufs::DataDescriptor*> FactManager::GetSynonymsForId(
  381. uint32_t id) const {
  382. return data_synonym_facts_->synonymous.GetEquivalenceClass(
  383. MakeDataDescriptor(id, {}));
  384. }
  385. bool FactManager::IsSynonymous(
  386. const protobufs::DataDescriptor& data_descriptor1,
  387. const protobufs::DataDescriptor& data_descriptor2) const {
  388. return data_synonym_facts_->IsSynonymous(data_descriptor1, data_descriptor2);
  389. };
  390. } // namespace fuzz
  391. } // namespace spvtools