fact_manager.cpp 56 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453
  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 <sstream>
  16. #include <unordered_map>
  17. #include <unordered_set>
  18. #include "source/fuzz/equivalence_relation.h"
  19. #include "source/fuzz/fuzzer_util.h"
  20. #include "source/fuzz/uniform_buffer_element_descriptor.h"
  21. #include "source/opt/ir_context.h"
  22. namespace spvtools {
  23. namespace fuzz {
  24. namespace {
  25. std::string ToString(const protobufs::FactConstantUniform& fact) {
  26. std::stringstream stream;
  27. stream << "(" << fact.uniform_buffer_element_descriptor().descriptor_set()
  28. << ", " << fact.uniform_buffer_element_descriptor().binding() << ")[";
  29. bool first = true;
  30. for (auto index : fact.uniform_buffer_element_descriptor().index()) {
  31. if (first) {
  32. first = false;
  33. } else {
  34. stream << ", ";
  35. }
  36. stream << index;
  37. }
  38. stream << "] == [";
  39. first = true;
  40. for (auto constant_word : fact.constant_word()) {
  41. if (first) {
  42. first = false;
  43. } else {
  44. stream << ", ";
  45. }
  46. stream << constant_word;
  47. }
  48. stream << "]";
  49. return stream.str();
  50. }
  51. std::string ToString(const protobufs::FactDataSynonym& fact) {
  52. std::stringstream stream;
  53. stream << fact.data1() << " = " << fact.data2();
  54. return stream.str();
  55. }
  56. std::string ToString(const protobufs::FactIdEquation& fact) {
  57. std::stringstream stream;
  58. stream << fact.lhs_id();
  59. stream << " " << static_cast<SpvOp>(fact.opcode());
  60. for (auto rhs_id : fact.rhs_id()) {
  61. stream << " " << rhs_id;
  62. }
  63. return stream.str();
  64. }
  65. std::string ToString(const protobufs::Fact& fact) {
  66. switch (fact.fact_case()) {
  67. case protobufs::Fact::kConstantUniformFact:
  68. return ToString(fact.constant_uniform_fact());
  69. case protobufs::Fact::kDataSynonymFact:
  70. return ToString(fact.data_synonym_fact());
  71. case protobufs::Fact::kIdEquationFact:
  72. return ToString(fact.id_equation_fact());
  73. default:
  74. assert(false && "Stringification not supported for this fact.");
  75. return "";
  76. }
  77. }
  78. } // namespace
  79. //=======================
  80. // Constant uniform facts
  81. // The purpose of this class is to group the fields and data used to represent
  82. // facts about uniform constants.
  83. class FactManager::ConstantUniformFacts {
  84. public:
  85. // See method in FactManager which delegates to this method.
  86. bool AddFact(const protobufs::FactConstantUniform& fact,
  87. opt::IRContext* context);
  88. // See method in FactManager which delegates to this method.
  89. std::vector<uint32_t> GetConstantsAvailableFromUniformsForType(
  90. opt::IRContext* ir_context, uint32_t type_id) const;
  91. // See method in FactManager which delegates to this method.
  92. const std::vector<protobufs::UniformBufferElementDescriptor>
  93. GetUniformDescriptorsForConstant(opt::IRContext* ir_context,
  94. uint32_t constant_id) const;
  95. // See method in FactManager which delegates to this method.
  96. uint32_t GetConstantFromUniformDescriptor(
  97. opt::IRContext* context,
  98. const protobufs::UniformBufferElementDescriptor& uniform_descriptor)
  99. const;
  100. // See method in FactManager which delegates to this method.
  101. std::vector<uint32_t> GetTypesForWhichUniformValuesAreKnown() const;
  102. // See method in FactManager which delegates to this method.
  103. const std::vector<std::pair<protobufs::FactConstantUniform, uint32_t>>&
  104. GetConstantUniformFactsAndTypes() const;
  105. private:
  106. // Returns true if and only if the words associated with
  107. // |constant_instruction| exactly match the words for the constant associated
  108. // with |constant_uniform_fact|.
  109. bool DataMatches(
  110. const opt::Instruction& constant_instruction,
  111. const protobufs::FactConstantUniform& constant_uniform_fact) const;
  112. // Yields the constant words associated with |constant_uniform_fact|.
  113. std::vector<uint32_t> GetConstantWords(
  114. const protobufs::FactConstantUniform& constant_uniform_fact) const;
  115. // Yields the id of a constant of type |type_id| whose data matches the
  116. // constant data in |constant_uniform_fact|, or 0 if no such constant is
  117. // declared.
  118. uint32_t GetConstantId(
  119. opt::IRContext* context,
  120. const protobufs::FactConstantUniform& constant_uniform_fact,
  121. uint32_t type_id) const;
  122. // Checks that the width of a floating-point constant is supported, and that
  123. // the constant is finite.
  124. bool FloatingPointValueIsSuitable(const protobufs::FactConstantUniform& fact,
  125. uint32_t width) const;
  126. std::vector<std::pair<protobufs::FactConstantUniform, uint32_t>>
  127. facts_and_type_ids_;
  128. };
  129. uint32_t FactManager::ConstantUniformFacts::GetConstantId(
  130. opt::IRContext* context,
  131. const protobufs::FactConstantUniform& constant_uniform_fact,
  132. uint32_t type_id) const {
  133. auto type = context->get_type_mgr()->GetType(type_id);
  134. assert(type != nullptr && "Unknown type id.");
  135. const opt::analysis::Constant* known_constant;
  136. if (type->AsInteger()) {
  137. opt::analysis::IntConstant candidate_constant(
  138. type->AsInteger(), GetConstantWords(constant_uniform_fact));
  139. known_constant =
  140. context->get_constant_mgr()->FindConstant(&candidate_constant);
  141. } else {
  142. assert(
  143. type->AsFloat() &&
  144. "Uniform constant facts are only supported for int and float types.");
  145. opt::analysis::FloatConstant candidate_constant(
  146. type->AsFloat(), GetConstantWords(constant_uniform_fact));
  147. known_constant =
  148. context->get_constant_mgr()->FindConstant(&candidate_constant);
  149. }
  150. if (!known_constant) {
  151. return 0;
  152. }
  153. return context->get_constant_mgr()->FindDeclaredConstant(known_constant,
  154. type_id);
  155. }
  156. std::vector<uint32_t> FactManager::ConstantUniformFacts::GetConstantWords(
  157. const protobufs::FactConstantUniform& constant_uniform_fact) const {
  158. std::vector<uint32_t> result;
  159. for (auto constant_word : constant_uniform_fact.constant_word()) {
  160. result.push_back(constant_word);
  161. }
  162. return result;
  163. }
  164. bool FactManager::ConstantUniformFacts::DataMatches(
  165. const opt::Instruction& constant_instruction,
  166. const protobufs::FactConstantUniform& constant_uniform_fact) const {
  167. assert(constant_instruction.opcode() == SpvOpConstant);
  168. std::vector<uint32_t> data_in_constant;
  169. for (uint32_t i = 0; i < constant_instruction.NumInOperands(); i++) {
  170. data_in_constant.push_back(constant_instruction.GetSingleWordInOperand(i));
  171. }
  172. return data_in_constant == GetConstantWords(constant_uniform_fact);
  173. }
  174. std::vector<uint32_t>
  175. FactManager::ConstantUniformFacts::GetConstantsAvailableFromUniformsForType(
  176. opt::IRContext* ir_context, uint32_t type_id) const {
  177. std::vector<uint32_t> result;
  178. std::set<uint32_t> already_seen;
  179. for (auto& fact_and_type_id : facts_and_type_ids_) {
  180. if (fact_and_type_id.second != type_id) {
  181. continue;
  182. }
  183. if (auto constant_id =
  184. GetConstantId(ir_context, fact_and_type_id.first, type_id)) {
  185. if (already_seen.find(constant_id) == already_seen.end()) {
  186. result.push_back(constant_id);
  187. already_seen.insert(constant_id);
  188. }
  189. }
  190. }
  191. return result;
  192. }
  193. const std::vector<protobufs::UniformBufferElementDescriptor>
  194. FactManager::ConstantUniformFacts::GetUniformDescriptorsForConstant(
  195. opt::IRContext* ir_context, uint32_t constant_id) const {
  196. std::vector<protobufs::UniformBufferElementDescriptor> result;
  197. auto constant_inst = ir_context->get_def_use_mgr()->GetDef(constant_id);
  198. assert(constant_inst->opcode() == SpvOpConstant &&
  199. "The given id must be that of a constant");
  200. auto type_id = constant_inst->type_id();
  201. for (auto& fact_and_type_id : facts_and_type_ids_) {
  202. if (fact_and_type_id.second != type_id) {
  203. continue;
  204. }
  205. if (DataMatches(*constant_inst, fact_and_type_id.first)) {
  206. result.emplace_back(
  207. fact_and_type_id.first.uniform_buffer_element_descriptor());
  208. }
  209. }
  210. return result;
  211. }
  212. uint32_t FactManager::ConstantUniformFacts::GetConstantFromUniformDescriptor(
  213. opt::IRContext* context,
  214. const protobufs::UniformBufferElementDescriptor& uniform_descriptor) const {
  215. // Consider each fact.
  216. for (auto& fact_and_type : facts_and_type_ids_) {
  217. // Check whether the uniform descriptor associated with the fact matches
  218. // |uniform_descriptor|.
  219. if (UniformBufferElementDescriptorEquals()(
  220. &uniform_descriptor,
  221. &fact_and_type.first.uniform_buffer_element_descriptor())) {
  222. return GetConstantId(context, fact_and_type.first, fact_and_type.second);
  223. }
  224. }
  225. // No fact associated with the given uniform descriptor was found.
  226. return 0;
  227. }
  228. std::vector<uint32_t>
  229. FactManager::ConstantUniformFacts::GetTypesForWhichUniformValuesAreKnown()
  230. const {
  231. std::vector<uint32_t> result;
  232. for (auto& fact_and_type : facts_and_type_ids_) {
  233. if (std::find(result.begin(), result.end(), fact_and_type.second) ==
  234. result.end()) {
  235. result.push_back(fact_and_type.second);
  236. }
  237. }
  238. return result;
  239. }
  240. bool FactManager::ConstantUniformFacts::FloatingPointValueIsSuitable(
  241. const protobufs::FactConstantUniform& fact, uint32_t width) const {
  242. const uint32_t kFloatWidth = 32;
  243. const uint32_t kDoubleWidth = 64;
  244. if (width != kFloatWidth && width != kDoubleWidth) {
  245. // Only 32- and 64-bit floating-point types are handled.
  246. return false;
  247. }
  248. std::vector<uint32_t> words = GetConstantWords(fact);
  249. if (width == 32) {
  250. float value;
  251. memcpy(&value, words.data(), sizeof(float));
  252. if (!std::isfinite(value)) {
  253. return false;
  254. }
  255. } else {
  256. double value;
  257. memcpy(&value, words.data(), sizeof(double));
  258. if (!std::isfinite(value)) {
  259. return false;
  260. }
  261. }
  262. return true;
  263. }
  264. bool FactManager::ConstantUniformFacts::AddFact(
  265. const protobufs::FactConstantUniform& fact, opt::IRContext* context) {
  266. // Try to find a unique instruction that declares a variable such that the
  267. // variable is decorated with the descriptor set and binding associated with
  268. // the constant uniform fact.
  269. opt::Instruction* uniform_variable = FindUniformVariable(
  270. fact.uniform_buffer_element_descriptor(), context, true);
  271. if (!uniform_variable) {
  272. return false;
  273. }
  274. assert(SpvOpVariable == uniform_variable->opcode());
  275. assert(SpvStorageClassUniform == uniform_variable->GetSingleWordInOperand(0));
  276. auto should_be_uniform_pointer_type =
  277. context->get_type_mgr()->GetType(uniform_variable->type_id());
  278. if (!should_be_uniform_pointer_type->AsPointer()) {
  279. return false;
  280. }
  281. if (should_be_uniform_pointer_type->AsPointer()->storage_class() !=
  282. SpvStorageClassUniform) {
  283. return false;
  284. }
  285. auto should_be_uniform_pointer_instruction =
  286. context->get_def_use_mgr()->GetDef(uniform_variable->type_id());
  287. auto composite_type =
  288. should_be_uniform_pointer_instruction->GetSingleWordInOperand(1);
  289. auto final_element_type_id = fuzzerutil::WalkCompositeTypeIndices(
  290. context, composite_type,
  291. fact.uniform_buffer_element_descriptor().index());
  292. if (!final_element_type_id) {
  293. return false;
  294. }
  295. auto final_element_type =
  296. context->get_type_mgr()->GetType(final_element_type_id);
  297. assert(final_element_type &&
  298. "There should be a type corresponding to this id.");
  299. if (!(final_element_type->AsFloat() || final_element_type->AsInteger())) {
  300. return false;
  301. }
  302. auto width = final_element_type->AsFloat()
  303. ? final_element_type->AsFloat()->width()
  304. : final_element_type->AsInteger()->width();
  305. if (final_element_type->AsFloat() &&
  306. !FloatingPointValueIsSuitable(fact, width)) {
  307. return false;
  308. }
  309. auto required_words = (width + 32 - 1) / 32;
  310. if (static_cast<uint32_t>(fact.constant_word().size()) != required_words) {
  311. return false;
  312. }
  313. facts_and_type_ids_.emplace_back(
  314. std::pair<protobufs::FactConstantUniform, uint32_t>(
  315. fact, final_element_type_id));
  316. return true;
  317. }
  318. const std::vector<std::pair<protobufs::FactConstantUniform, uint32_t>>&
  319. FactManager::ConstantUniformFacts::GetConstantUniformFactsAndTypes() const {
  320. return facts_and_type_ids_;
  321. }
  322. // End of uniform constant facts
  323. //==============================
  324. //==============================
  325. // Data synonym and id equation facts
  326. // This helper struct represents the right hand side of an equation as an
  327. // operator applied to a number of data descriptor operands.
  328. struct Operation {
  329. SpvOp opcode;
  330. std::vector<const protobufs::DataDescriptor*> operands;
  331. };
  332. // Hashing for operations, to allow deterministic unordered sets.
  333. struct OperationHash {
  334. size_t operator()(const Operation& operation) const {
  335. std::u32string hash;
  336. hash.push_back(operation.opcode);
  337. for (auto operand : operation.operands) {
  338. hash.push_back(static_cast<uint32_t>(DataDescriptorHash()(operand)));
  339. }
  340. return std::hash<std::u32string>()(hash);
  341. }
  342. };
  343. // Equality for operations, to allow deterministic unordered sets.
  344. struct OperationEquals {
  345. bool operator()(const Operation& first, const Operation& second) const {
  346. // Equal operations require...
  347. //
  348. // Equal opcodes.
  349. if (first.opcode != second.opcode) {
  350. return false;
  351. }
  352. // Matching operand counds.
  353. if (first.operands.size() != second.operands.size()) {
  354. return false;
  355. }
  356. // Equal operands.
  357. for (uint32_t i = 0; i < first.operands.size(); i++) {
  358. if (!DataDescriptorEquals()(first.operands[i], second.operands[i])) {
  359. return false;
  360. }
  361. }
  362. return true;
  363. }
  364. };
  365. // A helper, for debugging, to represent an operation as a string.
  366. std::string ToString(const Operation& operation) {
  367. std::stringstream stream;
  368. stream << operation.opcode;
  369. for (auto operand : operation.operands) {
  370. stream << " " << *operand;
  371. }
  372. return stream.str();
  373. }
  374. // The purpose of this class is to group the fields and data used to represent
  375. // facts about data synonyms and id equations.
  376. class FactManager::DataSynonymAndIdEquationFacts {
  377. public:
  378. // See method in FactManager which delegates to this method.
  379. void AddFact(const protobufs::FactDataSynonym& fact, opt::IRContext* context);
  380. // See method in FactManager which delegates to this method.
  381. void AddFact(const protobufs::FactIdEquation& fact, opt::IRContext* context);
  382. // See method in FactManager which delegates to this method.
  383. std::vector<const protobufs::DataDescriptor*> GetSynonymsForDataDescriptor(
  384. const protobufs::DataDescriptor& data_descriptor) const;
  385. // See method in FactManager which delegates to this method.
  386. std::vector<uint32_t> GetIdsForWhichSynonymsAreKnown() const;
  387. // See method in FactManager which delegates to this method.
  388. bool IsSynonymous(const protobufs::DataDescriptor& data_descriptor1,
  389. const protobufs::DataDescriptor& data_descriptor2) const;
  390. // See method in FactManager which delegates to this method.
  391. void ComputeClosureOfFacts(opt::IRContext* context,
  392. uint32_t maximum_equivalence_class_size);
  393. private:
  394. using OperationSet =
  395. std::unordered_set<Operation, OperationHash, OperationEquals>;
  396. // Adds the synonym |dd1| = |dd2| to the set of managed facts, and recurses
  397. // into sub-components of the data descriptors, if they are composites, to
  398. // record that their components are pairwise-synonymous.
  399. void AddDataSynonymFactRecursive(const protobufs::DataDescriptor& dd1,
  400. const protobufs::DataDescriptor& dd2,
  401. opt::IRContext* context);
  402. // Records the fact that |dd1| and |dd2| are equivalent, and merges the sets
  403. // of equations that are known about them.
  404. void MakeEquivalent(const protobufs::DataDescriptor& dd1,
  405. const protobufs::DataDescriptor& dd2);
  406. // Returns true if and only if |dd1| and |dd2| are valid data descriptors
  407. // whose associated data have the same type (modulo integer signedness).
  408. bool DataDescriptorsAreWellFormedAndComparable(
  409. opt::IRContext* context, const protobufs::DataDescriptor& dd1,
  410. const protobufs::DataDescriptor& dd2) const;
  411. OperationSet GetEquations(const protobufs::DataDescriptor* lhs) const;
  412. // Requires that |lhs_dd| and every element of |rhs_dds| is present in the
  413. // |synonymous_| equivalence relation, but is not necessarily its own
  414. // representative. Records the fact that the equation
  415. // "|lhs_dd| |opcode| |rhs_dds_non_canonical|" holds, and adds any
  416. // corollaries, in the form of data synonym or equation facts, that follow
  417. // from this and other known facts.
  418. void AddEquationFactRecursive(
  419. const protobufs::DataDescriptor& lhs_dd, SpvOp opcode,
  420. const std::vector<const protobufs::DataDescriptor*>& rhs_dds,
  421. opt::IRContext* context);
  422. // The data descriptors that are known to be synonymous with one another are
  423. // captured by this equivalence relation.
  424. EquivalenceRelation<protobufs::DataDescriptor, DataDescriptorHash,
  425. DataDescriptorEquals>
  426. synonymous_;
  427. // When a new synonym fact is added, it may be possible to deduce further
  428. // synonym facts by computing a closure of all known facts. However, this is
  429. // an expensive operation, so it should be performed sparingly and only there
  430. // is some chance of new facts being deduced. This boolean tracks whether a
  431. // closure computation is required - i.e., whether a new fact has been added
  432. // since the last time such a computation was performed.
  433. bool closure_computation_required_ = false;
  434. // Represents a set of equations on data descriptors as a map indexed by
  435. // left-hand-side, mapping a left-hand-side to a set of operations, each of
  436. // which (together with the left-hand-side) defines an equation.
  437. //
  438. // All data descriptors occurring in equations are required to be present in
  439. // the |synonymous_| equivalence relation, and to be their own representatives
  440. // in that relation.
  441. std::unordered_map<const protobufs::DataDescriptor*, OperationSet>
  442. id_equations_;
  443. };
  444. void FactManager::DataSynonymAndIdEquationFacts::AddFact(
  445. const protobufs::FactDataSynonym& fact, opt::IRContext* context) {
  446. // Add the fact, including all facts relating sub-components of the data
  447. // descriptors that are involved.
  448. AddDataSynonymFactRecursive(fact.data1(), fact.data2(), context);
  449. }
  450. void FactManager::DataSynonymAndIdEquationFacts::AddFact(
  451. const protobufs::FactIdEquation& fact, opt::IRContext* context) {
  452. protobufs::DataDescriptor lhs_dd = MakeDataDescriptor(fact.lhs_id(), {});
  453. // Register the LHS in the equivalence relation if needed.
  454. if (!synonymous_.Exists(lhs_dd)) {
  455. synonymous_.Register(lhs_dd);
  456. }
  457. // Get equivalence class representatives for all ids used on the RHS of the
  458. // equation.
  459. std::vector<const protobufs::DataDescriptor*> rhs_dd_ptrs;
  460. for (auto rhs_id : fact.rhs_id()) {
  461. // Register a data descriptor based on this id in the equivalence relation
  462. // if needed, and then record the equivalence class representative.
  463. protobufs::DataDescriptor rhs_dd = MakeDataDescriptor(rhs_id, {});
  464. if (!synonymous_.Exists(rhs_dd)) {
  465. synonymous_.Register(rhs_dd);
  466. }
  467. rhs_dd_ptrs.push_back(synonymous_.Find(&rhs_dd));
  468. }
  469. // Now add the fact.
  470. AddEquationFactRecursive(lhs_dd, static_cast<SpvOp>(fact.opcode()),
  471. rhs_dd_ptrs, context);
  472. }
  473. FactManager::DataSynonymAndIdEquationFacts::OperationSet
  474. FactManager::DataSynonymAndIdEquationFacts::GetEquations(
  475. const protobufs::DataDescriptor* lhs) const {
  476. auto existing = id_equations_.find(lhs);
  477. if (existing == id_equations_.end()) {
  478. return OperationSet();
  479. }
  480. return existing->second;
  481. }
  482. void FactManager::DataSynonymAndIdEquationFacts::AddEquationFactRecursive(
  483. const protobufs::DataDescriptor& lhs_dd, SpvOp opcode,
  484. const std::vector<const protobufs::DataDescriptor*>& rhs_dds,
  485. opt::IRContext* context) {
  486. assert(synonymous_.Exists(lhs_dd) &&
  487. "The LHS must be known to the equivalence relation.");
  488. for (auto rhs_dd : rhs_dds) {
  489. // Keep release compilers happy.
  490. (void)(rhs_dd);
  491. assert(synonymous_.Exists(*rhs_dd) &&
  492. "The RHS operands must be known to the equivalence relation.");
  493. }
  494. auto lhs_dd_representative = synonymous_.Find(&lhs_dd);
  495. if (id_equations_.count(lhs_dd_representative) == 0) {
  496. // We have not seen an equation with this LHS before, so associate the LHS
  497. // with an initially empty set.
  498. id_equations_.insert({lhs_dd_representative, OperationSet()});
  499. }
  500. {
  501. auto existing_equations = id_equations_.find(lhs_dd_representative);
  502. assert(existing_equations != id_equations_.end() &&
  503. "A set of operations should be present, even if empty.");
  504. Operation new_operation = {opcode, rhs_dds};
  505. if (existing_equations->second.count(new_operation)) {
  506. // This equation is known, so there is nothing further to be done.
  507. return;
  508. }
  509. // Add the equation to the set of known equations.
  510. existing_equations->second.insert(new_operation);
  511. }
  512. // Now try to work out corollaries implied by the new equation and existing
  513. // facts.
  514. switch (opcode) {
  515. case SpvOpIAdd: {
  516. // Equation form: "a = b + c"
  517. for (auto equation : GetEquations(rhs_dds[0])) {
  518. if (equation.opcode == SpvOpISub) {
  519. // Equation form: "a = (d - e) + c"
  520. if (synonymous_.IsEquivalent(*equation.operands[1], *rhs_dds[1])) {
  521. // Equation form: "a = (d - c) + c"
  522. // We can thus infer "a = d"
  523. AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0], context);
  524. }
  525. if (synonymous_.IsEquivalent(*equation.operands[0], *rhs_dds[1])) {
  526. // Equation form: "a = (c - e) + c"
  527. // We can thus infer "a = -e"
  528. AddEquationFactRecursive(lhs_dd, SpvOpSNegate,
  529. {equation.operands[1]}, context);
  530. }
  531. }
  532. }
  533. for (auto equation : GetEquations(rhs_dds[1])) {
  534. if (equation.opcode == SpvOpISub) {
  535. // Equation form: "a = b + (d - e)"
  536. if (synonymous_.IsEquivalent(*equation.operands[1], *rhs_dds[0])) {
  537. // Equation form: "a = b + (d - b)"
  538. // We can thus infer "a = d"
  539. AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0], context);
  540. }
  541. }
  542. }
  543. break;
  544. }
  545. case SpvOpISub: {
  546. // Equation form: "a = b - c"
  547. for (auto equation : GetEquations(rhs_dds[0])) {
  548. if (equation.opcode == SpvOpIAdd) {
  549. // Equation form: "a = (d + e) - c"
  550. if (synonymous_.IsEquivalent(*equation.operands[0], *rhs_dds[1])) {
  551. // Equation form: "a = (c + e) - c"
  552. // We can thus infer "a = e"
  553. AddDataSynonymFactRecursive(lhs_dd, *equation.operands[1], context);
  554. }
  555. if (synonymous_.IsEquivalent(*equation.operands[1], *rhs_dds[1])) {
  556. // Equation form: "a = (d + c) - c"
  557. // We can thus infer "a = d"
  558. AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0], context);
  559. }
  560. }
  561. if (equation.opcode == SpvOpISub) {
  562. // Equation form: "a = (d - e) - c"
  563. if (synonymous_.IsEquivalent(*equation.operands[0], *rhs_dds[1])) {
  564. // Equation form: "a = (c - e) - c"
  565. // We can thus infer "a = -e"
  566. AddEquationFactRecursive(lhs_dd, SpvOpSNegate,
  567. {equation.operands[1]}, context);
  568. }
  569. }
  570. }
  571. for (auto equation : GetEquations(rhs_dds[1])) {
  572. if (equation.opcode == SpvOpIAdd) {
  573. // Equation form: "a = b - (d + e)"
  574. if (synonymous_.IsEquivalent(*equation.operands[0], *rhs_dds[0])) {
  575. // Equation form: "a = b - (b + e)"
  576. // We can thus infer "a = -e"
  577. AddEquationFactRecursive(lhs_dd, SpvOpSNegate,
  578. {equation.operands[1]}, context);
  579. }
  580. if (synonymous_.IsEquivalent(*equation.operands[1], *rhs_dds[0])) {
  581. // Equation form: "a = b - (d + b)"
  582. // We can thus infer "a = -d"
  583. AddEquationFactRecursive(lhs_dd, SpvOpSNegate,
  584. {equation.operands[0]}, context);
  585. }
  586. }
  587. if (equation.opcode == SpvOpISub) {
  588. // Equation form: "a = b - (d - e)"
  589. if (synonymous_.IsEquivalent(*equation.operands[0], *rhs_dds[0])) {
  590. // Equation form: "a = b - (b - e)"
  591. // We can thus infer "a = e"
  592. AddDataSynonymFactRecursive(lhs_dd, *equation.operands[1], context);
  593. }
  594. }
  595. }
  596. break;
  597. }
  598. case SpvOpLogicalNot:
  599. case SpvOpSNegate: {
  600. // Equation form: "a = !b" or "a = -b"
  601. for (auto equation : GetEquations(rhs_dds[0])) {
  602. if (equation.opcode == opcode) {
  603. // Equation form: "a = !!b" or "a = -(-b)"
  604. // We can thus infer "a = b"
  605. AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0], context);
  606. }
  607. }
  608. break;
  609. }
  610. default:
  611. break;
  612. }
  613. }
  614. void FactManager::DataSynonymAndIdEquationFacts::AddDataSynonymFactRecursive(
  615. const protobufs::DataDescriptor& dd1, const protobufs::DataDescriptor& dd2,
  616. opt::IRContext* context) {
  617. assert(DataDescriptorsAreWellFormedAndComparable(context, dd1, dd2));
  618. // Record that the data descriptors provided in the fact are equivalent.
  619. MakeEquivalent(dd1, dd2);
  620. // We now check whether this is a synonym about composite objects. If it is,
  621. // we can recursively add synonym facts about their associated sub-components.
  622. // Get the type of the object referred to by the first data descriptor in the
  623. // synonym fact.
  624. uint32_t type_id = fuzzerutil::WalkCompositeTypeIndices(
  625. context, context->get_def_use_mgr()->GetDef(dd1.object())->type_id(),
  626. dd1.index());
  627. auto type = context->get_type_mgr()->GetType(type_id);
  628. auto type_instruction = context->get_def_use_mgr()->GetDef(type_id);
  629. assert(type != nullptr &&
  630. "Invalid data synonym fact: one side has an unknown type.");
  631. // Check whether the type is composite, recording the number of elements
  632. // associated with the composite if so.
  633. uint32_t num_composite_elements;
  634. if (type->AsArray()) {
  635. num_composite_elements =
  636. fuzzerutil::GetArraySize(*type_instruction, context);
  637. } else if (type->AsMatrix()) {
  638. num_composite_elements = type->AsMatrix()->element_count();
  639. } else if (type->AsStruct()) {
  640. num_composite_elements =
  641. fuzzerutil::GetNumberOfStructMembers(*type_instruction);
  642. } else if (type->AsVector()) {
  643. num_composite_elements = type->AsVector()->element_count();
  644. } else {
  645. // The type is not a composite, so return.
  646. return;
  647. }
  648. // If the fact has the form:
  649. // obj_1[a_1, ..., a_m] == obj_2[b_1, ..., b_n]
  650. // then for each composite index i, we add a fact of the form:
  651. // obj_1[a_1, ..., a_m, i] == obj_2[b_1, ..., b_n, i]
  652. //
  653. // However, to avoid adding a large number of synonym facts e.g. in the case
  654. // of arrays, we bound the number of composite elements to which this is
  655. // applied. Nevertheless, we always add a synonym fact for the final
  656. // components, as this may be an interesting edge case.
  657. // The bound on the number of indices of the composite pair to note as being
  658. // synonymous.
  659. const uint32_t kCompositeElementBound = 10;
  660. for (uint32_t i = 0; i < num_composite_elements;) {
  661. std::vector<uint32_t> extended_indices1 =
  662. fuzzerutil::RepeatedFieldToVector(dd1.index());
  663. extended_indices1.push_back(i);
  664. std::vector<uint32_t> extended_indices2 =
  665. fuzzerutil::RepeatedFieldToVector(dd2.index());
  666. extended_indices2.push_back(i);
  667. AddDataSynonymFactRecursive(
  668. MakeDataDescriptor(dd1.object(), std::move(extended_indices1)),
  669. MakeDataDescriptor(dd2.object(), std::move(extended_indices2)),
  670. context);
  671. if (i < kCompositeElementBound - 1 || i == num_composite_elements - 1) {
  672. // We have not reached the bound yet, or have already skipped ahead to the
  673. // last element, so increment the loop counter as standard.
  674. i++;
  675. } else {
  676. // We have reached the bound, so skip ahead to the last element.
  677. assert(i == kCompositeElementBound - 1);
  678. i = num_composite_elements - 1;
  679. }
  680. }
  681. }
  682. void FactManager::DataSynonymAndIdEquationFacts::ComputeClosureOfFacts(
  683. opt::IRContext* context, uint32_t maximum_equivalence_class_size) {
  684. // Suppose that obj_1[a_1, ..., a_m] and obj_2[b_1, ..., b_n] are distinct
  685. // data descriptors that describe objects of the same composite type, and that
  686. // the composite type is comprised of k components.
  687. //
  688. // For example, if m is a mat4x4 and v a vec4, we might consider:
  689. // m[2]: describes the 2nd column of m, a vec4
  690. // v[]: describes all of v, a vec4
  691. //
  692. // Suppose that we know, for every 0 <= i < k, that the fact:
  693. // obj_1[a_1, ..., a_m, i] == obj_2[b_1, ..., b_n, i]
  694. // holds - i.e. that the children of the two data descriptors are synonymous.
  695. //
  696. // Then we can conclude that:
  697. // obj_1[a_1, ..., a_m] == obj_2[b_1, ..., b_n]
  698. // holds.
  699. //
  700. // For instance, if we have the facts:
  701. // m[2, 0] == v[0]
  702. // m[2, 1] == v[1]
  703. // m[2, 2] == v[2]
  704. // m[2, 3] == v[3]
  705. // then we can conclude that:
  706. // m[2] == v.
  707. //
  708. // This method repeatedly searches the equivalence relation of data
  709. // descriptors, deducing and adding such facts, until a pass over the
  710. // relation leads to no further facts being deduced.
  711. // The method relies on working with pairs of data descriptors, and in
  712. // particular being able to hash and compare such pairs.
  713. using DataDescriptorPair =
  714. std::pair<protobufs::DataDescriptor, protobufs::DataDescriptor>;
  715. struct DataDescriptorPairHash {
  716. std::size_t operator()(const DataDescriptorPair& pair) const {
  717. return DataDescriptorHash()(&pair.first) ^
  718. DataDescriptorHash()(&pair.second);
  719. }
  720. };
  721. struct DataDescriptorPairEquals {
  722. bool operator()(const DataDescriptorPair& first,
  723. const DataDescriptorPair& second) const {
  724. return (DataDescriptorEquals()(&first.first, &second.first) &&
  725. DataDescriptorEquals()(&first.second, &second.second)) ||
  726. (DataDescriptorEquals()(&first.first, &second.second) &&
  727. DataDescriptorEquals()(&first.second, &second.first));
  728. }
  729. };
  730. // This map records, for a given pair of composite data descriptors of the
  731. // same type, all the indices at which the data descriptors are known to be
  732. // synonymous. A pair is a key to this map only if we have observed that
  733. // the pair are synonymous at *some* index, but not at *all* indices.
  734. // Once we find that a pair of data descriptors are equivalent at all indices
  735. // we record the fact that they are synonymous and remove them from the map.
  736. //
  737. // Using the m and v example from above, initially the pair (m[2], v) would
  738. // not be a key to the map. If we find that m[2, 2] == v[2] holds, we would
  739. // add an entry:
  740. // (m[2], v) -> [false, false, true, false]
  741. // to record that they are synonymous at index 2. If we then find that
  742. // m[2, 0] == v[0] holds, we would update this entry to:
  743. // (m[2], v) -> [true, false, true, false]
  744. // If we then find that m[2, 3] == v[3] holds, we would update this entry to:
  745. // (m[2], v) -> [true, false, true, true]
  746. // Finally, if we then find that m[2, 1] == v[1] holds, which would make the
  747. // boolean vector true at every index, we would add the fact:
  748. // m[2] == v
  749. // to the equivalence relation and remove (m[2], v) from the map.
  750. std::unordered_map<DataDescriptorPair, std::vector<bool>,
  751. DataDescriptorPairHash, DataDescriptorPairEquals>
  752. candidate_composite_synonyms;
  753. // We keep looking for new facts until we perform a complete pass over the
  754. // equivalence relation without finding any new facts.
  755. while (closure_computation_required_) {
  756. // We have not found any new facts yet during this pass; we set this to
  757. // 'true' if we do find a new fact.
  758. closure_computation_required_ = false;
  759. // Consider each class in the equivalence relation.
  760. for (auto representative :
  761. synonymous_.GetEquivalenceClassRepresentatives()) {
  762. auto equivalence_class = synonymous_.GetEquivalenceClass(*representative);
  763. if (equivalence_class.size() > maximum_equivalence_class_size) {
  764. // This equivalence class is larger than the maximum size we are willing
  765. // to consider, so we skip it. This potentially leads to missed fact
  766. // deductions, but avoids excessive runtime for closure computation.
  767. continue;
  768. }
  769. // Consider every data descriptor in the equivalence class.
  770. for (auto dd1_it = equivalence_class.begin();
  771. dd1_it != equivalence_class.end(); ++dd1_it) {
  772. // If this data descriptor has no indices then it does not have the form
  773. // obj_1[a_1, ..., a_m, i], so move on.
  774. auto dd1 = *dd1_it;
  775. if (dd1->index_size() == 0) {
  776. continue;
  777. }
  778. // Consider every other data descriptor later in the equivalence class
  779. // (due to symmetry, there is no need to compare with previous data
  780. // descriptors).
  781. auto dd2_it = dd1_it;
  782. for (++dd2_it; dd2_it != equivalence_class.end(); ++dd2_it) {
  783. auto dd2 = *dd2_it;
  784. // If this data descriptor has no indices then it does not have the
  785. // form obj_2[b_1, ..., b_n, i], so move on.
  786. if (dd2->index_size() == 0) {
  787. continue;
  788. }
  789. // At this point we know that:
  790. // - |dd1| has the form obj_1[a_1, ..., a_m, i]
  791. // - |dd2| has the form obj_2[b_1, ..., b_n, j]
  792. assert(dd1->index_size() > 0 && dd2->index_size() > 0 &&
  793. "Control should not reach here if either data descriptor has "
  794. "no indices.");
  795. // We are only interested if i == j.
  796. if (dd1->index(dd1->index_size() - 1) !=
  797. dd2->index(dd2->index_size() - 1)) {
  798. continue;
  799. }
  800. const uint32_t common_final_index = dd1->index(dd1->index_size() - 1);
  801. // Make data descriptors |dd1_prefix| and |dd2_prefix| for
  802. // obj_1[a_1, ..., a_m]
  803. // and
  804. // obj_2[b_1, ..., b_n]
  805. // These are the two data descriptors we might be getting closer to
  806. // deducing as being synonymous, due to knowing that they are
  807. // synonymous when extended by a particular index.
  808. protobufs::DataDescriptor dd1_prefix;
  809. dd1_prefix.set_object(dd1->object());
  810. for (uint32_t i = 0; i < static_cast<uint32_t>(dd1->index_size() - 1);
  811. i++) {
  812. dd1_prefix.add_index(dd1->index(i));
  813. }
  814. protobufs::DataDescriptor dd2_prefix;
  815. dd2_prefix.set_object(dd2->object());
  816. for (uint32_t i = 0; i < static_cast<uint32_t>(dd2->index_size() - 1);
  817. i++) {
  818. dd2_prefix.add_index(dd2->index(i));
  819. }
  820. assert(!DataDescriptorEquals()(&dd1_prefix, &dd2_prefix) &&
  821. "By construction these prefixes should be different.");
  822. // If we already know that these prefixes are synonymous, move on.
  823. if (synonymous_.Exists(dd1_prefix) &&
  824. synonymous_.Exists(dd2_prefix) &&
  825. synonymous_.IsEquivalent(dd1_prefix, dd2_prefix)) {
  826. continue;
  827. }
  828. // Get the type of obj_1
  829. auto dd1_root_type_id =
  830. context->get_def_use_mgr()->GetDef(dd1->object())->type_id();
  831. // Use this type, together with a_1, ..., a_m, to get the type of
  832. // obj_1[a_1, ..., a_m].
  833. auto dd1_prefix_type = fuzzerutil::WalkCompositeTypeIndices(
  834. context, dd1_root_type_id, dd1_prefix.index());
  835. // Similarly, get the type of obj_2 and use it to get the type of
  836. // obj_2[b_1, ..., b_n].
  837. auto dd2_root_type_id =
  838. context->get_def_use_mgr()->GetDef(dd2->object())->type_id();
  839. auto dd2_prefix_type = fuzzerutil::WalkCompositeTypeIndices(
  840. context, dd2_root_type_id, dd2_prefix.index());
  841. // If the types of dd1_prefix and dd2_prefix are not the same, they
  842. // cannot be synonymous.
  843. if (dd1_prefix_type != dd2_prefix_type) {
  844. continue;
  845. }
  846. // At this point, we know we have synonymous data descriptors of the
  847. // form:
  848. // obj_1[a_1, ..., a_m, i]
  849. // obj_2[b_1, ..., b_n, i]
  850. // with the same last_index i, such that:
  851. // obj_1[a_1, ..., a_m]
  852. // and
  853. // obj_2[b_1, ..., b_n]
  854. // have the same type.
  855. // Work out how many components there are in the (common) commposite
  856. // type associated with obj_1[a_1, ..., a_m] and obj_2[b_1, ..., b_n].
  857. // This depends on whether the composite type is array, matrix, struct
  858. // or vector.
  859. uint32_t num_components_in_composite;
  860. auto composite_type =
  861. context->get_type_mgr()->GetType(dd1_prefix_type);
  862. auto composite_type_instruction =
  863. context->get_def_use_mgr()->GetDef(dd1_prefix_type);
  864. if (composite_type->AsArray()) {
  865. num_components_in_composite =
  866. fuzzerutil::GetArraySize(*composite_type_instruction, context);
  867. if (num_components_in_composite == 0) {
  868. // This indicates that the array has an unknown size, in which
  869. // case we cannot be sure we have matched all of its elements with
  870. // synonymous elements of another array.
  871. continue;
  872. }
  873. } else if (composite_type->AsMatrix()) {
  874. num_components_in_composite =
  875. composite_type->AsMatrix()->element_count();
  876. } else if (composite_type->AsStruct()) {
  877. num_components_in_composite = fuzzerutil::GetNumberOfStructMembers(
  878. *composite_type_instruction);
  879. } else {
  880. assert(composite_type->AsVector());
  881. num_components_in_composite =
  882. composite_type->AsVector()->element_count();
  883. }
  884. // We are one step closer to being able to say that |dd1_prefix| and
  885. // |dd2_prefix| are synonymous.
  886. DataDescriptorPair candidate_composite_synonym(dd1_prefix,
  887. dd2_prefix);
  888. // We look up what we already know about this pair.
  889. auto existing_entry =
  890. candidate_composite_synonyms.find(candidate_composite_synonym);
  891. if (existing_entry == candidate_composite_synonyms.end()) {
  892. // If this is the first time we have seen the pair, we make a vector
  893. // of size |num_components_in_composite| that is 'true' at the
  894. // common final index associated with |dd1| and |dd2|, and 'false'
  895. // everywhere else, and register this vector as being associated
  896. // with the pair.
  897. std::vector<bool> entry;
  898. for (uint32_t i = 0; i < num_components_in_composite; i++) {
  899. entry.push_back(i == common_final_index);
  900. }
  901. candidate_composite_synonyms[candidate_composite_synonym] = entry;
  902. existing_entry =
  903. candidate_composite_synonyms.find(candidate_composite_synonym);
  904. } else {
  905. // We have seen this pair of data descriptors before, and we now
  906. // know that they are synonymous at one further index, so we
  907. // update the entry to record that.
  908. existing_entry->second[common_final_index] = true;
  909. }
  910. assert(existing_entry != candidate_composite_synonyms.end());
  911. // Check whether |dd1_prefix| and |dd2_prefix| are now known to match
  912. // at every sub-component.
  913. bool all_components_match = true;
  914. for (uint32_t i = 0; i < num_components_in_composite; i++) {
  915. if (!existing_entry->second[i]) {
  916. all_components_match = false;
  917. break;
  918. }
  919. }
  920. if (all_components_match) {
  921. // The two prefixes match on all sub-components, so we know that
  922. // they are synonymous. We add this fact *non-recursively*, as we
  923. // have deduced that |dd1_prefix| and |dd2_prefix| are synonymous
  924. // by observing that all their sub-components are already
  925. // synonymous.
  926. assert(DataDescriptorsAreWellFormedAndComparable(
  927. context, dd1_prefix, dd2_prefix));
  928. MakeEquivalent(dd1_prefix, dd2_prefix);
  929. // Now that we know this pair of data descriptors are synonymous,
  930. // there is no point recording how close they are to being
  931. // synonymous.
  932. candidate_composite_synonyms.erase(candidate_composite_synonym);
  933. }
  934. }
  935. }
  936. }
  937. }
  938. }
  939. void FactManager::DataSynonymAndIdEquationFacts::MakeEquivalent(
  940. const protobufs::DataDescriptor& dd1,
  941. const protobufs::DataDescriptor& dd2) {
  942. // Register the data descriptors if they are not already known to the
  943. // equivalence relation.
  944. for (const auto& dd : {dd1, dd2}) {
  945. if (!synonymous_.Exists(dd)) {
  946. synonymous_.Register(dd);
  947. }
  948. }
  949. if (synonymous_.IsEquivalent(dd1, dd2)) {
  950. // The data descriptors are already known to be equivalent, so there is
  951. // nothing to do.
  952. return;
  953. }
  954. // We must make the data descriptors equivalent, and also make sure any
  955. // equation facts known about their representatives are merged.
  956. // Record the original equivalence class representatives of the data
  957. // descriptors.
  958. auto dd1_original_representative = synonymous_.Find(&dd1);
  959. auto dd2_original_representative = synonymous_.Find(&dd2);
  960. // Make the data descriptors equivalent.
  961. synonymous_.MakeEquivalent(dd1, dd2);
  962. // As we have updated the equivalence relation, we might be able to deduce
  963. // more facts by performing a closure computation, so we record that such a
  964. // computation is required.
  965. closure_computation_required_ = true;
  966. // At this point, exactly one of |dd1_original_representative| and
  967. // |dd2_original_representative| will be the representative of the combined
  968. // equivalence class. We work out which one of them is still the class
  969. // representative and which one is no longer the class representative.
  970. auto still_representative = synonymous_.Find(dd1_original_representative) ==
  971. dd1_original_representative
  972. ? dd1_original_representative
  973. : dd2_original_representative;
  974. auto no_longer_representative =
  975. still_representative == dd1_original_representative
  976. ? dd2_original_representative
  977. : dd1_original_representative;
  978. assert(no_longer_representative != still_representative &&
  979. "The current and former representatives cannot be the same.");
  980. // We now need to add all equations about |no_longer_representative| to the
  981. // set of equations known about |still_representative|.
  982. // Get the equations associated with |no_longer_representative|.
  983. auto no_longer_representative_id_equations =
  984. id_equations_.find(no_longer_representative);
  985. if (no_longer_representative_id_equations != id_equations_.end()) {
  986. // There are some equations to transfer. There might not yet be any
  987. // equations about |still_representative|; create an empty set of equations
  988. // if this is the case.
  989. if (!id_equations_.count(still_representative)) {
  990. id_equations_.insert({still_representative, OperationSet()});
  991. }
  992. auto still_representative_id_equations =
  993. id_equations_.find(still_representative);
  994. assert(still_representative_id_equations != id_equations_.end() &&
  995. "At this point there must be a set of equations.");
  996. // Add all the equations known about |no_longer_representative| to the set
  997. // of equations known about |still_representative|.
  998. still_representative_id_equations->second.insert(
  999. no_longer_representative_id_equations->second.begin(),
  1000. no_longer_representative_id_equations->second.end());
  1001. }
  1002. // Delete the no longer-relevant equations about |no_longer_representative|.
  1003. id_equations_.erase(no_longer_representative);
  1004. }
  1005. bool FactManager::DataSynonymAndIdEquationFacts::
  1006. DataDescriptorsAreWellFormedAndComparable(
  1007. opt::IRContext* context, const protobufs::DataDescriptor& dd1,
  1008. const protobufs::DataDescriptor& dd2) const {
  1009. auto end_type_id_1 = fuzzerutil::WalkCompositeTypeIndices(
  1010. context, context->get_def_use_mgr()->GetDef(dd1.object())->type_id(),
  1011. dd1.index());
  1012. auto end_type_id_2 = fuzzerutil::WalkCompositeTypeIndices(
  1013. context, context->get_def_use_mgr()->GetDef(dd2.object())->type_id(),
  1014. dd2.index());
  1015. // The end types of the data descriptors must exist.
  1016. if (end_type_id_1 == 0 || end_type_id_2 == 0) {
  1017. return false;
  1018. }
  1019. // If the end types are the same, the data descriptors are comparable.
  1020. if (end_type_id_1 == end_type_id_2) {
  1021. return true;
  1022. }
  1023. // Otherwise they are only comparable if they are integer scalars or integer
  1024. // vectors that differ only in signedness.
  1025. // Get both types.
  1026. const opt::analysis::Type* type_1 =
  1027. context->get_type_mgr()->GetType(end_type_id_1);
  1028. const opt::analysis::Type* type_2 =
  1029. context->get_type_mgr()->GetType(end_type_id_2);
  1030. // If the first type is a vector, check that the second type is a vector of
  1031. // the same width, and drill down to the vector element types.
  1032. if (type_1->AsVector()) {
  1033. if (!type_2->AsVector()) {
  1034. return false;
  1035. }
  1036. if (type_1->AsVector()->element_count() !=
  1037. type_2->AsVector()->element_count()) {
  1038. return false;
  1039. }
  1040. type_1 = type_1->AsVector()->element_type();
  1041. type_2 = type_2->AsVector()->element_type();
  1042. }
  1043. // Check that type_1 and type_2 are both integer types of the same bit-width
  1044. // (but with potentially different signedness).
  1045. auto integer_type_1 = type_1->AsInteger();
  1046. auto integer_type_2 = type_2->AsInteger();
  1047. return integer_type_1 && integer_type_2 &&
  1048. integer_type_1->width() == integer_type_2->width();
  1049. }
  1050. std::vector<const protobufs::DataDescriptor*>
  1051. FactManager::DataSynonymAndIdEquationFacts::GetSynonymsForDataDescriptor(
  1052. const protobufs::DataDescriptor& data_descriptor) const {
  1053. if (synonymous_.Exists(data_descriptor)) {
  1054. return synonymous_.GetEquivalenceClass(data_descriptor);
  1055. }
  1056. return std::vector<const protobufs::DataDescriptor*>();
  1057. }
  1058. std::vector<uint32_t>
  1059. FactManager::DataSynonymAndIdEquationFacts::GetIdsForWhichSynonymsAreKnown()
  1060. const {
  1061. std::vector<uint32_t> result;
  1062. for (auto& data_descriptor : synonymous_.GetAllKnownValues()) {
  1063. if (data_descriptor->index().empty()) {
  1064. result.push_back(data_descriptor->object());
  1065. }
  1066. }
  1067. return result;
  1068. }
  1069. bool FactManager::DataSynonymAndIdEquationFacts::IsSynonymous(
  1070. const protobufs::DataDescriptor& data_descriptor1,
  1071. const protobufs::DataDescriptor& data_descriptor2) const {
  1072. return synonymous_.Exists(data_descriptor1) &&
  1073. synonymous_.Exists(data_descriptor2) &&
  1074. synonymous_.IsEquivalent(data_descriptor1, data_descriptor2);
  1075. }
  1076. // End of data synonym facts
  1077. //==============================
  1078. //==============================
  1079. // Dead block facts
  1080. // The purpose of this class is to group the fields and data used to represent
  1081. // facts about data blocks.
  1082. class FactManager::DeadBlockFacts {
  1083. public:
  1084. // See method in FactManager which delegates to this method.
  1085. void AddFact(const protobufs::FactBlockIsDead& fact);
  1086. // See method in FactManager which delegates to this method.
  1087. bool BlockIsDead(uint32_t block_id) const;
  1088. private:
  1089. std::set<uint32_t> dead_block_ids_;
  1090. };
  1091. void FactManager::DeadBlockFacts::AddFact(
  1092. const protobufs::FactBlockIsDead& fact) {
  1093. dead_block_ids_.insert(fact.block_id());
  1094. }
  1095. bool FactManager::DeadBlockFacts::BlockIsDead(uint32_t block_id) const {
  1096. return dead_block_ids_.count(block_id) != 0;
  1097. }
  1098. // End of dead block facts
  1099. //==============================
  1100. //==============================
  1101. // Livesafe function facts
  1102. // The purpose of this class is to group the fields and data used to represent
  1103. // facts about livesafe functions.
  1104. class FactManager::LivesafeFunctionFacts {
  1105. public:
  1106. // See method in FactManager which delegates to this method.
  1107. void AddFact(const protobufs::FactFunctionIsLivesafe& fact);
  1108. // See method in FactManager which delegates to this method.
  1109. bool FunctionIsLivesafe(uint32_t function_id) const;
  1110. private:
  1111. std::set<uint32_t> livesafe_function_ids_;
  1112. };
  1113. void FactManager::LivesafeFunctionFacts::AddFact(
  1114. const protobufs::FactFunctionIsLivesafe& fact) {
  1115. livesafe_function_ids_.insert(fact.function_id());
  1116. }
  1117. bool FactManager::LivesafeFunctionFacts::FunctionIsLivesafe(
  1118. uint32_t function_id) const {
  1119. return livesafe_function_ids_.count(function_id) != 0;
  1120. }
  1121. // End of livesafe function facts
  1122. //==============================
  1123. //==============================
  1124. // Irrelevant pointee value facts
  1125. // The purpose of this class is to group the fields and data used to represent
  1126. // facts about pointers whose pointee values are irrelevant.
  1127. class FactManager::IrrelevantPointeeValueFacts {
  1128. public:
  1129. // See method in FactManager which delegates to this method.
  1130. void AddFact(const protobufs::FactPointeeValueIsIrrelevant& fact);
  1131. // See method in FactManager which delegates to this method.
  1132. bool PointeeValueIsIrrelevant(uint32_t pointer_id) const;
  1133. private:
  1134. std::set<uint32_t> pointers_to_irrelevant_pointees_ids_;
  1135. };
  1136. void FactManager::IrrelevantPointeeValueFacts::AddFact(
  1137. const protobufs::FactPointeeValueIsIrrelevant& fact) {
  1138. pointers_to_irrelevant_pointees_ids_.insert(fact.pointer_id());
  1139. }
  1140. bool FactManager::IrrelevantPointeeValueFacts::PointeeValueIsIrrelevant(
  1141. uint32_t pointer_id) const {
  1142. return pointers_to_irrelevant_pointees_ids_.count(pointer_id) != 0;
  1143. }
  1144. // End of arbitrarily-valued variable facts
  1145. //==============================
  1146. FactManager::FactManager()
  1147. : uniform_constant_facts_(MakeUnique<ConstantUniformFacts>()),
  1148. data_synonym_and_id_equation_facts_(
  1149. MakeUnique<DataSynonymAndIdEquationFacts>()),
  1150. dead_block_facts_(MakeUnique<DeadBlockFacts>()),
  1151. livesafe_function_facts_(MakeUnique<LivesafeFunctionFacts>()),
  1152. irrelevant_pointee_value_facts_(
  1153. MakeUnique<IrrelevantPointeeValueFacts>()) {}
  1154. FactManager::~FactManager() = default;
  1155. void FactManager::AddFacts(const MessageConsumer& message_consumer,
  1156. const protobufs::FactSequence& initial_facts,
  1157. opt::IRContext* context) {
  1158. for (auto& fact : initial_facts.fact()) {
  1159. if (!AddFact(fact, context)) {
  1160. message_consumer(
  1161. SPV_MSG_WARNING, nullptr, {},
  1162. ("Invalid fact " + ToString(fact) + " ignored.").c_str());
  1163. }
  1164. }
  1165. }
  1166. bool FactManager::AddFact(const fuzz::protobufs::Fact& fact,
  1167. opt::IRContext* context) {
  1168. switch (fact.fact_case()) {
  1169. case protobufs::Fact::kConstantUniformFact:
  1170. return uniform_constant_facts_->AddFact(fact.constant_uniform_fact(),
  1171. context);
  1172. case protobufs::Fact::kDataSynonymFact:
  1173. data_synonym_and_id_equation_facts_->AddFact(fact.data_synonym_fact(),
  1174. context);
  1175. return true;
  1176. case protobufs::Fact::kBlockIsDeadFact:
  1177. dead_block_facts_->AddFact(fact.block_is_dead_fact());
  1178. return true;
  1179. case protobufs::Fact::kFunctionIsLivesafeFact:
  1180. livesafe_function_facts_->AddFact(fact.function_is_livesafe_fact());
  1181. return true;
  1182. default:
  1183. assert(false && "Unknown fact type.");
  1184. return false;
  1185. }
  1186. }
  1187. void FactManager::AddFactDataSynonym(const protobufs::DataDescriptor& data1,
  1188. const protobufs::DataDescriptor& data2,
  1189. opt::IRContext* context) {
  1190. protobufs::FactDataSynonym fact;
  1191. *fact.mutable_data1() = data1;
  1192. *fact.mutable_data2() = data2;
  1193. data_synonym_and_id_equation_facts_->AddFact(fact, context);
  1194. }
  1195. std::vector<uint32_t> FactManager::GetConstantsAvailableFromUniformsForType(
  1196. opt::IRContext* ir_context, uint32_t type_id) const {
  1197. return uniform_constant_facts_->GetConstantsAvailableFromUniformsForType(
  1198. ir_context, type_id);
  1199. }
  1200. const std::vector<protobufs::UniformBufferElementDescriptor>
  1201. FactManager::GetUniformDescriptorsForConstant(opt::IRContext* ir_context,
  1202. uint32_t constant_id) const {
  1203. return uniform_constant_facts_->GetUniformDescriptorsForConstant(ir_context,
  1204. constant_id);
  1205. }
  1206. uint32_t FactManager::GetConstantFromUniformDescriptor(
  1207. opt::IRContext* context,
  1208. const protobufs::UniformBufferElementDescriptor& uniform_descriptor) const {
  1209. return uniform_constant_facts_->GetConstantFromUniformDescriptor(
  1210. context, uniform_descriptor);
  1211. }
  1212. std::vector<uint32_t> FactManager::GetTypesForWhichUniformValuesAreKnown()
  1213. const {
  1214. return uniform_constant_facts_->GetTypesForWhichUniformValuesAreKnown();
  1215. }
  1216. const std::vector<std::pair<protobufs::FactConstantUniform, uint32_t>>&
  1217. FactManager::GetConstantUniformFactsAndTypes() const {
  1218. return uniform_constant_facts_->GetConstantUniformFactsAndTypes();
  1219. }
  1220. std::vector<uint32_t> FactManager::GetIdsForWhichSynonymsAreKnown() const {
  1221. return data_synonym_and_id_equation_facts_->GetIdsForWhichSynonymsAreKnown();
  1222. }
  1223. std::vector<const protobufs::DataDescriptor*>
  1224. FactManager::GetSynonymsForDataDescriptor(
  1225. const protobufs::DataDescriptor& data_descriptor) const {
  1226. return data_synonym_and_id_equation_facts_->GetSynonymsForDataDescriptor(
  1227. data_descriptor);
  1228. }
  1229. std::vector<const protobufs::DataDescriptor*> FactManager::GetSynonymsForId(
  1230. uint32_t id) const {
  1231. return GetSynonymsForDataDescriptor(MakeDataDescriptor(id, {}));
  1232. }
  1233. bool FactManager::IsSynonymous(
  1234. const protobufs::DataDescriptor& data_descriptor1,
  1235. const protobufs::DataDescriptor& data_descriptor2) const {
  1236. return data_synonym_and_id_equation_facts_->IsSynonymous(data_descriptor1,
  1237. data_descriptor2);
  1238. }
  1239. bool FactManager::BlockIsDead(uint32_t block_id) const {
  1240. return dead_block_facts_->BlockIsDead(block_id);
  1241. }
  1242. void FactManager::AddFactBlockIsDead(uint32_t block_id) {
  1243. protobufs::FactBlockIsDead fact;
  1244. fact.set_block_id(block_id);
  1245. dead_block_facts_->AddFact(fact);
  1246. }
  1247. bool FactManager::FunctionIsLivesafe(uint32_t function_id) const {
  1248. return livesafe_function_facts_->FunctionIsLivesafe(function_id);
  1249. }
  1250. void FactManager::AddFactFunctionIsLivesafe(uint32_t function_id) {
  1251. protobufs::FactFunctionIsLivesafe fact;
  1252. fact.set_function_id(function_id);
  1253. livesafe_function_facts_->AddFact(fact);
  1254. }
  1255. bool FactManager::PointeeValueIsIrrelevant(uint32_t pointer_id) const {
  1256. return irrelevant_pointee_value_facts_->PointeeValueIsIrrelevant(pointer_id);
  1257. }
  1258. void FactManager::AddFactValueOfPointeeIsIrrelevant(uint32_t pointer_id) {
  1259. protobufs::FactPointeeValueIsIrrelevant fact;
  1260. fact.set_pointer_id(pointer_id);
  1261. irrelevant_pointee_value_facts_->AddFact(fact);
  1262. }
  1263. void FactManager::AddFactIdEquation(uint32_t lhs_id, SpvOp opcode,
  1264. const std::vector<uint32_t>& rhs_id,
  1265. opt::IRContext* context) {
  1266. protobufs::FactIdEquation fact;
  1267. fact.set_lhs_id(lhs_id);
  1268. fact.set_opcode(opcode);
  1269. for (auto an_rhs_id : rhs_id) {
  1270. fact.add_rhs_id(an_rhs_id);
  1271. }
  1272. data_synonym_and_id_equation_facts_->AddFact(fact, context);
  1273. }
  1274. void FactManager::ComputeClosureOfFacts(
  1275. opt::IRContext* ir_context, uint32_t maximum_equivalence_class_size) {
  1276. data_synonym_and_id_equation_facts_->ComputeClosureOfFacts(
  1277. ir_context, maximum_equivalence_class_size);
  1278. }
  1279. } // namespace fuzz
  1280. } // namespace spvtools