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