diff.cpp 96 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658
  1. // Copyright (c) 2022 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/diff/diff.h"
  15. #include "source/diff/lcs.h"
  16. #include "source/disassemble.h"
  17. #include "source/ext_inst.h"
  18. #include "source/latest_version_spirv_header.h"
  19. #include "source/print.h"
  20. #include "spirv-tools/libspirv.hpp"
  21. namespace spvtools {
  22. namespace diff {
  23. namespace {
  24. // A map from an id to the instruction that defines it.
  25. using IdToInstructionMap = std::vector<const opt::Instruction*>;
  26. // A map from an id to the instructions that decorate it, or name it, etc.
  27. using IdToInfoMap = std::vector<std::vector<const opt::Instruction*>>;
  28. // A map from an instruction to another, used for instructions without id.
  29. using InstructionToInstructionMap =
  30. std::unordered_map<const opt::Instruction*, const opt::Instruction*>;
  31. // A flat list of instructions in a function for easier iteration.
  32. using InstructionList = std::vector<const opt::Instruction*>;
  33. // A map from a function to its list of instructions.
  34. using FunctionInstMap = std::unordered_map<uint32_t, InstructionList>;
  35. // A list of ids with some similar property, for example functions with the same
  36. // name.
  37. using IdGroup = std::vector<uint32_t>;
  38. // A map of function names to function ids with the same name. This is an
  39. // ordered map so different implementations produce identical results.
  40. using IdGroupMapByName = std::map<std::string, IdGroup>;
  41. using IdGroupMapByTypeId = std::map<uint32_t, IdGroup>;
  42. // A set of potential id mappings that haven't been resolved yet. Any id in src
  43. // may map in any id in dst. Note that ids are added in the same order as they
  44. // appear in src and dst to facilitate matching dependent instructions. For
  45. // example, this guarantees that when matching OpTypeVector, the basic type of
  46. // the vector is already (potentially) matched.
  47. struct PotentialIdMap {
  48. std::vector<uint32_t> src_ids;
  49. std::vector<uint32_t> dst_ids;
  50. };
  51. void CompactIds(std::vector<uint32_t>& ids) {
  52. size_t write_index = 0;
  53. for (size_t i = 0; i < ids.size(); ++i) {
  54. if (ids[i] != 0) {
  55. ids[write_index++] = ids[i];
  56. }
  57. }
  58. ids.resize(write_index);
  59. }
  60. // A mapping between src and dst ids.
  61. class IdMap {
  62. public:
  63. IdMap(size_t id_bound) { id_map_.resize(id_bound, 0); }
  64. void MapIds(uint32_t from, uint32_t to) {
  65. assert(from != 0);
  66. assert(to != 0);
  67. assert(from < id_map_.size());
  68. assert(id_map_[from] == 0);
  69. id_map_[from] = to;
  70. }
  71. uint32_t MappedId(uint32_t from) const {
  72. assert(from != 0);
  73. return from < id_map_.size() ? id_map_[from] : 0;
  74. }
  75. const opt::Instruction* MappedInst(const opt::Instruction* from_inst) const {
  76. assert(from_inst != nullptr);
  77. assert(!from_inst->HasResultId());
  78. auto mapped = inst_map_.find(from_inst);
  79. if (mapped == inst_map_.end()) {
  80. return nullptr;
  81. }
  82. return mapped->second;
  83. }
  84. bool IsMapped(uint32_t from) const {
  85. assert(from != 0);
  86. return from < id_map_.size() && id_map_[from] != 0;
  87. }
  88. // Map any ids in src and dst that have not been mapped to new ids in dst and
  89. // src respectively.
  90. void MapUnmatchedIds(IdMap& other_way);
  91. // Some instructions don't have result ids. Those are mapped by pointer.
  92. void MapInsts(const opt::Instruction* from_inst,
  93. const opt::Instruction* to_inst) {
  94. assert(from_inst != nullptr);
  95. assert(to_inst != nullptr);
  96. assert(inst_map_.find(from_inst) == inst_map_.end());
  97. inst_map_[from_inst] = to_inst;
  98. }
  99. uint32_t IdBound() const { return static_cast<uint32_t>(id_map_.size()); }
  100. private:
  101. // Given an id, returns the corresponding id in the other module, or 0 if not
  102. // matched yet.
  103. std::vector<uint32_t> id_map_;
  104. // Same for instructions that don't have an id.
  105. InstructionToInstructionMap inst_map_;
  106. };
  107. // Two way mapping of ids.
  108. class SrcDstIdMap {
  109. public:
  110. SrcDstIdMap(size_t src_id_bound, size_t dst_id_bound)
  111. : src_to_dst_(src_id_bound), dst_to_src_(dst_id_bound) {}
  112. void MapIds(uint32_t src, uint32_t dst) {
  113. src_to_dst_.MapIds(src, dst);
  114. dst_to_src_.MapIds(dst, src);
  115. }
  116. uint32_t MappedDstId(uint32_t src) {
  117. uint32_t dst = src_to_dst_.MappedId(src);
  118. assert(dst == 0 || dst_to_src_.MappedId(dst) == src);
  119. return dst;
  120. }
  121. uint32_t MappedSrcId(uint32_t dst) {
  122. uint32_t src = dst_to_src_.MappedId(dst);
  123. assert(src == 0 || src_to_dst_.MappedId(src) == dst);
  124. return src;
  125. }
  126. bool IsSrcMapped(uint32_t src) { return src_to_dst_.IsMapped(src); }
  127. bool IsDstMapped(uint32_t dst) { return dst_to_src_.IsMapped(dst); }
  128. // Map any ids in src and dst that have not been mapped to new ids in dst and
  129. // src respectively.
  130. void MapUnmatchedIds();
  131. // Some instructions don't have result ids. Those are mapped by pointer.
  132. void MapInsts(const opt::Instruction* src_inst,
  133. const opt::Instruction* dst_inst) {
  134. assert(src_inst->HasResultId() == dst_inst->HasResultId());
  135. if (src_inst->HasResultId()) {
  136. MapIds(src_inst->result_id(), dst_inst->result_id());
  137. } else {
  138. src_to_dst_.MapInsts(src_inst, dst_inst);
  139. dst_to_src_.MapInsts(dst_inst, src_inst);
  140. }
  141. }
  142. const IdMap& SrcToDstMap() const { return src_to_dst_; }
  143. const IdMap& DstToSrcMap() const { return dst_to_src_; }
  144. private:
  145. IdMap src_to_dst_;
  146. IdMap dst_to_src_;
  147. };
  148. struct IdInstructions {
  149. IdInstructions(const opt::Module* module)
  150. : inst_map_(module->IdBound(), nullptr),
  151. name_map_(module->IdBound()),
  152. decoration_map_(module->IdBound()) {
  153. // Map ids from all sections to instructions that define them.
  154. MapIdsToInstruction(module->ext_inst_imports());
  155. MapIdsToInstruction(module->types_values());
  156. for (const opt::Function& function : *module) {
  157. function.ForEachInst(
  158. [this](const opt::Instruction* inst) {
  159. if (inst->HasResultId()) {
  160. MapIdToInstruction(inst->result_id(), inst);
  161. }
  162. },
  163. true, true);
  164. }
  165. // Gather decorations applied to ids that could be useful in matching them
  166. // between src and dst modules.
  167. MapIdsToInfos(module->debugs2());
  168. MapIdsToInfos(module->annotations());
  169. }
  170. void MapIdToInstruction(uint32_t id, const opt::Instruction* inst);
  171. void MapIdsToInstruction(
  172. opt::IteratorRange<opt::Module::const_inst_iterator> section);
  173. void MapIdsToInfos(
  174. opt::IteratorRange<opt::Module::const_inst_iterator> section);
  175. IdToInstructionMap inst_map_;
  176. IdToInfoMap name_map_;
  177. IdToInfoMap decoration_map_;
  178. };
  179. class Differ {
  180. public:
  181. Differ(opt::IRContext* src, opt::IRContext* dst, std::ostream& out,
  182. Options options)
  183. : src_context_(src),
  184. dst_context_(dst),
  185. src_(src->module()),
  186. dst_(dst->module()),
  187. options_(options),
  188. out_(out),
  189. src_id_to_(src_),
  190. dst_id_to_(dst_),
  191. id_map_(src_->IdBound(), dst_->IdBound()) {
  192. // Cache function bodies in canonicalization order.
  193. GetFunctionBodies(src_context_, &src_funcs_, &src_func_insts_);
  194. GetFunctionBodies(dst_context_, &dst_funcs_, &dst_func_insts_);
  195. }
  196. // Match ids or instructions of different sections.
  197. void MatchCapabilities();
  198. void MatchExtensions();
  199. void MatchExtInstImportIds();
  200. void MatchMemoryModel();
  201. void MatchEntryPointIds();
  202. void MatchExecutionModes();
  203. void MatchTypeIds();
  204. void MatchConstants();
  205. void MatchVariableIds();
  206. void MatchFunctions();
  207. // Debug info and annotations are matched only after ids are matched.
  208. void MatchDebugs1();
  209. void MatchDebugs2();
  210. void MatchDebugs3();
  211. void MatchExtInstDebugInfo();
  212. void MatchAnnotations();
  213. // Output the diff.
  214. spv_result_t Output();
  215. void DumpIdMap() {
  216. if (!options_.dump_id_map) {
  217. return;
  218. }
  219. out_ << " Src -> Dst\n";
  220. for (uint32_t src_id = 1; src_id < src_->IdBound(); ++src_id) {
  221. uint32_t dst_id = id_map_.MappedDstId(src_id);
  222. if (src_id_to_.inst_map_[src_id] != nullptr && dst_id != 0)
  223. out_ << std::setw(4) << src_id << " -> " << std::setw(4) << dst_id
  224. << " [" << spvOpcodeString(src_id_to_.inst_map_[src_id]->opcode())
  225. << "]\n";
  226. }
  227. }
  228. private:
  229. // Helper functions that match ids between src and dst
  230. void PoolPotentialIds(
  231. opt::IteratorRange<opt::Module::const_inst_iterator> section,
  232. std::vector<uint32_t>& ids,
  233. std::function<bool(const opt::Instruction&)> filter,
  234. std::function<uint32_t(const opt::Instruction&)> get_id);
  235. void MatchIds(
  236. PotentialIdMap& potential,
  237. std::function<bool(const opt::Instruction*, const opt::Instruction*)>
  238. match);
  239. // Helper functions that match id-less instructions between src and dst.
  240. void MatchPreambleInstructions(
  241. opt::IteratorRange<opt::Module::const_inst_iterator> src_insts,
  242. opt::IteratorRange<opt::Module::const_inst_iterator> dst_insts);
  243. InstructionList SortPreambleInstructions(
  244. const opt::Module* module,
  245. opt::IteratorRange<opt::Module::const_inst_iterator> insts);
  246. int ComparePreambleInstructions(const opt::Instruction* a,
  247. const opt::Instruction* b,
  248. const opt::Module* src_inst_module,
  249. const opt::Module* dst_inst_module);
  250. // Helper functions that match debug and annotation instructions of already
  251. // matched ids.
  252. void MatchDebugAndAnnotationInstructions(
  253. opt::IteratorRange<opt::Module::const_inst_iterator> src_insts,
  254. opt::IteratorRange<opt::Module::const_inst_iterator> dst_insts);
  255. // Helper functions that determine if two instructions match
  256. bool DoIdsMatch(uint32_t src_id, uint32_t dst_id);
  257. bool DoesOperandMatch(const opt::Operand& src_operand,
  258. const opt::Operand& dst_operand);
  259. bool DoOperandsMatch(const opt::Instruction* src_inst,
  260. const opt::Instruction* dst_inst,
  261. uint32_t in_operand_index_start,
  262. uint32_t in_operand_count);
  263. bool DoInstructionsMatch(const opt::Instruction* src_inst,
  264. const opt::Instruction* dst_inst);
  265. bool DoIdsMatchFuzzy(uint32_t src_id, uint32_t dst_id);
  266. bool DoesOperandMatchFuzzy(const opt::Operand& src_operand,
  267. const opt::Operand& dst_operand);
  268. bool DoInstructionsMatchFuzzy(const opt::Instruction* src_inst,
  269. const opt::Instruction* dst_inst);
  270. bool DoDebugAndAnnotationInstructionsMatch(const opt::Instruction* src_inst,
  271. const opt::Instruction* dst_inst);
  272. bool AreVariablesMatchable(uint32_t src_id, uint32_t dst_id,
  273. uint32_t flexibility);
  274. bool MatchOpTypeStruct(const opt::Instruction* src_inst,
  275. const opt::Instruction* dst_inst,
  276. uint32_t flexibility);
  277. bool MatchOpConstant(const opt::Instruction* src_inst,
  278. const opt::Instruction* dst_inst, uint32_t flexibility);
  279. bool MatchOpSpecConstant(const opt::Instruction* src_inst,
  280. const opt::Instruction* dst_inst);
  281. bool MatchOpVariable(const opt::Instruction* src_inst,
  282. const opt::Instruction* dst_inst, uint32_t flexibility);
  283. bool MatchPerVertexType(uint32_t src_type_id, uint32_t dst_type_id);
  284. bool MatchPerVertexVariable(const opt::Instruction* src_inst,
  285. const opt::Instruction* dst_inst);
  286. // Helper functions for function matching.
  287. using FunctionMap = std::map<uint32_t, const opt::Function*>;
  288. InstructionList GetFunctionBody(opt::IRContext* context,
  289. opt::Function& function);
  290. InstructionList GetFunctionHeader(const opt::Function& function);
  291. void GetFunctionBodies(opt::IRContext* context, FunctionMap* functions,
  292. FunctionInstMap* function_insts);
  293. void GetFunctionHeaderInstructions(const opt::Module* module,
  294. FunctionInstMap* function_insts);
  295. void GroupIdsByName(const IdGroup& functions, bool is_src,
  296. IdGroupMapByName* groups);
  297. void GroupIdsByTypeId(const IdGroup& functions, bool is_src,
  298. IdGroupMapByTypeId* groups);
  299. template <typename T>
  300. void GroupIds(const IdGroup& functions, bool is_src,
  301. std::map<T, IdGroup>* groups,
  302. std::function<T(const IdInstructions, uint32_t)> get_group);
  303. void BestEffortMatchFunctions(const IdGroup& src_func_ids,
  304. const IdGroup& dst_func_ids,
  305. const FunctionInstMap& src_func_insts,
  306. const FunctionInstMap& dst_func_insts);
  307. // Calculates the diff of two function bodies. Note that the matched
  308. // instructions themselves may not be identical; output of exact matches
  309. // should produce the exact instruction while inexact matches should produce a
  310. // diff as well.
  311. //
  312. // Returns the similarity of the two bodies = 2*N_match / (N_src + N_dst)
  313. void MatchFunctionParamIds(const opt::Function* src_func,
  314. const opt::Function* dst_func);
  315. float MatchFunctionBodies(const InstructionList& src_body,
  316. const InstructionList& dst_body,
  317. DiffMatch* src_match_result,
  318. DiffMatch* dst_match_result);
  319. void MatchIdsInFunctionBodies(const InstructionList& src_body,
  320. const InstructionList& dst_body,
  321. const DiffMatch& src_match_result,
  322. const DiffMatch& dst_match_result,
  323. uint32_t flexibility);
  324. void MatchVariablesUsedByMatchedInstructions(const opt::Instruction* src_inst,
  325. const opt::Instruction* dst_inst,
  326. uint32_t flexibility);
  327. // Helper functions to retrieve information pertaining to an id
  328. const opt::Instruction* GetInst(const IdInstructions& id_to, uint32_t id);
  329. uint32_t GetConstantUint(const IdInstructions& id_to, uint32_t constant_id);
  330. SpvExecutionModel GetExecutionModel(const opt::Module* module,
  331. uint32_t entry_point_id);
  332. std::string GetName(const IdInstructions& id_to, uint32_t id, bool* has_name);
  333. std::string GetFunctionName(const IdInstructions& id_to, uint32_t id);
  334. uint32_t GetVarTypeId(const IdInstructions& id_to, uint32_t var_id,
  335. SpvStorageClass* storage_class);
  336. bool GetDecorationValue(const IdInstructions& id_to, uint32_t id,
  337. SpvDecoration decoration, uint32_t* decoration_value);
  338. bool IsIntType(const IdInstructions& id_to, uint32_t type_id);
  339. // bool IsUintType(const IdInstructions& id_to, uint32_t type_id);
  340. bool IsFloatType(const IdInstructions& id_to, uint32_t type_id);
  341. bool IsConstantUint(const IdInstructions& id_to, uint32_t id);
  342. bool IsVariable(const IdInstructions& id_to, uint32_t pointer_id);
  343. bool IsOp(const IdInstructions& id_to, uint32_t id, SpvOp opcode);
  344. bool IsPerVertexType(const IdInstructions& id_to, uint32_t type_id);
  345. bool IsPerVertexVariable(const IdInstructions& id_to, uint32_t type_id);
  346. SpvStorageClass GetPerVertexStorageClass(const opt::Module* module,
  347. uint32_t type_id);
  348. spv_ext_inst_type_t GetExtInstType(const IdInstructions& id_to,
  349. uint32_t set_id);
  350. spv_number_kind_t GetNumberKind(const IdInstructions& id_to,
  351. const opt::Instruction& inst,
  352. uint32_t operand_index,
  353. uint32_t* number_bit_width);
  354. spv_number_kind_t GetTypeNumberKind(const IdInstructions& id_to, uint32_t id,
  355. uint32_t* number_bit_width);
  356. // Helper functions to output a diff line
  357. const opt::Instruction* MappedDstInst(const opt::Instruction* src_inst);
  358. const opt::Instruction* MappedSrcInst(const opt::Instruction* dst_inst);
  359. const opt::Instruction* MappedInstImpl(const opt::Instruction* inst,
  360. const IdMap& to_other,
  361. const IdInstructions& other_id_to);
  362. void OutputLine(std::function<bool()> are_lines_identical,
  363. std::function<void()> output_src_line,
  364. std::function<void()> output_dst_line);
  365. template <typename InstList>
  366. void OutputSection(
  367. const InstList& src_insts, const InstList& dst_insts,
  368. std::function<void(const opt::Instruction&, const IdInstructions&,
  369. const opt::Instruction&)>
  370. write_inst);
  371. void ToParsedInstruction(const opt::Instruction& inst,
  372. const IdInstructions& id_to,
  373. const opt::Instruction& original_inst,
  374. spv_parsed_instruction_t* parsed_inst,
  375. std::vector<spv_parsed_operand_t>& parsed_operands,
  376. std::vector<uint32_t>& inst_binary);
  377. opt::Instruction ToMappedSrcIds(const opt::Instruction& dst_inst);
  378. void OutputRed() {
  379. if (options_.color_output) out_ << spvtools::clr::red{true};
  380. }
  381. void OutputGreen() {
  382. if (options_.color_output) out_ << spvtools::clr::green{true};
  383. }
  384. void OutputResetColor() {
  385. if (options_.color_output) out_ << spvtools::clr::reset{true};
  386. }
  387. opt::IRContext* src_context_;
  388. opt::IRContext* dst_context_;
  389. const opt::Module* src_;
  390. const opt::Module* dst_;
  391. Options options_;
  392. std::ostream& out_;
  393. // Helpers to look up instructions based on id.
  394. IdInstructions src_id_to_;
  395. IdInstructions dst_id_to_;
  396. // The ids that have been matched between src and dst so far.
  397. SrcDstIdMap id_map_;
  398. // List of instructions in function bodies after canonicalization. Cached
  399. // here to avoid duplicate work. More importantly, some maps use
  400. // opt::Instruction pointers so they need to be unique.
  401. FunctionInstMap src_func_insts_;
  402. FunctionInstMap dst_func_insts_;
  403. FunctionMap src_funcs_;
  404. FunctionMap dst_funcs_;
  405. };
  406. void IdMap::MapUnmatchedIds(IdMap& other_way) {
  407. const uint32_t src_id_bound = static_cast<uint32_t>(id_map_.size());
  408. const uint32_t dst_id_bound = static_cast<uint32_t>(other_way.id_map_.size());
  409. uint32_t next_src_id = src_id_bound;
  410. uint32_t next_dst_id = dst_id_bound;
  411. for (uint32_t src_id = 1; src_id < src_id_bound; ++src_id) {
  412. if (!IsMapped(src_id)) {
  413. MapIds(src_id, next_dst_id);
  414. other_way.id_map_.push_back(0);
  415. other_way.MapIds(next_dst_id++, src_id);
  416. }
  417. }
  418. for (uint32_t dst_id = 1; dst_id < dst_id_bound; ++dst_id) {
  419. if (!other_way.IsMapped(dst_id)) {
  420. id_map_.push_back(0);
  421. MapIds(next_src_id, dst_id);
  422. other_way.MapIds(dst_id, next_src_id++);
  423. }
  424. }
  425. }
  426. void SrcDstIdMap::MapUnmatchedIds() {
  427. src_to_dst_.MapUnmatchedIds(dst_to_src_);
  428. }
  429. void IdInstructions::MapIdToInstruction(uint32_t id,
  430. const opt::Instruction* inst) {
  431. assert(id != 0);
  432. assert(id < inst_map_.size());
  433. assert(inst_map_[id] == nullptr);
  434. inst_map_[id] = inst;
  435. }
  436. void IdInstructions::MapIdsToInstruction(
  437. opt::IteratorRange<opt::Module::const_inst_iterator> section) {
  438. for (const opt::Instruction& inst : section) {
  439. uint32_t result_id = inst.result_id();
  440. if (result_id == 0) {
  441. continue;
  442. }
  443. MapIdToInstruction(result_id, &inst);
  444. }
  445. }
  446. void IdInstructions::MapIdsToInfos(
  447. opt::IteratorRange<opt::Module::const_inst_iterator> section) {
  448. for (const opt::Instruction& inst : section) {
  449. IdToInfoMap* info_map = nullptr;
  450. uint32_t id_operand = 0;
  451. switch (inst.opcode()) {
  452. case SpvOpName:
  453. info_map = &name_map_;
  454. break;
  455. case SpvOpMemberName:
  456. info_map = &name_map_;
  457. break;
  458. case SpvOpDecorate:
  459. info_map = &decoration_map_;
  460. break;
  461. case SpvOpMemberDecorate:
  462. info_map = &decoration_map_;
  463. break;
  464. default:
  465. // Currently unsupported instruction, don't attempt to use it for
  466. // matching.
  467. break;
  468. }
  469. if (info_map == nullptr) {
  470. continue;
  471. }
  472. uint32_t id = inst.GetOperand(id_operand).AsId();
  473. assert(id != 0);
  474. assert(id < info_map->size());
  475. assert(std::find((*info_map)[id].begin(), (*info_map)[id].end(), &inst) ==
  476. (*info_map)[id].end());
  477. (*info_map)[id].push_back(&inst);
  478. }
  479. }
  480. void Differ::PoolPotentialIds(
  481. opt::IteratorRange<opt::Module::const_inst_iterator> section,
  482. std::vector<uint32_t>& ids,
  483. std::function<bool(const opt::Instruction&)> filter,
  484. std::function<uint32_t(const opt::Instruction&)> get_id) {
  485. for (const opt::Instruction& inst : section) {
  486. if (!filter(inst)) {
  487. continue;
  488. }
  489. uint32_t result_id = get_id(inst);
  490. assert(result_id != 0);
  491. assert(std::find(ids.begin(), ids.end(), result_id) == ids.end());
  492. ids.push_back(result_id);
  493. }
  494. }
  495. void Differ::MatchIds(
  496. PotentialIdMap& potential,
  497. std::function<bool(const opt::Instruction*, const opt::Instruction*)>
  498. match) {
  499. for (size_t src_index = 0; src_index < potential.src_ids.size();
  500. ++src_index) {
  501. for (size_t dst_index = 0; dst_index < potential.dst_ids.size();
  502. ++dst_index) {
  503. const uint32_t src_id = potential.src_ids[src_index];
  504. const uint32_t dst_id = potential.dst_ids[dst_index];
  505. if (dst_id == 0) {
  506. // Already matched.
  507. continue;
  508. }
  509. const opt::Instruction* src_inst = src_id_to_.inst_map_[src_id];
  510. const opt::Instruction* dst_inst = dst_id_to_.inst_map_[dst_id];
  511. if (match(src_inst, dst_inst)) {
  512. id_map_.MapIds(src_id, dst_id);
  513. // Remove the ids from the potential list.
  514. potential.src_ids[src_index] = 0;
  515. potential.dst_ids[dst_index] = 0;
  516. // Find a match for the next src id.
  517. break;
  518. }
  519. }
  520. }
  521. // Remove matched ids to make the next iteration faster.
  522. CompactIds(potential.src_ids);
  523. CompactIds(potential.dst_ids);
  524. }
  525. void Differ::MatchPreambleInstructions(
  526. opt::IteratorRange<opt::Module::const_inst_iterator> src_insts,
  527. opt::IteratorRange<opt::Module::const_inst_iterator> dst_insts) {
  528. // First, pool all instructions from each section and sort them.
  529. InstructionList sorted_src_insts = SortPreambleInstructions(src_, src_insts);
  530. InstructionList sorted_dst_insts = SortPreambleInstructions(dst_, dst_insts);
  531. // Then walk and match them.
  532. size_t src_cur = 0;
  533. size_t dst_cur = 0;
  534. while (src_cur < sorted_src_insts.size() &&
  535. dst_cur < sorted_dst_insts.size()) {
  536. const opt::Instruction* src_inst = sorted_src_insts[src_cur];
  537. const opt::Instruction* dst_inst = sorted_dst_insts[dst_cur];
  538. int compare = ComparePreambleInstructions(src_inst, dst_inst, src_, dst_);
  539. if (compare == 0) {
  540. id_map_.MapInsts(src_inst, dst_inst);
  541. }
  542. if (compare <= 0) {
  543. ++src_cur;
  544. }
  545. if (compare >= 0) {
  546. ++dst_cur;
  547. }
  548. }
  549. }
  550. InstructionList Differ::SortPreambleInstructions(
  551. const opt::Module* module,
  552. opt::IteratorRange<opt::Module::const_inst_iterator> insts) {
  553. InstructionList sorted;
  554. for (const opt::Instruction& inst : insts) {
  555. sorted.push_back(&inst);
  556. }
  557. std::sort(
  558. sorted.begin(), sorted.end(),
  559. [this, module](const opt::Instruction* a, const opt::Instruction* b) {
  560. return ComparePreambleInstructions(a, b, module, module) < 0;
  561. });
  562. return sorted;
  563. }
  564. int Differ::ComparePreambleInstructions(const opt::Instruction* a,
  565. const opt::Instruction* b,
  566. const opt::Module* src_inst_module,
  567. const opt::Module* dst_inst_module) {
  568. assert(a->opcode() == b->opcode());
  569. assert(!a->HasResultId());
  570. assert(!a->HasResultType());
  571. const uint32_t a_operand_count = a->NumOperands();
  572. const uint32_t b_operand_count = b->NumOperands();
  573. if (a_operand_count < b_operand_count) {
  574. return -1;
  575. }
  576. if (a_operand_count > b_operand_count) {
  577. return 1;
  578. }
  579. // Instead of comparing OpExecutionMode entry point ids as ids, compare them
  580. // through their corresponding execution model. This simplifies traversing
  581. // the sorted list of instructions between src and dst modules.
  582. if (a->opcode() == SpvOpExecutionMode) {
  583. const SpvExecutionModel src_model =
  584. GetExecutionModel(src_inst_module, a->GetOperand(0).AsId());
  585. const SpvExecutionModel dst_model =
  586. GetExecutionModel(dst_inst_module, b->GetOperand(0).AsId());
  587. if (src_model < dst_model) {
  588. return -1;
  589. }
  590. if (src_model > dst_model) {
  591. return 1;
  592. }
  593. }
  594. // Match every operand of the instruction.
  595. for (uint32_t operand_index = 0; operand_index < a_operand_count;
  596. ++operand_index) {
  597. const opt::Operand& a_operand = a->GetOperand(operand_index);
  598. const opt::Operand& b_operand = b->GetOperand(operand_index);
  599. if (a_operand.type < b_operand.type) {
  600. return -1;
  601. }
  602. if (a_operand.type > b_operand.type) {
  603. return 1;
  604. }
  605. assert(a_operand.words.size() == 1);
  606. assert(b_operand.words.size() == 1);
  607. switch (a_operand.type) {
  608. case SPV_OPERAND_TYPE_ID:
  609. // Don't compare ids, there can't be multiple instances of the
  610. // OpExecutionMode with different ids of the same execution model.
  611. break;
  612. case SPV_OPERAND_TYPE_TYPE_ID:
  613. case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID:
  614. case SPV_OPERAND_TYPE_SCOPE_ID:
  615. assert(false && "Unreachable");
  616. break;
  617. case SPV_OPERAND_TYPE_LITERAL_STRING: {
  618. int str_compare =
  619. strcmp(a_operand.AsString().c_str(), b_operand.AsString().c_str());
  620. if (str_compare != 0) {
  621. return str_compare;
  622. }
  623. break;
  624. }
  625. default:
  626. // Expect literal values to match.
  627. if (a_operand.words[0] < b_operand.words[0]) {
  628. return -1;
  629. }
  630. if (a_operand.words[0] > b_operand.words[0]) {
  631. return 1;
  632. }
  633. break;
  634. }
  635. }
  636. return 0;
  637. }
  638. void Differ::MatchDebugAndAnnotationInstructions(
  639. opt::IteratorRange<opt::Module::const_inst_iterator> src_insts,
  640. opt::IteratorRange<opt::Module::const_inst_iterator> dst_insts) {
  641. for (const opt::Instruction& src_inst : src_insts) {
  642. for (const opt::Instruction& dst_inst : dst_insts) {
  643. if (MappedSrcInst(&dst_inst) != nullptr) {
  644. continue;
  645. }
  646. // Map instructions as soon as they match. Debug and annotation
  647. // instructions are matched such that there can't be multiple matches.
  648. if (DoDebugAndAnnotationInstructionsMatch(&src_inst, &dst_inst)) {
  649. id_map_.MapInsts(&src_inst, &dst_inst);
  650. break;
  651. }
  652. }
  653. }
  654. }
  655. bool Differ::DoIdsMatch(uint32_t src_id, uint32_t dst_id) {
  656. assert(dst_id != 0);
  657. return id_map_.MappedDstId(src_id) == dst_id;
  658. }
  659. bool Differ::DoesOperandMatch(const opt::Operand& src_operand,
  660. const opt::Operand& dst_operand) {
  661. assert(src_operand.type == dst_operand.type);
  662. switch (src_operand.type) {
  663. case SPV_OPERAND_TYPE_ID:
  664. case SPV_OPERAND_TYPE_TYPE_ID:
  665. case SPV_OPERAND_TYPE_RESULT_ID:
  666. case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID:
  667. case SPV_OPERAND_TYPE_SCOPE_ID:
  668. // Match ids only if they are already matched in the id map.
  669. return DoIdsMatch(src_operand.AsId(), dst_operand.AsId());
  670. case SPV_OPERAND_TYPE_LITERAL_STRING:
  671. return src_operand.AsString() == dst_operand.AsString();
  672. default:
  673. // Otherwise expect them to match exactly.
  674. assert(src_operand.type != SPV_OPERAND_TYPE_LITERAL_STRING);
  675. if (src_operand.words.size() != dst_operand.words.size()) {
  676. return false;
  677. }
  678. for (size_t i = 0; i < src_operand.words.size(); ++i) {
  679. if (src_operand.words[i] != dst_operand.words[i]) {
  680. return false;
  681. }
  682. }
  683. return true;
  684. }
  685. }
  686. bool Differ::DoOperandsMatch(const opt::Instruction* src_inst,
  687. const opt::Instruction* dst_inst,
  688. uint32_t in_operand_index_start,
  689. uint32_t in_operand_count) {
  690. // Caller should have returned early for instructions with different opcode.
  691. assert(src_inst->opcode() == dst_inst->opcode());
  692. bool match = true;
  693. for (uint32_t i = 0; i < in_operand_count; ++i) {
  694. const uint32_t in_operand_index = in_operand_index_start + i;
  695. const opt::Operand& src_operand = src_inst->GetInOperand(in_operand_index);
  696. const opt::Operand& dst_operand = dst_inst->GetInOperand(in_operand_index);
  697. match = match && DoesOperandMatch(src_operand, dst_operand);
  698. }
  699. return match;
  700. }
  701. bool Differ::DoInstructionsMatch(const opt::Instruction* src_inst,
  702. const opt::Instruction* dst_inst) {
  703. // Check whether the two instructions are identical, that is the instructions
  704. // themselves are matched, every id is matched, and every other value is
  705. // identical.
  706. if (MappedDstInst(src_inst) != dst_inst) {
  707. return false;
  708. }
  709. assert(src_inst->opcode() == dst_inst->opcode());
  710. if (src_inst->NumOperands() != dst_inst->NumOperands()) {
  711. return false;
  712. }
  713. for (uint32_t operand_index = 0; operand_index < src_inst->NumOperands();
  714. ++operand_index) {
  715. const opt::Operand& src_operand = src_inst->GetOperand(operand_index);
  716. const opt::Operand& dst_operand = dst_inst->GetOperand(operand_index);
  717. if (!DoesOperandMatch(src_operand, dst_operand)) {
  718. return false;
  719. }
  720. }
  721. return true;
  722. }
  723. bool Differ::DoIdsMatchFuzzy(uint32_t src_id, uint32_t dst_id) {
  724. assert(dst_id != 0);
  725. const uint32_t mapped_dst_id = id_map_.MappedDstId(src_id);
  726. // Consider unmatched ids as a match. In function bodies, no result id is
  727. // matched yet and thus they are excluded from instruction matching when used
  728. // as parameters in subsequent instructions.
  729. if (mapped_dst_id == 0 || mapped_dst_id == dst_id) {
  730. return true;
  731. }
  732. // Int and Uint constants are interchangeable, match them in that case.
  733. if (IsConstantUint(src_id_to_, src_id) &&
  734. IsConstantUint(dst_id_to_, dst_id)) {
  735. return GetConstantUint(src_id_to_, src_id) ==
  736. GetConstantUint(dst_id_to_, dst_id);
  737. }
  738. return false;
  739. }
  740. bool Differ::DoesOperandMatchFuzzy(const opt::Operand& src_operand,
  741. const opt::Operand& dst_operand) {
  742. if (src_operand.type != dst_operand.type) {
  743. return false;
  744. }
  745. assert(src_operand.type != SPV_OPERAND_TYPE_RESULT_ID);
  746. assert(dst_operand.type != SPV_OPERAND_TYPE_RESULT_ID);
  747. switch (src_operand.type) {
  748. case SPV_OPERAND_TYPE_ID:
  749. case SPV_OPERAND_TYPE_TYPE_ID:
  750. case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID:
  751. case SPV_OPERAND_TYPE_SCOPE_ID:
  752. // Match id operands only if they are already matched in the id map.
  753. return DoIdsMatchFuzzy(src_operand.AsId(), dst_operand.AsId());
  754. default:
  755. // Otherwise allow everything to match.
  756. return true;
  757. }
  758. }
  759. bool Differ::DoInstructionsMatchFuzzy(const opt::Instruction* src_inst,
  760. const opt::Instruction* dst_inst) {
  761. // Similar to DoOperandsMatch, but only checks that ids that have already been
  762. // matched are identical. Ids that are unknown are allowed to match, as well
  763. // as any non-id operand.
  764. if (src_inst->opcode() != dst_inst->opcode()) {
  765. return false;
  766. }
  767. // For external instructions, make sure the set and opcode of the external
  768. // instruction matches too.
  769. if (src_inst->opcode() == SpvOpExtInst) {
  770. if (!DoOperandsMatch(src_inst, dst_inst, 0, 2)) {
  771. return false;
  772. }
  773. }
  774. assert(src_inst->HasResultType() == dst_inst->HasResultType());
  775. if (src_inst->HasResultType() &&
  776. !DoIdsMatchFuzzy(src_inst->type_id(), dst_inst->type_id())) {
  777. return false;
  778. }
  779. // TODO: allow some instructions to match with different instruction lengths,
  780. // for example OpImage* with additional operands.
  781. if (src_inst->NumInOperandWords() != dst_inst->NumInOperandWords()) {
  782. return false;
  783. }
  784. bool match = true;
  785. for (uint32_t in_operand_index = 0;
  786. in_operand_index < src_inst->NumInOperandWords(); ++in_operand_index) {
  787. const opt::Operand& src_operand = src_inst->GetInOperand(in_operand_index);
  788. const opt::Operand& dst_operand = dst_inst->GetInOperand(in_operand_index);
  789. match = match && DoesOperandMatchFuzzy(src_operand, dst_operand);
  790. }
  791. return match;
  792. }
  793. bool Differ::DoDebugAndAnnotationInstructionsMatch(
  794. const opt::Instruction* src_inst, const opt::Instruction* dst_inst) {
  795. if (src_inst->opcode() != dst_inst->opcode()) {
  796. return false;
  797. }
  798. switch (src_inst->opcode()) {
  799. case SpvOpString:
  800. case SpvOpSourceExtension:
  801. case SpvOpModuleProcessed:
  802. return DoesOperandMatch(src_inst->GetOperand(0), dst_inst->GetOperand(0));
  803. case SpvOpSource:
  804. return DoOperandsMatch(src_inst, dst_inst, 0, 2);
  805. case SpvOpSourceContinued:
  806. return true;
  807. case SpvOpName:
  808. return DoOperandsMatch(src_inst, dst_inst, 0, 1);
  809. case SpvOpMemberName:
  810. return DoOperandsMatch(src_inst, dst_inst, 0, 2);
  811. case SpvOpDecorate:
  812. return DoOperandsMatch(src_inst, dst_inst, 0, 2);
  813. case SpvOpMemberDecorate:
  814. return DoOperandsMatch(src_inst, dst_inst, 0, 3);
  815. case SpvOpExtInst:
  816. case SpvOpDecorationGroup:
  817. case SpvOpGroupDecorate:
  818. case SpvOpGroupMemberDecorate:
  819. return false;
  820. default:
  821. return false;
  822. }
  823. }
  824. bool Differ::AreVariablesMatchable(uint32_t src_id, uint32_t dst_id,
  825. uint32_t flexibility) {
  826. // Variables must match by their built-in decorations.
  827. uint32_t src_built_in_decoration = 0, dst_built_in_decoration = 0;
  828. const bool src_is_built_in = GetDecorationValue(
  829. src_id_to_, src_id, SpvDecorationBuiltIn, &src_built_in_decoration);
  830. const bool dst_is_built_in = GetDecorationValue(
  831. dst_id_to_, dst_id, SpvDecorationBuiltIn, &dst_built_in_decoration);
  832. if (src_is_built_in != dst_is_built_in) {
  833. return false;
  834. }
  835. if (src_is_built_in && src_built_in_decoration != dst_built_in_decoration) {
  836. return false;
  837. }
  838. // Check their types and storage classes.
  839. SpvStorageClass src_storage_class, dst_storage_class;
  840. const uint32_t src_type_id =
  841. GetVarTypeId(src_id_to_, src_id, &src_storage_class);
  842. const uint32_t dst_type_id =
  843. GetVarTypeId(dst_id_to_, dst_id, &dst_storage_class);
  844. if (!DoIdsMatch(src_type_id, dst_type_id)) {
  845. return false;
  846. }
  847. switch (flexibility) {
  848. case 0:
  849. if (src_storage_class != dst_storage_class) {
  850. return false;
  851. }
  852. break;
  853. case 1:
  854. if (src_storage_class != dst_storage_class) {
  855. // Allow one of the two to be Private while the other is Input or
  856. // Output, this allows matching in/out variables that have been turned
  857. // global as part of linking two stages (as done in ANGLE).
  858. const bool src_is_io = src_storage_class == SpvStorageClassInput ||
  859. src_storage_class == SpvStorageClassOutput;
  860. const bool dst_is_io = dst_storage_class == SpvStorageClassInput ||
  861. dst_storage_class == SpvStorageClassOutput;
  862. const bool src_is_private = src_storage_class == SpvStorageClassPrivate;
  863. const bool dst_is_private = dst_storage_class == SpvStorageClassPrivate;
  864. if (!((src_is_io && dst_is_private) || (src_is_private && dst_is_io))) {
  865. return false;
  866. }
  867. }
  868. break;
  869. default:
  870. assert(false && "Unreachable");
  871. return false;
  872. }
  873. // TODO: Is there any other way to check compatiblity of the variables? It's
  874. // easy to tell when the variables definitely don't match, but there's little
  875. // information that can be used for a definite match.
  876. return true;
  877. }
  878. bool Differ::MatchOpTypeStruct(const opt::Instruction* src_inst,
  879. const opt::Instruction* dst_inst,
  880. uint32_t flexibility) {
  881. const uint32_t src_type_id = src_inst->result_id();
  882. const uint32_t dst_type_id = dst_inst->result_id();
  883. bool src_has_name = false, dst_has_name = false;
  884. std::string src_name = GetName(src_id_to_, src_type_id, &src_has_name);
  885. std::string dst_name = GetName(dst_id_to_, dst_type_id, &dst_has_name);
  886. // If debug info is present, always match the structs by name.
  887. if (src_has_name && dst_has_name) {
  888. if (src_name != dst_name) {
  889. return false;
  890. }
  891. // For gl_PerVertex, find the type pointer of this type (array) and make
  892. // sure the storage classes of src and dst match; geometry and tessellation
  893. // shaders have two instances of gl_PerVertex.
  894. if (src_name == "gl_PerVertex") {
  895. return MatchPerVertexType(src_type_id, dst_type_id);
  896. }
  897. return true;
  898. }
  899. // If debug info is not present, match the structs by their type.
  900. // For gl_PerVertex, find the type pointer of this type (array) and match by
  901. // storage class. The gl_PerVertex struct is itself found by the BuiltIn
  902. // decorations applied to its members.
  903. const bool src_is_per_vertex = IsPerVertexType(src_id_to_, src_type_id);
  904. const bool dst_is_per_vertex = IsPerVertexType(dst_id_to_, dst_type_id);
  905. if (src_is_per_vertex != dst_is_per_vertex) {
  906. return false;
  907. }
  908. if (src_is_per_vertex) {
  909. return MatchPerVertexType(src_type_id, dst_type_id);
  910. }
  911. switch (flexibility) {
  912. case 0:
  913. if (src_inst->NumInOperandWords() != dst_inst->NumInOperandWords()) {
  914. return false;
  915. }
  916. return DoOperandsMatch(src_inst, dst_inst, 0,
  917. src_inst->NumInOperandWords());
  918. case 1:
  919. // TODO: match by taking a diff of the fields, and see if there's a >75%
  920. // match. Need to then make sure OpMemberName, OpMemberDecorate,
  921. // OpAccessChain etc are aware of the struct field matching.
  922. return false;
  923. default:
  924. assert(false && "Unreachable");
  925. return false;
  926. }
  927. }
  928. bool Differ::MatchOpConstant(const opt::Instruction* src_inst,
  929. const opt::Instruction* dst_inst,
  930. uint32_t flexibility) {
  931. // The constants' type must match. In flexibility == 1, match constants of
  932. // int and uint, as they are generally interchangeable.
  933. switch (flexibility) {
  934. case 0:
  935. if (!DoesOperandMatch(src_inst->GetOperand(0), dst_inst->GetOperand(0))) {
  936. return false;
  937. }
  938. break;
  939. case 1:
  940. if (!IsIntType(src_id_to_, src_inst->type_id()) ||
  941. !IsIntType(dst_id_to_, dst_inst->type_id())) {
  942. return false;
  943. }
  944. break;
  945. default:
  946. assert(false && "Unreachable");
  947. return false;
  948. }
  949. const opt::Operand& src_value_operand = src_inst->GetOperand(2);
  950. const opt::Operand& dst_value_operand = dst_inst->GetOperand(2);
  951. const uint64_t src_value = src_value_operand.AsLiteralUint64();
  952. const uint64_t dst_value = dst_value_operand.AsLiteralUint64();
  953. // If values are identical, it's a match.
  954. if (src_value == dst_value) {
  955. return true;
  956. }
  957. // Otherwise, only allow flexibility for float types.
  958. if (IsFloatType(src_id_to_, src_inst->type_id()) && flexibility == 1) {
  959. // Tolerance is:
  960. //
  961. // - For float: allow 4 bits of mantissa as error
  962. // - For double: allow 6 bits of mantissa as error
  963. //
  964. // TODO: the above values are arbitrary and a placeholder; investigate the
  965. // amount of error resulting from using `printf("%f", f)` and `printf("%lf",
  966. // d)` and having glslang parse them.
  967. const uint64_t tolerance = src_value_operand.words.size() == 1 ? 16 : 64;
  968. return src_value - dst_value < tolerance ||
  969. dst_value - src_value < tolerance;
  970. }
  971. return false;
  972. }
  973. bool Differ::MatchOpSpecConstant(const opt::Instruction* src_inst,
  974. const opt::Instruction* dst_inst) {
  975. const uint32_t src_id = src_inst->result_id();
  976. const uint32_t dst_id = dst_inst->result_id();
  977. bool src_has_name = false, dst_has_name = false;
  978. std::string src_name = GetName(src_id_to_, src_id, &src_has_name);
  979. std::string dst_name = GetName(dst_id_to_, dst_id, &dst_has_name);
  980. // If debug info is present, always match the spec consts by name.
  981. if (src_has_name && dst_has_name) {
  982. return src_name == dst_name;
  983. }
  984. // Otherwise, match them by SpecId.
  985. uint32_t src_spec_id, dst_spec_id;
  986. if (GetDecorationValue(src_id_to_, src_id, SpvDecorationSpecId,
  987. &src_spec_id) &&
  988. GetDecorationValue(dst_id_to_, dst_id, SpvDecorationSpecId,
  989. &dst_spec_id)) {
  990. return src_spec_id == dst_spec_id;
  991. }
  992. // There is no spec id, this is not valid.
  993. assert(false && "Unreachable");
  994. return false;
  995. }
  996. bool Differ::MatchOpVariable(const opt::Instruction* src_inst,
  997. const opt::Instruction* dst_inst,
  998. uint32_t flexibility) {
  999. const uint32_t src_id = src_inst->result_id();
  1000. const uint32_t dst_id = dst_inst->result_id();
  1001. const bool src_is_pervertex = IsPerVertexVariable(src_id_to_, src_id);
  1002. const bool dst_is_pervertex = IsPerVertexVariable(dst_id_to_, dst_id);
  1003. // For gl_PerVertex, make sure the input and output instances are matched
  1004. // correctly.
  1005. if (src_is_pervertex != dst_is_pervertex) {
  1006. return false;
  1007. }
  1008. if (src_is_pervertex) {
  1009. return MatchPerVertexVariable(src_inst, dst_inst);
  1010. }
  1011. bool src_has_name = false, dst_has_name = false;
  1012. std::string src_name = GetName(src_id_to_, src_id, &src_has_name);
  1013. std::string dst_name = GetName(dst_id_to_, dst_id, &dst_has_name);
  1014. // If debug info is present, always match the variables by name.
  1015. if (src_has_name && dst_has_name) {
  1016. return src_name == dst_name;
  1017. }
  1018. // If debug info is not present, see if the variables can be matched by their
  1019. // built-in decorations.
  1020. uint32_t src_built_in_decoration;
  1021. const bool src_is_built_in = GetDecorationValue(
  1022. src_id_to_, src_id, SpvDecorationBuiltIn, &src_built_in_decoration);
  1023. if (src_is_built_in && AreVariablesMatchable(src_id, dst_id, flexibility)) {
  1024. return true;
  1025. }
  1026. SpvStorageClass src_storage_class, dst_storage_class;
  1027. GetVarTypeId(src_id_to_, src_id, &src_storage_class);
  1028. GetVarTypeId(dst_id_to_, dst_id, &dst_storage_class);
  1029. if (src_storage_class != dst_storage_class) {
  1030. return false;
  1031. }
  1032. // If variables are decorated with set/binding, match by the value of those
  1033. // decorations.
  1034. if (!options_.ignore_set_binding) {
  1035. uint32_t src_set = 0, dst_set = 0;
  1036. uint32_t src_binding = 0, dst_binding = 0;
  1037. const bool src_has_set = GetDecorationValue(
  1038. src_id_to_, src_id, SpvDecorationDescriptorSet, &src_set);
  1039. const bool dst_has_set = GetDecorationValue(
  1040. dst_id_to_, dst_id, SpvDecorationDescriptorSet, &dst_set);
  1041. const bool src_has_binding =
  1042. GetDecorationValue(src_id_to_, src_id, SpvDecorationBinding, &src_set);
  1043. const bool dst_has_binding =
  1044. GetDecorationValue(dst_id_to_, dst_id, SpvDecorationBinding, &dst_set);
  1045. if (src_has_set && dst_has_set && src_has_binding && dst_has_binding) {
  1046. return src_set == dst_set && src_binding == dst_binding;
  1047. }
  1048. }
  1049. // If variables are decorated with location, match by the value of that
  1050. // decoration.
  1051. if (!options_.ignore_location) {
  1052. uint32_t src_location, dst_location;
  1053. const bool src_has_location = GetDecorationValue(
  1054. src_id_to_, src_id, SpvDecorationLocation, &src_location);
  1055. const bool dst_has_location = GetDecorationValue(
  1056. dst_id_to_, dst_id, SpvDecorationLocation, &dst_location);
  1057. if (src_has_location && dst_has_location) {
  1058. return src_location == dst_location;
  1059. }
  1060. }
  1061. // Currently, there's no other way to match variables.
  1062. return false;
  1063. }
  1064. bool Differ::MatchPerVertexType(uint32_t src_type_id, uint32_t dst_type_id) {
  1065. // For gl_PerVertex, find the type pointer of this type (array) and make sure
  1066. // the storage classes of src and dst match; geometry and tessellation shaders
  1067. // have two instances of gl_PerVertex.
  1068. SpvStorageClass src_storage_class =
  1069. GetPerVertexStorageClass(src_, src_type_id);
  1070. SpvStorageClass dst_storage_class =
  1071. GetPerVertexStorageClass(dst_, dst_type_id);
  1072. assert(src_storage_class == SpvStorageClassInput ||
  1073. src_storage_class == SpvStorageClassOutput);
  1074. assert(dst_storage_class == SpvStorageClassInput ||
  1075. dst_storage_class == SpvStorageClassOutput);
  1076. return src_storage_class == dst_storage_class;
  1077. }
  1078. bool Differ::MatchPerVertexVariable(const opt::Instruction* src_inst,
  1079. const opt::Instruction* dst_inst) {
  1080. SpvStorageClass src_storage_class =
  1081. SpvStorageClass(src_inst->GetInOperand(0).words[0]);
  1082. SpvStorageClass dst_storage_class =
  1083. SpvStorageClass(dst_inst->GetInOperand(0).words[0]);
  1084. return src_storage_class == dst_storage_class;
  1085. }
  1086. InstructionList Differ::GetFunctionBody(opt::IRContext* context,
  1087. opt::Function& function) {
  1088. // Canonicalize the blocks of the function to produce better diff, for example
  1089. // to not produce any diff if the src and dst have the same switch/case blocks
  1090. // but with the cases simply reordered.
  1091. std::list<opt::BasicBlock*> order;
  1092. context->cfg()->ComputeStructuredOrder(&function, &*function.begin(), &order);
  1093. // Go over the instructions of the function and add the instructions to a flat
  1094. // list to simplify future iterations.
  1095. InstructionList body;
  1096. for (opt::BasicBlock* block : order) {
  1097. block->ForEachInst(
  1098. [&body](const opt::Instruction* inst) { body.push_back(inst); }, true);
  1099. }
  1100. body.push_back(function.EndInst());
  1101. return body;
  1102. }
  1103. InstructionList Differ::GetFunctionHeader(const opt::Function& function) {
  1104. // Go over the instructions of the function and add the header instructions to
  1105. // a flat list to simplify diff generation.
  1106. InstructionList body;
  1107. function.WhileEachInst(
  1108. [&body](const opt::Instruction* inst) {
  1109. if (inst->opcode() == SpvOpLabel) {
  1110. return false;
  1111. }
  1112. body.push_back(inst);
  1113. return true;
  1114. },
  1115. true, true);
  1116. return body;
  1117. }
  1118. void Differ::GetFunctionBodies(opt::IRContext* context, FunctionMap* functions,
  1119. FunctionInstMap* function_insts) {
  1120. for (opt::Function& function : *context->module()) {
  1121. uint32_t id = function.result_id();
  1122. assert(functions->find(id) == functions->end());
  1123. assert(function_insts->find(id) == function_insts->end());
  1124. (*functions)[id] = &function;
  1125. InstructionList body = GetFunctionBody(context, function);
  1126. (*function_insts)[id] = std::move(body);
  1127. }
  1128. }
  1129. void Differ::GetFunctionHeaderInstructions(const opt::Module* module,
  1130. FunctionInstMap* function_insts) {
  1131. for (opt::Function& function : *module) {
  1132. InstructionList body = GetFunctionHeader(function);
  1133. (*function_insts)[function.result_id()] = std::move(body);
  1134. }
  1135. }
  1136. template <typename T>
  1137. void Differ::GroupIds(
  1138. const IdGroup& functions, bool is_src, std::map<T, IdGroup>* groups,
  1139. std::function<T(const IdInstructions, uint32_t)> get_group) {
  1140. assert(groups->empty());
  1141. const IdInstructions& id_to = is_src ? src_id_to_ : dst_id_to_;
  1142. for (const uint32_t func_id : functions) {
  1143. // Don't include functions that are already matched, for example through
  1144. // OpEntryPoint.
  1145. const bool is_matched =
  1146. is_src ? id_map_.IsSrcMapped(func_id) : id_map_.IsDstMapped(func_id);
  1147. if (is_matched) {
  1148. continue;
  1149. }
  1150. T group = get_group(id_to, func_id);
  1151. (*groups)[group].push_back(func_id);
  1152. }
  1153. }
  1154. void Differ::BestEffortMatchFunctions(const IdGroup& src_func_ids,
  1155. const IdGroup& dst_func_ids,
  1156. const FunctionInstMap& src_func_insts,
  1157. const FunctionInstMap& dst_func_insts) {
  1158. struct MatchResult {
  1159. uint32_t src_id;
  1160. uint32_t dst_id;
  1161. DiffMatch src_match;
  1162. DiffMatch dst_match;
  1163. float match_rate;
  1164. bool operator<(const MatchResult& other) const {
  1165. return match_rate > other.match_rate;
  1166. }
  1167. };
  1168. std::vector<MatchResult> all_match_results;
  1169. for (const uint32_t src_func_id : src_func_ids) {
  1170. if (id_map_.IsSrcMapped(src_func_id)) {
  1171. continue;
  1172. }
  1173. const std::string src_name = GetFunctionName(src_id_to_, src_func_id);
  1174. for (const uint32_t dst_func_id : dst_func_ids) {
  1175. if (id_map_.IsDstMapped(dst_func_id)) {
  1176. continue;
  1177. }
  1178. // Don't match functions that are named, but the names are different.
  1179. const std::string dst_name = GetFunctionName(dst_id_to_, dst_func_id);
  1180. if (src_name != "" && dst_name != "" && src_name != dst_name) {
  1181. continue;
  1182. }
  1183. DiffMatch src_match_result, dst_match_result;
  1184. float match_rate = MatchFunctionBodies(
  1185. src_func_insts.at(src_func_id), dst_func_insts.at(dst_func_id),
  1186. &src_match_result, &dst_match_result);
  1187. // Only consider the functions a match if there's at least 60% match.
  1188. // This is an arbitrary limit that should be tuned.
  1189. constexpr float pass_match_rate = 0.6f;
  1190. if (match_rate >= pass_match_rate) {
  1191. all_match_results.emplace_back(
  1192. MatchResult{src_func_id, dst_func_id, std::move(src_match_result),
  1193. std::move(dst_match_result), match_rate});
  1194. }
  1195. }
  1196. }
  1197. std::sort(all_match_results.begin(), all_match_results.end());
  1198. for (const MatchResult& match_result : all_match_results) {
  1199. if (id_map_.IsSrcMapped(match_result.src_id) ||
  1200. id_map_.IsDstMapped(match_result.dst_id)) {
  1201. continue;
  1202. }
  1203. id_map_.MapIds(match_result.src_id, match_result.dst_id);
  1204. MatchIdsInFunctionBodies(src_func_insts.at(match_result.src_id),
  1205. dst_func_insts.at(match_result.dst_id),
  1206. match_result.src_match, match_result.dst_match, 0);
  1207. }
  1208. }
  1209. void Differ::GroupIdsByName(const IdGroup& functions, bool is_src,
  1210. IdGroupMapByName* groups) {
  1211. GroupIds<std::string>(functions, is_src, groups,
  1212. [this](const IdInstructions& id_to, uint32_t func_id) {
  1213. return GetFunctionName(id_to, func_id);
  1214. });
  1215. }
  1216. void Differ::GroupIdsByTypeId(const IdGroup& functions, bool is_src,
  1217. IdGroupMapByTypeId* groups) {
  1218. GroupIds<uint32_t>(functions, is_src, groups,
  1219. [this](const IdInstructions& id_to, uint32_t func_id) {
  1220. return GetInst(id_to, func_id)->type_id();
  1221. });
  1222. }
  1223. void Differ::MatchFunctionParamIds(const opt::Function* src_func,
  1224. const opt::Function* dst_func) {
  1225. IdGroup src_params;
  1226. IdGroup dst_params;
  1227. src_func->ForEachParam(
  1228. [&src_params](const opt::Instruction* param) {
  1229. src_params.push_back(param->result_id());
  1230. },
  1231. false);
  1232. dst_func->ForEachParam(
  1233. [&dst_params](const opt::Instruction* param) {
  1234. dst_params.push_back(param->result_id());
  1235. },
  1236. false);
  1237. IdGroupMapByName src_param_groups;
  1238. IdGroupMapByName dst_param_groups;
  1239. GroupIdsByName(src_params, true, &src_param_groups);
  1240. GroupIdsByName(dst_params, false, &dst_param_groups);
  1241. // Match parameters with identical names.
  1242. for (const auto& src_param_group : src_param_groups) {
  1243. const std::string& name = src_param_group.first;
  1244. const IdGroup& src_group = src_param_group.second;
  1245. if (name == "") {
  1246. continue;
  1247. }
  1248. const IdGroup& dst_group = dst_param_groups[name];
  1249. // There shouldn't be two parameters with the same name, so the ids should
  1250. // match. There is nothing restricting the SPIR-V however to have two
  1251. // parameters with the same name, so be resilient against that.
  1252. if (src_group.size() == 1 && dst_group.size() == 1) {
  1253. id_map_.MapIds(src_group[0], dst_group[0]);
  1254. }
  1255. }
  1256. // Then match the parameters by their type. If there are multiple of them,
  1257. // match them by their order.
  1258. IdGroupMapByTypeId src_param_groups_by_type_id;
  1259. IdGroupMapByTypeId dst_param_groups_by_type_id;
  1260. GroupIdsByTypeId(src_params, true, &src_param_groups_by_type_id);
  1261. GroupIdsByTypeId(dst_params, false, &dst_param_groups_by_type_id);
  1262. for (const auto& src_param_group_by_type_id : src_param_groups_by_type_id) {
  1263. const uint32_t type_id = src_param_group_by_type_id.first;
  1264. const IdGroup& src_group_by_type_id = src_param_group_by_type_id.second;
  1265. const IdGroup& dst_group_by_type_id = dst_param_groups_by_type_id[type_id];
  1266. const size_t shared_param_count =
  1267. std::min(src_group_by_type_id.size(), dst_group_by_type_id.size());
  1268. for (size_t param_index = 0; param_index < shared_param_count;
  1269. ++param_index) {
  1270. id_map_.MapIds(src_group_by_type_id[0], dst_group_by_type_id[0]);
  1271. }
  1272. }
  1273. }
  1274. float Differ::MatchFunctionBodies(const InstructionList& src_body,
  1275. const InstructionList& dst_body,
  1276. DiffMatch* src_match_result,
  1277. DiffMatch* dst_match_result) {
  1278. LongestCommonSubsequence<std::vector<const opt::Instruction*>> lcs(src_body,
  1279. dst_body);
  1280. size_t best_match_length = lcs.Get<const opt::Instruction*>(
  1281. [this](const opt::Instruction* src_inst,
  1282. const opt::Instruction* dst_inst) {
  1283. return DoInstructionsMatchFuzzy(src_inst, dst_inst);
  1284. },
  1285. src_match_result, dst_match_result);
  1286. // TODO: take the gaps in between matches and match those again with a relaxed
  1287. // instruction-and-type-only comparison. This can produce a better diff for
  1288. // example if an array index is changed, causing the OpAccessChain id to not
  1289. // match and subsequently every operation that's derived from that id.
  1290. // Usually this mismatch cascades until the next OpStore which doesn't produce
  1291. // an id.
  1292. return static_cast<float>(best_match_length) * 2.0f /
  1293. static_cast<float>(src_body.size() + dst_body.size());
  1294. }
  1295. void Differ::MatchIdsInFunctionBodies(const InstructionList& src_body,
  1296. const InstructionList& dst_body,
  1297. const DiffMatch& src_match_result,
  1298. const DiffMatch& dst_match_result,
  1299. uint32_t flexibility) {
  1300. size_t src_cur = 0;
  1301. size_t dst_cur = 0;
  1302. while (src_cur < src_body.size() && dst_cur < dst_body.size()) {
  1303. if (src_match_result[src_cur] && dst_match_result[dst_cur]) {
  1304. // Match instructions the src and dst instructions.
  1305. //
  1306. // TODO: count the matchings between variables discovered this way and
  1307. // choose the "best match" after all functions have been diffed and all
  1308. // instructions analyzed.
  1309. const opt::Instruction* src_inst = src_body[src_cur++];
  1310. const opt::Instruction* dst_inst = dst_body[dst_cur++];
  1311. // Record the matching between the instructions. This is done only once
  1312. // (hence flexibility == 0). Calls with non-zero flexibility values will
  1313. // only deal with matching other ids based on the operands.
  1314. if (flexibility == 0) {
  1315. id_map_.MapInsts(src_inst, dst_inst);
  1316. }
  1317. // Match any unmatched variables referenced by the instructions.
  1318. MatchVariablesUsedByMatchedInstructions(src_inst, dst_inst, flexibility);
  1319. continue;
  1320. }
  1321. if (!src_match_result[src_cur]) {
  1322. ++src_cur;
  1323. }
  1324. if (!dst_match_result[dst_cur]) {
  1325. ++dst_cur;
  1326. }
  1327. }
  1328. }
  1329. void Differ::MatchVariablesUsedByMatchedInstructions(
  1330. const opt::Instruction* src_inst, const opt::Instruction* dst_inst,
  1331. uint32_t flexibility) {
  1332. // For OpAccessChain, OpLoad and OpStore instructions that reference unmatched
  1333. // variables, match them as a best effort.
  1334. assert(src_inst->opcode() == dst_inst->opcode());
  1335. switch (src_inst->opcode()) {
  1336. default:
  1337. // TODO: match functions based on OpFunctionCall?
  1338. break;
  1339. case SpvOpAccessChain:
  1340. case SpvOpInBoundsAccessChain:
  1341. case SpvOpPtrAccessChain:
  1342. case SpvOpInBoundsPtrAccessChain:
  1343. case SpvOpLoad:
  1344. case SpvOpStore:
  1345. const uint32_t src_pointer_id = src_inst->GetInOperand(0).AsId();
  1346. const uint32_t dst_pointer_id = dst_inst->GetInOperand(0).AsId();
  1347. if (IsVariable(src_id_to_, src_pointer_id) &&
  1348. IsVariable(dst_id_to_, dst_pointer_id) &&
  1349. !id_map_.IsSrcMapped(src_pointer_id) &&
  1350. !id_map_.IsDstMapped(dst_pointer_id) &&
  1351. AreVariablesMatchable(src_pointer_id, dst_pointer_id, flexibility)) {
  1352. id_map_.MapIds(src_pointer_id, dst_pointer_id);
  1353. }
  1354. break;
  1355. }
  1356. }
  1357. const opt::Instruction* Differ::GetInst(const IdInstructions& id_to,
  1358. uint32_t id) {
  1359. assert(id != 0);
  1360. assert(id < id_to.inst_map_.size());
  1361. const opt::Instruction* inst = id_to.inst_map_[id];
  1362. assert(inst != nullptr);
  1363. return inst;
  1364. }
  1365. uint32_t Differ::GetConstantUint(const IdInstructions& id_to,
  1366. uint32_t constant_id) {
  1367. const opt::Instruction* constant_inst = GetInst(id_to, constant_id);
  1368. assert(constant_inst->opcode() == SpvOpConstant);
  1369. assert(GetInst(id_to, constant_inst->type_id())->opcode() == SpvOpTypeInt);
  1370. return constant_inst->GetInOperand(0).words[0];
  1371. }
  1372. SpvExecutionModel Differ::GetExecutionModel(const opt::Module* module,
  1373. uint32_t entry_point_id) {
  1374. for (const opt::Instruction& inst : module->entry_points()) {
  1375. assert(inst.opcode() == SpvOpEntryPoint);
  1376. if (inst.GetOperand(1).AsId() == entry_point_id) {
  1377. return SpvExecutionModel(inst.GetOperand(0).words[0]);
  1378. }
  1379. }
  1380. assert(false && "Unreachable");
  1381. return SpvExecutionModel(0xFFF);
  1382. }
  1383. std::string Differ::GetName(const IdInstructions& id_to, uint32_t id,
  1384. bool* has_name) {
  1385. assert(id != 0);
  1386. assert(id < id_to.name_map_.size());
  1387. for (const opt::Instruction* inst : id_to.name_map_[id]) {
  1388. if (inst->opcode() == SpvOpName) {
  1389. *has_name = true;
  1390. return inst->GetOperand(1).AsString();
  1391. }
  1392. }
  1393. *has_name = false;
  1394. return "";
  1395. }
  1396. std::string Differ::GetFunctionName(const IdInstructions& id_to, uint32_t id) {
  1397. bool has_name = false;
  1398. std::string name = GetName(id_to, id, &has_name);
  1399. if (!has_name) {
  1400. return "";
  1401. }
  1402. // Remove args from the name
  1403. return name.substr(0, name.find('('));
  1404. }
  1405. uint32_t Differ::GetVarTypeId(const IdInstructions& id_to, uint32_t var_id,
  1406. SpvStorageClass* storage_class) {
  1407. const opt::Instruction* var_inst = GetInst(id_to, var_id);
  1408. assert(var_inst->opcode() == SpvOpVariable);
  1409. *storage_class = SpvStorageClass(var_inst->GetInOperand(0).words[0]);
  1410. // Get the type pointer from the variable.
  1411. const uint32_t type_pointer_id = var_inst->type_id();
  1412. const opt::Instruction* type_pointer_inst = GetInst(id_to, type_pointer_id);
  1413. // Get the type from the type pointer.
  1414. return type_pointer_inst->GetInOperand(1).AsId();
  1415. }
  1416. bool Differ::GetDecorationValue(const IdInstructions& id_to, uint32_t id,
  1417. SpvDecoration decoration,
  1418. uint32_t* decoration_value) {
  1419. assert(id != 0);
  1420. assert(id < id_to.decoration_map_.size());
  1421. for (const opt::Instruction* inst : id_to.decoration_map_[id]) {
  1422. if (inst->opcode() == SpvOpDecorate && inst->GetOperand(0).AsId() == id &&
  1423. inst->GetOperand(1).words[0] == decoration) {
  1424. *decoration_value = inst->GetOperand(2).words[0];
  1425. return true;
  1426. }
  1427. }
  1428. return false;
  1429. }
  1430. bool Differ::IsIntType(const IdInstructions& id_to, uint32_t type_id) {
  1431. return IsOp(id_to, type_id, SpvOpTypeInt);
  1432. #if 0
  1433. const opt::Instruction *type_inst = GetInst(id_to, type_id);
  1434. return type_inst->opcode() == SpvOpTypeInt && type_inst->GetInOperand(1).words[0] != 0;
  1435. #endif
  1436. }
  1437. #if 0
  1438. bool Differ::IsUintType(const IdInstructions& id_to, uint32_t type_id) {
  1439. const opt::Instruction *type_inst = GetInst(id_to, type_id);
  1440. return type_inst->opcode() == SpvOpTypeInt && type_inst->GetInOperand(1).words[0] == 0;
  1441. }
  1442. #endif
  1443. bool Differ::IsFloatType(const IdInstructions& id_to, uint32_t type_id) {
  1444. return IsOp(id_to, type_id, SpvOpTypeFloat);
  1445. }
  1446. bool Differ::IsConstantUint(const IdInstructions& id_to, uint32_t id) {
  1447. const opt::Instruction* constant_inst = GetInst(id_to, id);
  1448. if (constant_inst->opcode() != SpvOpConstant) {
  1449. return false;
  1450. }
  1451. const opt::Instruction* type_inst = GetInst(id_to, constant_inst->type_id());
  1452. return type_inst->opcode() == SpvOpTypeInt;
  1453. }
  1454. bool Differ::IsVariable(const IdInstructions& id_to, uint32_t pointer_id) {
  1455. return IsOp(id_to, pointer_id, SpvOpVariable);
  1456. }
  1457. bool Differ::IsOp(const IdInstructions& id_to, uint32_t id, SpvOp op) {
  1458. return GetInst(id_to, id)->opcode() == op;
  1459. }
  1460. bool Differ::IsPerVertexType(const IdInstructions& id_to, uint32_t type_id) {
  1461. assert(type_id != 0);
  1462. assert(type_id < id_to.decoration_map_.size());
  1463. for (const opt::Instruction* inst : id_to.decoration_map_[type_id]) {
  1464. if (inst->opcode() == SpvOpMemberDecorate &&
  1465. inst->GetOperand(0).AsId() == type_id &&
  1466. inst->GetOperand(2).words[0] == SpvDecorationBuiltIn) {
  1467. SpvBuiltIn built_in = SpvBuiltIn(inst->GetOperand(3).words[0]);
  1468. // Only gl_PerVertex can have, and it can only have, the following
  1469. // built-in decorations.
  1470. return built_in == SpvBuiltInPosition ||
  1471. built_in == SpvBuiltInPointSize ||
  1472. built_in == SpvBuiltInClipDistance ||
  1473. built_in == SpvBuiltInCullDistance;
  1474. }
  1475. }
  1476. return false;
  1477. }
  1478. bool Differ::IsPerVertexVariable(const IdInstructions& id_to, uint32_t var_id) {
  1479. // Get the type from the type pointer.
  1480. SpvStorageClass storage_class;
  1481. uint32_t type_id = GetVarTypeId(id_to, var_id, &storage_class);
  1482. const opt::Instruction* type_inst = GetInst(id_to, type_id);
  1483. // If array, get the element type.
  1484. if (type_inst->opcode() == SpvOpTypeArray) {
  1485. type_id = type_inst->GetInOperand(0).AsId();
  1486. }
  1487. // Now check if the type is gl_PerVertex.
  1488. return IsPerVertexType(id_to, type_id);
  1489. }
  1490. SpvStorageClass Differ::GetPerVertexStorageClass(const opt::Module* module,
  1491. uint32_t type_id) {
  1492. for (const opt::Instruction& inst : module->types_values()) {
  1493. switch (inst.opcode()) {
  1494. case SpvOpTypeArray:
  1495. // The gl_PerVertex instance could be an array, look for a variable of
  1496. // the array type instead.
  1497. if (inst.GetInOperand(0).AsId() == type_id) {
  1498. type_id = inst.result_id();
  1499. }
  1500. break;
  1501. case SpvOpTypePointer:
  1502. // Find the storage class of the pointer to this type.
  1503. if (inst.GetInOperand(1).AsId() == type_id) {
  1504. return SpvStorageClass(inst.GetInOperand(0).words[0]);
  1505. }
  1506. break;
  1507. default:
  1508. break;
  1509. }
  1510. }
  1511. // gl_PerVertex is declared, but is unused. Return either of Input or Output
  1512. // classes just so it matches one in the other module. This should be highly
  1513. // unlikely, perhaps except for ancient GS-used-to-emulate-CS scenarios.
  1514. return SpvStorageClassOutput;
  1515. }
  1516. spv_ext_inst_type_t Differ::GetExtInstType(const IdInstructions& id_to,
  1517. uint32_t set_id) {
  1518. const opt::Instruction* set_inst = GetInst(id_to, set_id);
  1519. return spvExtInstImportTypeGet(set_inst->GetInOperand(0).AsString().c_str());
  1520. }
  1521. spv_number_kind_t Differ::GetNumberKind(const IdInstructions& id_to,
  1522. const opt::Instruction& inst,
  1523. uint32_t operand_index,
  1524. uint32_t* number_bit_width) {
  1525. const opt::Operand& operand = inst.GetOperand(operand_index);
  1526. *number_bit_width = 0;
  1527. // A very limited version of Parser::parseOperand.
  1528. switch (operand.type) {
  1529. case SPV_OPERAND_TYPE_LITERAL_INTEGER:
  1530. case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER:
  1531. // Always unsigned integers.
  1532. *number_bit_width = 32;
  1533. return SPV_NUMBER_UNSIGNED_INT;
  1534. case SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER:
  1535. case SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER:
  1536. switch (inst.opcode()) {
  1537. case SpvOpSwitch:
  1538. case SpvOpConstant:
  1539. case SpvOpSpecConstant:
  1540. // Same kind of number as the selector (OpSwitch) or the type
  1541. // (Op*Constant).
  1542. return GetTypeNumberKind(id_to, inst.GetOperand(0).AsId(),
  1543. number_bit_width);
  1544. default:
  1545. assert(false && "Unreachable");
  1546. break;
  1547. }
  1548. break;
  1549. default:
  1550. break;
  1551. }
  1552. return SPV_NUMBER_NONE;
  1553. }
  1554. spv_number_kind_t Differ::GetTypeNumberKind(const IdInstructions& id_to,
  1555. uint32_t id,
  1556. uint32_t* number_bit_width) {
  1557. const opt::Instruction* type_inst = GetInst(id_to, id);
  1558. if (!spvOpcodeIsScalarType(type_inst->opcode())) {
  1559. type_inst = GetInst(id_to, type_inst->type_id());
  1560. }
  1561. switch (type_inst->opcode()) {
  1562. case SpvOpTypeInt:
  1563. *number_bit_width = type_inst->GetOperand(1).words[0];
  1564. return type_inst->GetOperand(2).words[0] == 0 ? SPV_NUMBER_UNSIGNED_INT
  1565. : SPV_NUMBER_SIGNED_INT;
  1566. break;
  1567. case SpvOpTypeFloat:
  1568. *number_bit_width = type_inst->GetOperand(1).words[0];
  1569. return SPV_NUMBER_FLOATING;
  1570. default:
  1571. assert(false && "Unreachable");
  1572. return SPV_NUMBER_NONE;
  1573. }
  1574. }
  1575. void Differ::MatchCapabilities() {
  1576. MatchPreambleInstructions(src_->capabilities(), dst_->capabilities());
  1577. }
  1578. void Differ::MatchExtensions() {
  1579. MatchPreambleInstructions(src_->extensions(), dst_->extensions());
  1580. }
  1581. void Differ::MatchExtInstImportIds() {
  1582. // Bunch all of this section's ids as potential matches.
  1583. PotentialIdMap potential_id_map;
  1584. auto get_result_id = [](const opt::Instruction& inst) {
  1585. return inst.result_id();
  1586. };
  1587. auto accept_all = [](const opt::Instruction&) { return true; };
  1588. PoolPotentialIds(src_->ext_inst_imports(), potential_id_map.src_ids,
  1589. accept_all, get_result_id);
  1590. PoolPotentialIds(dst_->ext_inst_imports(), potential_id_map.dst_ids,
  1591. accept_all, get_result_id);
  1592. // Then match the ids.
  1593. MatchIds(potential_id_map, [](const opt::Instruction* src_inst,
  1594. const opt::Instruction* dst_inst) {
  1595. // Match OpExtInstImport by exact name, which is operand 1
  1596. const opt::Operand& src_name = src_inst->GetOperand(1);
  1597. const opt::Operand& dst_name = dst_inst->GetOperand(1);
  1598. return src_name.AsString() == dst_name.AsString();
  1599. });
  1600. }
  1601. void Differ::MatchMemoryModel() {
  1602. // Always match the memory model instructions, there is always a single one of
  1603. // it.
  1604. id_map_.MapInsts(src_->GetMemoryModel(), dst_->GetMemoryModel());
  1605. }
  1606. void Differ::MatchEntryPointIds() {
  1607. // Match OpEntryPoint ids (at index 1) by ExecutionModel (at index 0) and
  1608. // possibly name (at index 2). OpEntryPoint doesn't produce a result id, so
  1609. // this function doesn't use the helpers the other functions use.
  1610. // Map from execution model to OpEntryPoint instructions of that model.
  1611. using ExecutionModelMap =
  1612. std::unordered_map<uint32_t, std::vector<const opt::Instruction*>>;
  1613. ExecutionModelMap src_entry_points_map;
  1614. ExecutionModelMap dst_entry_points_map;
  1615. std::set<uint32_t> all_execution_models;
  1616. for (const opt::Instruction& src_inst : src_->entry_points()) {
  1617. uint32_t execution_model = src_inst.GetOperand(0).words[0];
  1618. src_entry_points_map[execution_model].push_back(&src_inst);
  1619. all_execution_models.insert(execution_model);
  1620. }
  1621. for (const opt::Instruction& dst_inst : dst_->entry_points()) {
  1622. uint32_t execution_model = dst_inst.GetOperand(0).words[0];
  1623. dst_entry_points_map[execution_model].push_back(&dst_inst);
  1624. all_execution_models.insert(execution_model);
  1625. }
  1626. // Go through each model and match the ids.
  1627. for (const uint32_t execution_model : all_execution_models) {
  1628. auto& src_insts = src_entry_points_map[execution_model];
  1629. auto& dst_insts = dst_entry_points_map[execution_model];
  1630. // If there is only one entry point in src and dst with that model, match
  1631. // them unconditionally.
  1632. if (src_insts.size() == 1 && dst_insts.size() == 1) {
  1633. uint32_t src_id = src_insts[0]->GetOperand(1).AsId();
  1634. uint32_t dst_id = dst_insts[0]->GetOperand(1).AsId();
  1635. id_map_.MapIds(src_id, dst_id);
  1636. id_map_.MapInsts(src_insts[0], dst_insts[0]);
  1637. continue;
  1638. }
  1639. // Otherwise match them by name.
  1640. bool matched = false;
  1641. for (const opt::Instruction* src_inst : src_insts) {
  1642. for (const opt::Instruction* dst_inst : dst_insts) {
  1643. const opt::Operand& src_name = src_inst->GetOperand(2);
  1644. const opt::Operand& dst_name = dst_inst->GetOperand(2);
  1645. if (src_name.AsString() == dst_name.AsString()) {
  1646. uint32_t src_id = src_inst->GetOperand(1).AsId();
  1647. uint32_t dst_id = dst_inst->GetOperand(1).AsId();
  1648. id_map_.MapIds(src_id, dst_id);
  1649. id_map_.MapInsts(src_inst, dst_inst);
  1650. matched = true;
  1651. break;
  1652. }
  1653. }
  1654. if (matched) {
  1655. break;
  1656. }
  1657. }
  1658. }
  1659. }
  1660. void Differ::MatchExecutionModes() {
  1661. MatchPreambleInstructions(src_->execution_modes(), dst_->execution_modes());
  1662. }
  1663. void Differ::MatchTypeIds() {
  1664. // Bunch all of type ids as potential matches.
  1665. PotentialIdMap potential_id_map;
  1666. auto get_result_id = [](const opt::Instruction& inst) {
  1667. return inst.result_id();
  1668. };
  1669. auto accept_type_ops = [](const opt::Instruction& inst) {
  1670. return spvOpcodeGeneratesType(inst.opcode());
  1671. };
  1672. PoolPotentialIds(src_->types_values(), potential_id_map.src_ids,
  1673. accept_type_ops, get_result_id);
  1674. PoolPotentialIds(dst_->types_values(), potential_id_map.dst_ids,
  1675. accept_type_ops, get_result_id);
  1676. // Then match the ids. Start with exact matches, then match the leftover with
  1677. // gradually loosening degrees of strictness. For example, in the absence of
  1678. // debug info, two block types will be matched if they differ only in a few of
  1679. // the fields.
  1680. for (uint32_t flexibility = 0; flexibility < 2; ++flexibility) {
  1681. MatchIds(potential_id_map, [this, flexibility](
  1682. const opt::Instruction* src_inst,
  1683. const opt::Instruction* dst_inst) {
  1684. const SpvOp src_op = src_inst->opcode();
  1685. const SpvOp dst_op = dst_inst->opcode();
  1686. // Don't match if the opcode is not the same.
  1687. if (src_op != dst_op) {
  1688. return false;
  1689. }
  1690. switch (src_op) {
  1691. case SpvOpTypeVoid:
  1692. case SpvOpTypeBool:
  1693. case SpvOpTypeSampler:
  1694. // void, bool and sampler are unique, match them.
  1695. return true;
  1696. case SpvOpTypeInt:
  1697. case SpvOpTypeFloat:
  1698. case SpvOpTypeVector:
  1699. case SpvOpTypeMatrix:
  1700. case SpvOpTypeSampledImage:
  1701. case SpvOpTypeRuntimeArray:
  1702. case SpvOpTypePointer:
  1703. case SpvOpTypeFunction:
  1704. // Match these instructions when all operands match.
  1705. assert(src_inst->NumInOperandWords() ==
  1706. dst_inst->NumInOperandWords());
  1707. return DoOperandsMatch(src_inst, dst_inst, 0,
  1708. src_inst->NumInOperandWords());
  1709. case SpvOpTypeImage:
  1710. // Match these instructions when all operands match, including the
  1711. // optional final parameter (if provided in both).
  1712. if (src_inst->NumInOperandWords() != dst_inst->NumInOperandWords()) {
  1713. return false;
  1714. }
  1715. return DoOperandsMatch(src_inst, dst_inst, 0,
  1716. src_inst->NumInOperandWords());
  1717. case SpvOpTypeArray:
  1718. // Match arrays only if the element type and length match. The length
  1719. // is an id of a constant, so the actual constant it's defining is
  1720. // compared instead.
  1721. if (!DoOperandsMatch(src_inst, dst_inst, 0, 1)) {
  1722. return false;
  1723. }
  1724. return GetConstantUint(src_id_to_,
  1725. src_inst->GetInOperand(1).AsId()) ==
  1726. GetConstantUint(dst_id_to_, dst_inst->GetInOperand(1).AsId());
  1727. case SpvOpTypeStruct:
  1728. return MatchOpTypeStruct(src_inst, dst_inst, flexibility);
  1729. default:
  1730. return false;
  1731. }
  1732. });
  1733. }
  1734. }
  1735. void Differ::MatchConstants() {
  1736. // Bunch all of constant ids as potential matches.
  1737. PotentialIdMap potential_id_map;
  1738. auto get_result_id = [](const opt::Instruction& inst) {
  1739. return inst.result_id();
  1740. };
  1741. auto accept_type_ops = [](const opt::Instruction& inst) {
  1742. return spvOpcodeIsConstant(inst.opcode());
  1743. };
  1744. PoolPotentialIds(src_->types_values(), potential_id_map.src_ids,
  1745. accept_type_ops, get_result_id);
  1746. PoolPotentialIds(dst_->types_values(), potential_id_map.dst_ids,
  1747. accept_type_ops, get_result_id);
  1748. // Then match the ids. Constants are matched exactly, except for float types
  1749. // that are first matched exactly, then leftovers are matched with a small
  1750. // error.
  1751. for (uint32_t flexibility = 0; flexibility < 2; ++flexibility) {
  1752. MatchIds(potential_id_map, [this, flexibility](
  1753. const opt::Instruction* src_inst,
  1754. const opt::Instruction* dst_inst) {
  1755. const SpvOp src_op = src_inst->opcode();
  1756. const SpvOp dst_op = dst_inst->opcode();
  1757. // Don't match if the opcode is not the same.
  1758. if (src_op != dst_op) {
  1759. return false;
  1760. }
  1761. switch (src_op) {
  1762. case SpvOpConstantTrue:
  1763. case SpvOpConstantFalse:
  1764. // true and false are unique, match them.
  1765. return true;
  1766. case SpvOpConstant:
  1767. return MatchOpConstant(src_inst, dst_inst, flexibility);
  1768. case SpvOpConstantComposite:
  1769. // Composite constants must match in type and value.
  1770. //
  1771. // TODO: match OpConstantNull with OpConstantComposite with all zeros
  1772. // at flexibility == 1
  1773. // TODO: match constants from structs that have been flexibly-matched.
  1774. if (src_inst->NumInOperandWords() != dst_inst->NumInOperandWords()) {
  1775. return false;
  1776. }
  1777. return DoesOperandMatch(src_inst->GetOperand(0),
  1778. dst_inst->GetOperand(0)) &&
  1779. DoOperandsMatch(src_inst, dst_inst, 0,
  1780. src_inst->NumInOperandWords());
  1781. case SpvOpConstantSampler:
  1782. // Match sampler constants exactly.
  1783. // TODO: Allow flexibility in parameters to better diff shaders where
  1784. // the sampler param has changed.
  1785. assert(src_inst->NumInOperandWords() ==
  1786. dst_inst->NumInOperandWords());
  1787. return DoOperandsMatch(src_inst, dst_inst, 0,
  1788. src_inst->NumInOperandWords());
  1789. case SpvOpConstantNull:
  1790. // Match null constants as long as the type matches.
  1791. return DoesOperandMatch(src_inst->GetOperand(0),
  1792. dst_inst->GetOperand(0));
  1793. case SpvOpSpecConstantTrue:
  1794. case SpvOpSpecConstantFalse:
  1795. case SpvOpSpecConstant:
  1796. case SpvOpSpecConstantComposite:
  1797. case SpvOpSpecConstantOp:
  1798. // Match spec constants by name if available, then by the SpecId
  1799. // decoration.
  1800. return MatchOpSpecConstant(src_inst, dst_inst);
  1801. default:
  1802. return false;
  1803. }
  1804. });
  1805. }
  1806. }
  1807. void Differ::MatchVariableIds() {
  1808. // Bunch all of variable ids as potential matches.
  1809. PotentialIdMap potential_id_map;
  1810. auto get_result_id = [](const opt::Instruction& inst) {
  1811. return inst.result_id();
  1812. };
  1813. auto accept_type_ops = [](const opt::Instruction& inst) {
  1814. return inst.opcode() == SpvOpVariable;
  1815. };
  1816. PoolPotentialIds(src_->types_values(), potential_id_map.src_ids,
  1817. accept_type_ops, get_result_id);
  1818. PoolPotentialIds(dst_->types_values(), potential_id_map.dst_ids,
  1819. accept_type_ops, get_result_id);
  1820. // Then match the ids. Start with exact matches, then match the leftover with
  1821. // gradually loosening degrees of strictness. For example, in the absence of
  1822. // debug info, two otherwise identical variables will be matched if one of
  1823. // them has a Private storage class and the other doesn't.
  1824. for (uint32_t flexibility = 0; flexibility < 2; ++flexibility) {
  1825. MatchIds(potential_id_map,
  1826. [this, flexibility](const opt::Instruction* src_inst,
  1827. const opt::Instruction* dst_inst) {
  1828. assert(src_inst->opcode() == SpvOpVariable);
  1829. assert(dst_inst->opcode() == SpvOpVariable);
  1830. return MatchOpVariable(src_inst, dst_inst, flexibility);
  1831. });
  1832. }
  1833. }
  1834. void Differ::MatchFunctions() {
  1835. IdGroup src_func_ids;
  1836. IdGroup dst_func_ids;
  1837. for (const auto& func : src_funcs_) {
  1838. src_func_ids.push_back(func.first);
  1839. }
  1840. for (const auto& func : dst_funcs_) {
  1841. dst_func_ids.push_back(func.first);
  1842. }
  1843. // Base the matching of functions on debug info when available.
  1844. IdGroupMapByName src_func_groups;
  1845. IdGroupMapByName dst_func_groups;
  1846. GroupIdsByName(src_func_ids, true, &src_func_groups);
  1847. GroupIdsByName(dst_func_ids, false, &dst_func_groups);
  1848. // Match functions with identical names.
  1849. for (const auto& src_func_group : src_func_groups) {
  1850. const std::string& name = src_func_group.first;
  1851. const IdGroup& src_group = src_func_group.second;
  1852. if (name == "") {
  1853. continue;
  1854. }
  1855. const IdGroup& dst_group = dst_func_groups[name];
  1856. // If there is a single function with this name in src and dst, it's a
  1857. // definite match.
  1858. if (src_group.size() == 1 && dst_group.size() == 1) {
  1859. id_map_.MapIds(src_group[0], dst_group[0]);
  1860. continue;
  1861. }
  1862. // If there are multiple functions with the same name, group them by type,
  1863. // and match only if the types match (and are unique).
  1864. IdGroupMapByTypeId src_func_groups_by_type_id;
  1865. IdGroupMapByTypeId dst_func_groups_by_type_id;
  1866. GroupIdsByTypeId(src_group, true, &src_func_groups_by_type_id);
  1867. GroupIdsByTypeId(dst_group, false, &dst_func_groups_by_type_id);
  1868. for (const auto& src_func_group_by_type_id : src_func_groups_by_type_id) {
  1869. const uint32_t type_id = src_func_group_by_type_id.first;
  1870. const IdGroup& src_group_by_type_id = src_func_group_by_type_id.second;
  1871. const IdGroup& dst_group_by_type_id = dst_func_groups_by_type_id[type_id];
  1872. if (src_group_by_type_id.size() == 1 &&
  1873. dst_group_by_type_id.size() == 1) {
  1874. id_map_.MapIds(src_group_by_type_id[0], dst_group_by_type_id[0]);
  1875. }
  1876. }
  1877. }
  1878. // Any functions that are left are pooled together and matched as if unnamed,
  1879. // with the only exception that two functions with mismatching names are not
  1880. // matched.
  1881. //
  1882. // Before that however, the diff of the functions that are matched are taken
  1883. // and processed, so that more of the global variables can be matched before
  1884. // attempting to match the rest of the functions. They can contribute to the
  1885. // precision of the diff of those functions.
  1886. for (const uint32_t src_func_id : src_func_ids) {
  1887. const uint32_t dst_func_id = id_map_.MappedDstId(src_func_id);
  1888. if (dst_func_id == 0) {
  1889. continue;
  1890. }
  1891. // Since these functions are definite matches, match their parameters for a
  1892. // better diff.
  1893. MatchFunctionParamIds(src_funcs_[src_func_id], dst_funcs_[dst_func_id]);
  1894. // Take the diff of the two functions.
  1895. DiffMatch src_match_result, dst_match_result;
  1896. MatchFunctionBodies(src_func_insts_[src_func_id],
  1897. dst_func_insts_[dst_func_id], &src_match_result,
  1898. &dst_match_result);
  1899. // Match ids between the two function bodies; which can also result in
  1900. // global variables getting matched.
  1901. MatchIdsInFunctionBodies(src_func_insts_[src_func_id],
  1902. dst_func_insts_[dst_func_id], src_match_result,
  1903. dst_match_result, 0);
  1904. }
  1905. // Best effort match functions with matching type.
  1906. IdGroupMapByTypeId src_func_groups_by_type_id;
  1907. IdGroupMapByTypeId dst_func_groups_by_type_id;
  1908. GroupIdsByTypeId(src_func_ids, true, &src_func_groups_by_type_id);
  1909. GroupIdsByTypeId(dst_func_ids, false, &dst_func_groups_by_type_id);
  1910. for (const auto& src_func_group_by_type_id : src_func_groups_by_type_id) {
  1911. const uint32_t type_id = src_func_group_by_type_id.first;
  1912. const IdGroup& src_group_by_type_id = src_func_group_by_type_id.second;
  1913. const IdGroup& dst_group_by_type_id = dst_func_groups_by_type_id[type_id];
  1914. BestEffortMatchFunctions(src_group_by_type_id, dst_group_by_type_id,
  1915. src_func_insts_, dst_func_insts_);
  1916. }
  1917. // Any function that's left, best effort match them.
  1918. BestEffortMatchFunctions(src_func_ids, dst_func_ids, src_func_insts_,
  1919. dst_func_insts_);
  1920. }
  1921. void Differ::MatchDebugs1() {
  1922. // This section in cludes: OpString, OpSourceExtension, OpSource,
  1923. // OpSourceContinued
  1924. MatchDebugAndAnnotationInstructions(src_->debugs1(), dst_->debugs1());
  1925. }
  1926. void Differ::MatchDebugs2() {
  1927. // This section includes: OpName, OpMemberName
  1928. MatchDebugAndAnnotationInstructions(src_->debugs2(), dst_->debugs2());
  1929. }
  1930. void Differ::MatchDebugs3() {
  1931. // This section includes: OpModuleProcessed
  1932. MatchDebugAndAnnotationInstructions(src_->debugs3(), dst_->debugs3());
  1933. }
  1934. void Differ::MatchExtInstDebugInfo() {
  1935. // This section includes OpExtInst for DebugInfo extension
  1936. MatchDebugAndAnnotationInstructions(src_->ext_inst_debuginfo(),
  1937. dst_->ext_inst_debuginfo());
  1938. }
  1939. void Differ::MatchAnnotations() {
  1940. // This section includes OpDecorate and family.
  1941. MatchDebugAndAnnotationInstructions(src_->annotations(), dst_->annotations());
  1942. }
  1943. const opt::Instruction* Differ::MappedDstInst(
  1944. const opt::Instruction* src_inst) {
  1945. return MappedInstImpl(src_inst, id_map_.SrcToDstMap(), dst_id_to_);
  1946. }
  1947. const opt::Instruction* Differ::MappedSrcInst(
  1948. const opt::Instruction* dst_inst) {
  1949. return MappedInstImpl(dst_inst, id_map_.DstToSrcMap(), src_id_to_);
  1950. }
  1951. const opt::Instruction* Differ::MappedInstImpl(
  1952. const opt::Instruction* inst, const IdMap& to_other,
  1953. const IdInstructions& other_id_to) {
  1954. if (inst->HasResultId()) {
  1955. if (to_other.IsMapped(inst->result_id())) {
  1956. const uint32_t other_result_id = to_other.MappedId(inst->result_id());
  1957. assert(other_result_id < other_id_to.inst_map_.size());
  1958. return other_id_to.inst_map_[other_result_id];
  1959. }
  1960. return nullptr;
  1961. }
  1962. return to_other.MappedInst(inst);
  1963. }
  1964. void Differ::OutputLine(std::function<bool()> are_lines_identical,
  1965. std::function<void()> output_src_line,
  1966. std::function<void()> output_dst_line) {
  1967. if (are_lines_identical()) {
  1968. out_ << " ";
  1969. output_src_line();
  1970. } else {
  1971. OutputRed();
  1972. out_ << "-";
  1973. output_src_line();
  1974. OutputGreen();
  1975. out_ << "+";
  1976. output_dst_line();
  1977. OutputResetColor();
  1978. }
  1979. }
  1980. const opt::Instruction* IterInst(opt::Module::const_inst_iterator& iter) {
  1981. return &*iter;
  1982. }
  1983. const opt::Instruction* IterInst(InstructionList::const_iterator& iter) {
  1984. return *iter;
  1985. }
  1986. template <typename InstList>
  1987. void Differ::OutputSection(
  1988. const InstList& src_insts, const InstList& dst_insts,
  1989. std::function<void(const opt::Instruction&, const IdInstructions&,
  1990. const opt::Instruction&)>
  1991. write_inst) {
  1992. auto src_iter = src_insts.begin();
  1993. auto dst_iter = dst_insts.begin();
  1994. // - While src_inst doesn't have a match, output it with -
  1995. // - While dst_inst doesn't have a match, output it with +
  1996. // - Now src_inst and dst_inst both have matches; might not match each other!
  1997. // * If section is unordered, just process src_inst and its match (dst_inst
  1998. // or not),
  1999. // dst_inst will eventually be processed when its match is seen.
  2000. // * If section is ordered, also just process src_inst and its match. Its
  2001. // match must
  2002. // necessarily be dst_inst.
  2003. while (src_iter != src_insts.end() || dst_iter != dst_insts.end()) {
  2004. OutputRed();
  2005. while (src_iter != src_insts.end() &&
  2006. MappedDstInst(IterInst(src_iter)) == nullptr) {
  2007. out_ << "-";
  2008. write_inst(*IterInst(src_iter), src_id_to_, *IterInst(src_iter));
  2009. ++src_iter;
  2010. }
  2011. OutputGreen();
  2012. while (dst_iter != dst_insts.end() &&
  2013. MappedSrcInst(IterInst(dst_iter)) == nullptr) {
  2014. out_ << "+";
  2015. write_inst(ToMappedSrcIds(*IterInst(dst_iter)), dst_id_to_,
  2016. *IterInst(dst_iter));
  2017. ++dst_iter;
  2018. }
  2019. OutputResetColor();
  2020. if (src_iter != src_insts.end() && dst_iter != dst_insts.end()) {
  2021. const opt::Instruction* src_inst = IterInst(src_iter);
  2022. const opt::Instruction* matched_dst_inst = MappedDstInst(src_inst);
  2023. assert(matched_dst_inst != nullptr);
  2024. assert(MappedSrcInst(IterInst(dst_iter)) != nullptr);
  2025. OutputLine(
  2026. [this, src_inst, matched_dst_inst]() {
  2027. return DoInstructionsMatch(src_inst, matched_dst_inst);
  2028. },
  2029. [this, src_inst, &write_inst]() {
  2030. write_inst(*src_inst, src_id_to_, *src_inst);
  2031. },
  2032. [this, matched_dst_inst, &write_inst]() {
  2033. write_inst(ToMappedSrcIds(*matched_dst_inst), dst_id_to_,
  2034. *matched_dst_inst);
  2035. });
  2036. ++src_iter;
  2037. ++dst_iter;
  2038. }
  2039. }
  2040. }
  2041. void Differ::ToParsedInstruction(
  2042. const opt::Instruction& inst, const IdInstructions& id_to,
  2043. const opt::Instruction& original_inst,
  2044. spv_parsed_instruction_t* parsed_inst,
  2045. std::vector<spv_parsed_operand_t>& parsed_operands,
  2046. std::vector<uint32_t>& inst_binary) {
  2047. inst.ToBinaryWithoutAttachedDebugInsts(&inst_binary);
  2048. parsed_operands.resize(inst.NumOperands());
  2049. parsed_inst->words = inst_binary.data();
  2050. parsed_inst->num_words = static_cast<uint16_t>(inst_binary.size());
  2051. parsed_inst->opcode = static_cast<uint16_t>(inst.opcode());
  2052. parsed_inst->ext_inst_type =
  2053. inst.opcode() == SpvOpExtInst
  2054. ? GetExtInstType(id_to, original_inst.GetInOperand(0).AsId())
  2055. : SPV_EXT_INST_TYPE_NONE;
  2056. parsed_inst->type_id = inst.HasResultType() ? inst.GetOperand(0).AsId() : 0;
  2057. parsed_inst->result_id = inst.HasResultId() ? inst.result_id() : 0;
  2058. parsed_inst->operands = parsed_operands.data();
  2059. parsed_inst->num_operands = static_cast<uint16_t>(parsed_operands.size());
  2060. // Word 0 is always op and num_words, so operands start at offset 1.
  2061. uint32_t offset = 1;
  2062. for (uint16_t operand_index = 0; operand_index < parsed_inst->num_operands;
  2063. ++operand_index) {
  2064. const opt::Operand& operand = inst.GetOperand(operand_index);
  2065. spv_parsed_operand_t& parsed_operand = parsed_operands[operand_index];
  2066. parsed_operand.offset = static_cast<uint16_t>(offset);
  2067. parsed_operand.num_words = static_cast<uint16_t>(operand.words.size());
  2068. parsed_operand.type = operand.type;
  2069. parsed_operand.number_kind = GetNumberKind(
  2070. id_to, original_inst, operand_index, &parsed_operand.number_bit_width);
  2071. offset += parsed_operand.num_words;
  2072. }
  2073. }
  2074. opt::Instruction Differ::ToMappedSrcIds(const opt::Instruction& dst_inst) {
  2075. // Create an identical instruction to dst_inst, except ids are changed to the
  2076. // mapped one.
  2077. opt::Instruction mapped_inst = dst_inst;
  2078. for (uint32_t operand_index = 0; operand_index < mapped_inst.NumOperands();
  2079. ++operand_index) {
  2080. opt::Operand& operand = mapped_inst.GetOperand(operand_index);
  2081. if (spvIsIdType(operand.type)) {
  2082. assert(id_map_.IsDstMapped(operand.AsId()));
  2083. operand.words[0] = id_map_.MappedSrcId(operand.AsId());
  2084. }
  2085. }
  2086. return mapped_inst;
  2087. }
  2088. spv_result_t Differ::Output() {
  2089. id_map_.MapUnmatchedIds();
  2090. src_id_to_.inst_map_.resize(id_map_.SrcToDstMap().IdBound(), nullptr);
  2091. dst_id_to_.inst_map_.resize(id_map_.DstToSrcMap().IdBound(), nullptr);
  2092. const spv_target_env target_env = SPV_ENV_UNIVERSAL_1_6;
  2093. spv_opcode_table opcode_table;
  2094. spv_operand_table operand_table;
  2095. spv_ext_inst_table ext_inst_table;
  2096. spv_result_t result;
  2097. result = spvOpcodeTableGet(&opcode_table, target_env);
  2098. if (result != SPV_SUCCESS) return result;
  2099. result = spvOperandTableGet(&operand_table, target_env);
  2100. if (result != SPV_SUCCESS) return result;
  2101. result = spvExtInstTableGet(&ext_inst_table, target_env);
  2102. if (result != SPV_SUCCESS) return result;
  2103. spv_context_t context{
  2104. target_env,
  2105. opcode_table,
  2106. operand_table,
  2107. ext_inst_table,
  2108. };
  2109. const AssemblyGrammar grammar(&context);
  2110. if (!grammar.isValid()) return SPV_ERROR_INVALID_TABLE;
  2111. uint32_t disassembly_options = SPV_BINARY_TO_TEXT_OPTION_PRINT;
  2112. if (options_.indent) {
  2113. disassembly_options |= SPV_BINARY_TO_TEXT_OPTION_INDENT;
  2114. }
  2115. NameMapper name_mapper = GetTrivialNameMapper();
  2116. disassemble::InstructionDisassembler dis(grammar, out_, disassembly_options,
  2117. name_mapper);
  2118. if (!options_.no_header) {
  2119. // Output the header
  2120. // TODO: when using diff with text, the assembler overrides the version and
  2121. // generator, so these aren't reflected correctly in the output. Could
  2122. // potentially extract this info from the header comment.
  2123. OutputLine([]() { return true; }, [&dis]() { dis.EmitHeaderSpirv(); },
  2124. []() { assert(false && "Unreachable"); });
  2125. OutputLine([this]() { return src_->version() == dst_->version(); },
  2126. [this, &dis]() { dis.EmitHeaderVersion(src_->version()); },
  2127. [this, &dis]() { dis.EmitHeaderVersion(dst_->version()); });
  2128. OutputLine([this]() { return src_->generator() == dst_->generator(); },
  2129. [this, &dis]() { dis.EmitHeaderGenerator(src_->generator()); },
  2130. [this, &dis]() { dis.EmitHeaderGenerator(dst_->generator()); });
  2131. OutputLine(
  2132. [this]() { return src_->IdBound() == id_map_.SrcToDstMap().IdBound(); },
  2133. [this, &dis]() { dis.EmitHeaderIdBound(src_->IdBound()); },
  2134. [this, &dis]() {
  2135. dis.EmitHeaderIdBound(id_map_.SrcToDstMap().IdBound());
  2136. });
  2137. OutputLine([this]() { return src_->schema() == dst_->schema(); },
  2138. [this, &dis]() { dis.EmitHeaderSchema(src_->schema()); },
  2139. [this, &dis]() { dis.EmitHeaderSchema(dst_->schema()); });
  2140. }
  2141. // For each section, iterate both modules and output the disassembly.
  2142. auto write_inst = [this, &dis](const opt::Instruction& inst,
  2143. const IdInstructions& id_to,
  2144. const opt::Instruction& original_inst) {
  2145. spv_parsed_instruction_t parsed_inst;
  2146. std::vector<spv_parsed_operand_t> parsed_operands;
  2147. std::vector<uint32_t> inst_binary;
  2148. ToParsedInstruction(inst, id_to, original_inst, &parsed_inst,
  2149. parsed_operands, inst_binary);
  2150. dis.EmitInstruction(parsed_inst, 0);
  2151. };
  2152. OutputSection(src_->capabilities(), dst_->capabilities(), write_inst);
  2153. OutputSection(src_->extensions(), dst_->extensions(), write_inst);
  2154. OutputSection(src_->ext_inst_imports(), dst_->ext_inst_imports(), write_inst);
  2155. // There is only one memory model.
  2156. OutputLine(
  2157. [this]() {
  2158. return DoInstructionsMatch(src_->GetMemoryModel(),
  2159. dst_->GetMemoryModel());
  2160. },
  2161. [this, &write_inst]() {
  2162. write_inst(*src_->GetMemoryModel(), src_id_to_,
  2163. *src_->GetMemoryModel());
  2164. },
  2165. [this, &write_inst]() {
  2166. write_inst(*dst_->GetMemoryModel(), dst_id_to_,
  2167. *dst_->GetMemoryModel());
  2168. });
  2169. OutputSection(src_->entry_points(), dst_->entry_points(), write_inst);
  2170. OutputSection(src_->execution_modes(), dst_->execution_modes(), write_inst);
  2171. OutputSection(src_->debugs1(), dst_->debugs1(), write_inst);
  2172. OutputSection(src_->debugs2(), dst_->debugs2(), write_inst);
  2173. OutputSection(src_->debugs3(), dst_->debugs3(), write_inst);
  2174. OutputSection(src_->ext_inst_debuginfo(), dst_->ext_inst_debuginfo(),
  2175. write_inst);
  2176. OutputSection(src_->annotations(), dst_->annotations(), write_inst);
  2177. OutputSection(src_->types_values(), dst_->types_values(), write_inst);
  2178. // Get the body of all the functions.
  2179. FunctionInstMap src_func_header_insts;
  2180. FunctionInstMap dst_func_header_insts;
  2181. GetFunctionHeaderInstructions(src_, &src_func_header_insts);
  2182. GetFunctionHeaderInstructions(dst_, &dst_func_header_insts);
  2183. for (const auto& src_func : src_func_insts_) {
  2184. const uint32_t src_func_id = src_func.first;
  2185. const InstructionList& src_insts = src_func.second;
  2186. const InstructionList& src_header_insts =
  2187. src_func_header_insts[src_func_id];
  2188. const uint32_t dst_func_id = id_map_.MappedDstId(src_func_id);
  2189. if (dst_func_insts_.find(dst_func_id) == dst_func_insts_.end()) {
  2190. OutputSection(src_header_insts, InstructionList(), write_inst);
  2191. OutputSection(src_insts, InstructionList(), write_inst);
  2192. continue;
  2193. }
  2194. const InstructionList& dst_insts = dst_func_insts_[dst_func_id];
  2195. const InstructionList& dst_header_insts =
  2196. dst_func_header_insts[dst_func_id];
  2197. OutputSection(src_header_insts, dst_header_insts, write_inst);
  2198. OutputSection(src_insts, dst_insts, write_inst);
  2199. }
  2200. for (const auto& dst_func : dst_func_insts_) {
  2201. const uint32_t dst_func_id = dst_func.first;
  2202. const InstructionList& dst_insts = dst_func.second;
  2203. const InstructionList& dst_header_insts =
  2204. dst_func_header_insts[dst_func_id];
  2205. const uint32_t src_func_id = id_map_.MappedSrcId(dst_func_id);
  2206. if (src_func_insts_.find(src_func_id) == src_func_insts_.end()) {
  2207. OutputSection(InstructionList(), dst_header_insts, write_inst);
  2208. OutputSection(InstructionList(), dst_insts, write_inst);
  2209. }
  2210. }
  2211. out_ << std::flush;
  2212. return SPV_SUCCESS;
  2213. }
  2214. } // anonymous namespace
  2215. spv_result_t Diff(opt::IRContext* src, opt::IRContext* dst, std::ostream& out,
  2216. Options options) {
  2217. // High level algorithm:
  2218. //
  2219. // - Some sections of SPIR-V don't deal with ids; instructions in those
  2220. // sections are matched identically. For example OpCapability instructions.
  2221. // - Some sections produce ids, and they can be trivially matched by their
  2222. // parameters. For example OpExtInstImport instructions.
  2223. // - Some sections annotate ids. These are matched at the end, after the ids
  2224. // themselves are matched. For example OpName or OpDecorate instructions.
  2225. // - Some sections produce ids that depend on other ids and they can be
  2226. // recursively matched. For example OpType* instructions.
  2227. // - Some sections produce ids that are not trivially matched. For these ids,
  2228. // the debug info is used when possible, or a best guess (such as through
  2229. // decorations) is used. For example OpVariable instructions.
  2230. // - Matching functions is done with multiple attempts:
  2231. // * Functions with identical debug names are matched if there are no
  2232. // overloads.
  2233. // * Otherwise, functions with identical debug names and types are matched.
  2234. // * The rest of the functions are best-effort matched, first in groups of
  2235. // identical type, then any with any.
  2236. // * The best-effort matching takes the diff of every pair of functions in
  2237. // a group and selects the top matches that also meet a similarity
  2238. // index.
  2239. // * Once a pair of functions are matched, the fuzzy diff of the
  2240. // instructions is used to match the instructions in the function body.
  2241. // The fuzzy diff makes sure that sufficiently similar instructions are
  2242. // matched and that yet-to-be-matched result ids don't result in a larger
  2243. // diff.
  2244. //
  2245. // Once the instructions are matched between the src and dst SPIR-V, the src
  2246. // is traversed and its disassembly is output. In the process, any unmatched
  2247. // instruction is prefixed with -, and any unmatched instruction in dst in the
  2248. // same section is output prefixed with +. To avoid confusion, the
  2249. // instructions in dst are output with matching ids in src so the output
  2250. // assembly is consistent.
  2251. Differ differ(src, dst, out, options);
  2252. // First, match instructions between the different non-annotation sections of
  2253. // the SPIR-V.
  2254. differ.MatchCapabilities();
  2255. differ.MatchExtensions();
  2256. differ.MatchExtInstImportIds();
  2257. differ.MatchMemoryModel();
  2258. differ.MatchEntryPointIds();
  2259. differ.MatchExecutionModes();
  2260. differ.MatchTypeIds();
  2261. differ.MatchConstants();
  2262. differ.MatchVariableIds();
  2263. differ.MatchFunctions();
  2264. // Match instructions that annotate previously-matched ids.
  2265. differ.MatchDebugs1();
  2266. differ.MatchDebugs2();
  2267. differ.MatchDebugs3();
  2268. differ.MatchExtInstDebugInfo();
  2269. differ.MatchAnnotations();
  2270. // Show the disassembly with the diff.
  2271. //
  2272. // TODO: Based on an option, output either based on src or dst, i.e. the diff
  2273. // can show the ids and instruction/function order either from src or dst.
  2274. spv_result_t result = differ.Output();
  2275. differ.DumpIdMap();
  2276. return result;
  2277. }
  2278. } // namespace diff
  2279. } // namespace spvtools