fact_manager.cpp 53 KB

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