• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2017 Pierre Moreau
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 "spirv-tools/linker.hpp"
16 
17 #include <algorithm>
18 #include <cstdio>
19 #include <cstring>
20 #include <iostream>
21 #include <memory>
22 #include <string>
23 #include <unordered_map>
24 #include <unordered_set>
25 #include <utility>
26 #include <vector>
27 
28 #include "source/assembly_grammar.h"
29 #include "source/diagnostic.h"
30 #include "source/opt/build_module.h"
31 #include "source/opt/compact_ids_pass.h"
32 #include "source/opt/decoration_manager.h"
33 #include "source/opt/ir_loader.h"
34 #include "source/opt/pass_manager.h"
35 #include "source/opt/remove_duplicates_pass.h"
36 #include "source/opt/type_manager.h"
37 #include "source/spirv_target_env.h"
38 #include "source/util/make_unique.h"
39 #include "spirv-tools/libspirv.hpp"
40 
41 namespace spvtools {
42 namespace {
43 
44 using opt::Instruction;
45 using opt::IRContext;
46 using opt::Module;
47 using opt::PassManager;
48 using opt::RemoveDuplicatesPass;
49 using opt::analysis::DecorationManager;
50 using opt::analysis::DefUseManager;
51 using opt::analysis::Type;
52 using opt::analysis::TypeManager;
53 
54 // Stores various information about an imported or exported symbol.
55 struct LinkageSymbolInfo {
56   SpvId id;          // ID of the symbol
57   SpvId type_id;     // ID of the type of the symbol
58   std::string name;  // unique name defining the symbol and used for matching
59                      // imports and exports together
60   std::vector<SpvId> parameter_ids;  // ID of the parameters of the symbol, if
61                                      // it is a function
62 };
63 struct LinkageEntry {
64   LinkageSymbolInfo imported_symbol;
65   LinkageSymbolInfo exported_symbol;
66 
LinkageEntryspvtools::__anone4e82c220111::LinkageEntry67   LinkageEntry(const LinkageSymbolInfo& import_info,
68                const LinkageSymbolInfo& export_info)
69       : imported_symbol(import_info), exported_symbol(export_info) {}
70 };
71 using LinkageTable = std::vector<LinkageEntry>;
72 
73 // Shifts the IDs used in each binary of |modules| so that they occupy a
74 // disjoint range from the other binaries, and compute the new ID bound which
75 // is returned in |max_id_bound|.
76 //
77 // Both |modules| and |max_id_bound| should not be null, and |modules| should
78 // not be empty either. Furthermore |modules| should not contain any null
79 // pointers.
80 spv_result_t ShiftIdsInModules(const MessageConsumer& consumer,
81                                std::vector<opt::Module*>* modules,
82                                uint32_t* max_id_bound);
83 
84 // Generates the header for the linked module and returns it in |header|.
85 //
86 // |header| should not be null, |modules| should not be empty and pointers
87 // should be non-null. |max_id_bound| should be strictly greater than 0.
88 //
89 // TODO(pierremoreau): What to do when binaries use different versions of
90 //                     SPIR-V? For now, use the max of all versions found in
91 //                     the input modules.
92 spv_result_t GenerateHeader(const MessageConsumer& consumer,
93                             const std::vector<opt::Module*>& modules,
94                             uint32_t max_id_bound, opt::ModuleHeader* header);
95 
96 // Merge all the modules from |in_modules| into a single module owned by
97 // |linked_context|.
98 //
99 // |linked_context| should not be null.
100 spv_result_t MergeModules(const MessageConsumer& consumer,
101                           const std::vector<Module*>& in_modules,
102                           const AssemblyGrammar& grammar,
103                           IRContext* linked_context);
104 
105 // Compute all pairs of import and export and return it in |linkings_to_do|.
106 //
107 // |linkings_to_do should not be null. Built-in symbols will be ignored.
108 //
109 // TODO(pierremoreau): Linkage attributes applied by a group decoration are
110 //                     currently not handled. (You could have a group being
111 //                     applied to a single ID.)
112 // TODO(pierremoreau): What should be the proper behaviour with built-in
113 //                     symbols?
114 spv_result_t GetImportExportPairs(const MessageConsumer& consumer,
115                                   const opt::IRContext& linked_context,
116                                   const DefUseManager& def_use_manager,
117                                   const DecorationManager& decoration_manager,
118                                   bool allow_partial_linkage,
119                                   LinkageTable* linkings_to_do);
120 
121 // Checks that for each pair of import and export, the import and export have
122 // the same type as well as the same decorations.
123 //
124 // TODO(pierremoreau): Decorations on functions parameters are currently not
125 // checked.
126 spv_result_t CheckImportExportCompatibility(const MessageConsumer& consumer,
127                                             const LinkageTable& linkings_to_do,
128                                             opt::IRContext* context);
129 
130 // Remove linkage specific instructions, such as prototypes of imported
131 // functions, declarations of imported variables, import (and export if
132 // necessary) linkage attribtes.
133 //
134 // |linked_context| and |decoration_manager| should not be null, and the
135 // 'RemoveDuplicatePass' should be run first.
136 //
137 // TODO(pierremoreau): Linkage attributes applied by a group decoration are
138 //                     currently not handled. (You could have a group being
139 //                     applied to a single ID.)
140 spv_result_t RemoveLinkageSpecificInstructions(
141     const MessageConsumer& consumer, const LinkerOptions& options,
142     const LinkageTable& linkings_to_do, DecorationManager* decoration_manager,
143     opt::IRContext* linked_context);
144 
145 // Verify that the unique ids of each instruction in |linked_context| (i.e. the
146 // merged module) are truly unique. Does not check the validity of other ids
147 spv_result_t VerifyIds(const MessageConsumer& consumer,
148                        opt::IRContext* linked_context);
149 
ShiftIdsInModules(const MessageConsumer & consumer,std::vector<opt::Module * > * modules,uint32_t * max_id_bound)150 spv_result_t ShiftIdsInModules(const MessageConsumer& consumer,
151                                std::vector<opt::Module*>* modules,
152                                uint32_t* max_id_bound) {
153   spv_position_t position = {};
154 
155   if (modules == nullptr)
156     return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_DATA)
157            << "|modules| of ShiftIdsInModules should not be null.";
158   if (modules->empty())
159     return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_DATA)
160            << "|modules| of ShiftIdsInModules should not be empty.";
161   if (max_id_bound == nullptr)
162     return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_DATA)
163            << "|max_id_bound| of ShiftIdsInModules should not be null.";
164 
165   uint32_t id_bound = modules->front()->IdBound() - 1u;
166   for (auto module_iter = modules->begin() + 1; module_iter != modules->end();
167        ++module_iter) {
168     Module* module = *module_iter;
169     module->ForEachInst([&id_bound](Instruction* insn) {
170       insn->ForEachId([&id_bound](uint32_t* id) { *id += id_bound; });
171     });
172     id_bound += module->IdBound() - 1u;
173     if (id_bound > 0x3FFFFF)
174       return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_ID)
175              << "The limit of IDs, 4194303, was exceeded:"
176              << " " << id_bound << " is the current ID bound.";
177 
178     // Invalidate the DefUseManager
179     module->context()->InvalidateAnalyses(opt::IRContext::kAnalysisDefUse);
180   }
181   ++id_bound;
182   if (id_bound > 0x3FFFFF)
183     return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_ID)
184            << "The limit of IDs, 4194303, was exceeded:"
185            << " " << id_bound << " is the current ID bound.";
186 
187   *max_id_bound = id_bound;
188 
189   return SPV_SUCCESS;
190 }
191 
GenerateHeader(const MessageConsumer & consumer,const std::vector<opt::Module * > & modules,uint32_t max_id_bound,opt::ModuleHeader * header)192 spv_result_t GenerateHeader(const MessageConsumer& consumer,
193                             const std::vector<opt::Module*>& modules,
194                             uint32_t max_id_bound, opt::ModuleHeader* header) {
195   spv_position_t position = {};
196 
197   if (modules.empty())
198     return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_DATA)
199            << "|modules| of GenerateHeader should not be empty.";
200   if (max_id_bound == 0u)
201     return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_DATA)
202            << "|max_id_bound| of GenerateHeader should not be null.";
203 
204   uint32_t version = 0u;
205   for (const auto& module : modules)
206     version = std::max(version, module->version());
207 
208   header->magic_number = SpvMagicNumber;
209   header->version = version;
210   header->generator = 17u;
211   header->bound = max_id_bound;
212   header->reserved = 0u;
213 
214   return SPV_SUCCESS;
215 }
216 
MergeModules(const MessageConsumer & consumer,const std::vector<Module * > & input_modules,const AssemblyGrammar & grammar,IRContext * linked_context)217 spv_result_t MergeModules(const MessageConsumer& consumer,
218                           const std::vector<Module*>& input_modules,
219                           const AssemblyGrammar& grammar,
220                           IRContext* linked_context) {
221   spv_position_t position = {};
222 
223   if (linked_context == nullptr)
224     return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_DATA)
225            << "|linked_module| of MergeModules should not be null.";
226   Module* linked_module = linked_context->module();
227 
228   if (input_modules.empty()) return SPV_SUCCESS;
229 
230   for (const auto& module : input_modules)
231     for (const auto& inst : module->capabilities())
232       linked_module->AddCapability(
233           std::unique_ptr<Instruction>(inst.Clone(linked_context)));
234 
235   for (const auto& module : input_modules)
236     for (const auto& inst : module->extensions())
237       linked_module->AddExtension(
238           std::unique_ptr<Instruction>(inst.Clone(linked_context)));
239 
240   for (const auto& module : input_modules)
241     for (const auto& inst : module->ext_inst_imports())
242       linked_module->AddExtInstImport(
243           std::unique_ptr<Instruction>(inst.Clone(linked_context)));
244 
245   do {
246     const Instruction* memory_model_inst = input_modules[0]->GetMemoryModel();
247     if (memory_model_inst == nullptr) break;
248 
249     uint32_t addressing_model = memory_model_inst->GetSingleWordOperand(0u);
250     uint32_t memory_model = memory_model_inst->GetSingleWordOperand(1u);
251     for (const auto& module : input_modules) {
252       memory_model_inst = module->GetMemoryModel();
253       if (memory_model_inst == nullptr) continue;
254 
255       if (addressing_model != memory_model_inst->GetSingleWordOperand(0u)) {
256         spv_operand_desc initial_desc = nullptr, current_desc = nullptr;
257         grammar.lookupOperand(SPV_OPERAND_TYPE_ADDRESSING_MODEL,
258                               addressing_model, &initial_desc);
259         grammar.lookupOperand(SPV_OPERAND_TYPE_ADDRESSING_MODEL,
260                               memory_model_inst->GetSingleWordOperand(0u),
261                               &current_desc);
262         return DiagnosticStream(position, consumer, "", SPV_ERROR_INTERNAL)
263                << "Conflicting addressing models: " << initial_desc->name
264                << " vs " << current_desc->name << ".";
265       }
266       if (memory_model != memory_model_inst->GetSingleWordOperand(1u)) {
267         spv_operand_desc initial_desc = nullptr, current_desc = nullptr;
268         grammar.lookupOperand(SPV_OPERAND_TYPE_MEMORY_MODEL, memory_model,
269                               &initial_desc);
270         grammar.lookupOperand(SPV_OPERAND_TYPE_MEMORY_MODEL,
271                               memory_model_inst->GetSingleWordOperand(1u),
272                               &current_desc);
273         return DiagnosticStream(position, consumer, "", SPV_ERROR_INTERNAL)
274                << "Conflicting memory models: " << initial_desc->name << " vs "
275                << current_desc->name << ".";
276       }
277     }
278 
279     if (memory_model_inst != nullptr)
280       linked_module->SetMemoryModel(std::unique_ptr<Instruction>(
281           memory_model_inst->Clone(linked_context)));
282   } while (false);
283 
284   std::vector<std::pair<uint32_t, const char*>> entry_points;
285   for (const auto& module : input_modules)
286     for (const auto& inst : module->entry_points()) {
287       const uint32_t model = inst.GetSingleWordInOperand(0);
288       const char* const name =
289           reinterpret_cast<const char*>(inst.GetInOperand(2).words.data());
290       const auto i = std::find_if(
291           entry_points.begin(), entry_points.end(),
292           [model, name](const std::pair<uint32_t, const char*>& v) {
293             return v.first == model && strcmp(name, v.second) == 0;
294           });
295       if (i != entry_points.end()) {
296         spv_operand_desc desc = nullptr;
297         grammar.lookupOperand(SPV_OPERAND_TYPE_EXECUTION_MODEL, model, &desc);
298         return DiagnosticStream(position, consumer, "", SPV_ERROR_INTERNAL)
299                << "The entry point \"" << name << "\", with execution model "
300                << desc->name << ", was already defined.";
301       }
302       linked_module->AddEntryPoint(
303           std::unique_ptr<Instruction>(inst.Clone(linked_context)));
304       entry_points.emplace_back(model, name);
305     }
306 
307   for (const auto& module : input_modules)
308     for (const auto& inst : module->execution_modes())
309       linked_module->AddExecutionMode(
310           std::unique_ptr<Instruction>(inst.Clone(linked_context)));
311 
312   for (const auto& module : input_modules)
313     for (const auto& inst : module->debugs1())
314       linked_module->AddDebug1Inst(
315           std::unique_ptr<Instruction>(inst.Clone(linked_context)));
316 
317   for (const auto& module : input_modules)
318     for (const auto& inst : module->debugs2())
319       linked_module->AddDebug2Inst(
320           std::unique_ptr<Instruction>(inst.Clone(linked_context)));
321 
322   for (const auto& module : input_modules)
323     for (const auto& inst : module->debugs3())
324       linked_module->AddDebug3Inst(
325           std::unique_ptr<Instruction>(inst.Clone(linked_context)));
326 
327   for (const auto& module : input_modules)
328     for (const auto& inst : module->ext_inst_debuginfo())
329       linked_module->AddExtInstDebugInfo(
330           std::unique_ptr<Instruction>(inst.Clone(linked_context)));
331 
332   // If the generated module uses SPIR-V 1.1 or higher, add an
333   // OpModuleProcessed instruction about the linking step.
334   if (linked_module->version() >= 0x10100) {
335     const std::string processed_string("Linked by SPIR-V Tools Linker");
336     const auto num_chars = processed_string.size();
337     // Compute num words, accommodate the terminating null character.
338     const auto num_words = (num_chars + 1 + 3) / 4;
339     std::vector<uint32_t> processed_words(num_words, 0u);
340     std::memcpy(processed_words.data(), processed_string.data(), num_chars);
341     linked_module->AddDebug3Inst(std::unique_ptr<Instruction>(
342         new Instruction(linked_context, SpvOpModuleProcessed, 0u, 0u,
343                         {{SPV_OPERAND_TYPE_LITERAL_STRING, processed_words}})));
344   }
345 
346   for (const auto& module : input_modules)
347     for (const auto& inst : module->annotations())
348       linked_module->AddAnnotationInst(
349           std::unique_ptr<Instruction>(inst.Clone(linked_context)));
350 
351   // TODO(pierremoreau): Since the modules have not been validate, should we
352   //                     expect SpvStorageClassFunction variables outside
353   //                     functions?
354   uint32_t num_global_values = 0u;
355   for (const auto& module : input_modules) {
356     for (const auto& inst : module->types_values()) {
357       linked_module->AddType(
358           std::unique_ptr<Instruction>(inst.Clone(linked_context)));
359       num_global_values += inst.opcode() == SpvOpVariable;
360     }
361   }
362   if (num_global_values > 0xFFFF)
363     return DiagnosticStream(position, consumer, "", SPV_ERROR_INTERNAL)
364            << "The limit of global values, 65535, was exceeded;"
365            << " " << num_global_values << " global values were found.";
366 
367   // Process functions and their basic blocks
368   for (const auto& module : input_modules) {
369     for (const auto& func : *module) {
370       std::unique_ptr<opt::Function> cloned_func(func.Clone(linked_context));
371       linked_module->AddFunction(std::move(cloned_func));
372     }
373   }
374 
375   return SPV_SUCCESS;
376 }
377 
GetImportExportPairs(const MessageConsumer & consumer,const opt::IRContext & linked_context,const DefUseManager & def_use_manager,const DecorationManager & decoration_manager,bool allow_partial_linkage,LinkageTable * linkings_to_do)378 spv_result_t GetImportExportPairs(const MessageConsumer& consumer,
379                                   const opt::IRContext& linked_context,
380                                   const DefUseManager& def_use_manager,
381                                   const DecorationManager& decoration_manager,
382                                   bool allow_partial_linkage,
383                                   LinkageTable* linkings_to_do) {
384   spv_position_t position = {};
385 
386   if (linkings_to_do == nullptr)
387     return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_DATA)
388            << "|linkings_to_do| of GetImportExportPairs should not be empty.";
389 
390   std::vector<LinkageSymbolInfo> imports;
391   std::unordered_map<std::string, std::vector<LinkageSymbolInfo>> exports;
392 
393   // Figure out the imports and exports
394   for (const auto& decoration : linked_context.annotations()) {
395     if (decoration.opcode() != SpvOpDecorate ||
396         decoration.GetSingleWordInOperand(1u) != SpvDecorationLinkageAttributes)
397       continue;
398 
399     const SpvId id = decoration.GetSingleWordInOperand(0u);
400     // Ignore if the targeted symbol is a built-in
401     bool is_built_in = false;
402     for (const auto& id_decoration :
403          decoration_manager.GetDecorationsFor(id, false)) {
404       if (id_decoration->GetSingleWordInOperand(1u) == SpvDecorationBuiltIn) {
405         is_built_in = true;
406         break;
407       }
408     }
409     if (is_built_in) {
410       continue;
411     }
412 
413     const uint32_t type = decoration.GetSingleWordInOperand(3u);
414 
415     LinkageSymbolInfo symbol_info;
416     symbol_info.name =
417         reinterpret_cast<const char*>(decoration.GetInOperand(2u).words.data());
418     symbol_info.id = id;
419     symbol_info.type_id = 0u;
420 
421     // Retrieve the type of the current symbol. This information will be used
422     // when checking that the imported and exported symbols have the same
423     // types.
424     const Instruction* def_inst = def_use_manager.GetDef(id);
425     if (def_inst == nullptr)
426       return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY)
427              << "ID " << id << " is never defined:\n";
428 
429     if (def_inst->opcode() == SpvOpVariable) {
430       symbol_info.type_id = def_inst->type_id();
431     } else if (def_inst->opcode() == SpvOpFunction) {
432       symbol_info.type_id = def_inst->GetSingleWordInOperand(1u);
433 
434       // range-based for loop calls begin()/end(), but never cbegin()/cend(),
435       // which will not work here.
436       for (auto func_iter = linked_context.module()->cbegin();
437            func_iter != linked_context.module()->cend(); ++func_iter) {
438         if (func_iter->result_id() != id) continue;
439         func_iter->ForEachParam([&symbol_info](const Instruction* inst) {
440           symbol_info.parameter_ids.push_back(inst->result_id());
441         });
442       }
443     } else {
444       return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY)
445              << "Only global variables and functions can be decorated using"
446              << " LinkageAttributes; " << id << " is neither of them.\n";
447     }
448 
449     if (type == SpvLinkageTypeImport)
450       imports.push_back(symbol_info);
451     else if (type == SpvLinkageTypeExport)
452       exports[symbol_info.name].push_back(symbol_info);
453   }
454 
455   // Find the import/export pairs
456   for (const auto& import : imports) {
457     std::vector<LinkageSymbolInfo> possible_exports;
458     const auto& exp = exports.find(import.name);
459     if (exp != exports.end()) possible_exports = exp->second;
460     if (possible_exports.empty() && !allow_partial_linkage)
461       return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY)
462              << "Unresolved external reference to \"" << import.name << "\".";
463     else if (possible_exports.size() > 1u)
464       return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY)
465              << "Too many external references, " << possible_exports.size()
466              << ", were found for \"" << import.name << "\".";
467 
468     if (!possible_exports.empty())
469       linkings_to_do->emplace_back(import, possible_exports.front());
470   }
471 
472   return SPV_SUCCESS;
473 }
474 
CheckImportExportCompatibility(const MessageConsumer & consumer,const LinkageTable & linkings_to_do,opt::IRContext * context)475 spv_result_t CheckImportExportCompatibility(const MessageConsumer& consumer,
476                                             const LinkageTable& linkings_to_do,
477                                             opt::IRContext* context) {
478   spv_position_t position = {};
479 
480   // Ensure the import and export types are the same.
481   const DecorationManager& decoration_manager = *context->get_decoration_mgr();
482   const TypeManager& type_manager = *context->get_type_mgr();
483   for (const auto& linking_entry : linkings_to_do) {
484     Type* imported_symbol_type =
485         type_manager.GetType(linking_entry.imported_symbol.type_id);
486     Type* exported_symbol_type =
487         type_manager.GetType(linking_entry.exported_symbol.type_id);
488     if (!(*imported_symbol_type == *exported_symbol_type))
489       return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY)
490              << "Type mismatch on symbol \""
491              << linking_entry.imported_symbol.name
492              << "\" between imported variable/function %"
493              << linking_entry.imported_symbol.id
494              << " and exported variable/function %"
495              << linking_entry.exported_symbol.id << ".";
496   }
497 
498   // Ensure the import and export decorations are similar
499   for (const auto& linking_entry : linkings_to_do) {
500     if (!decoration_manager.HaveTheSameDecorations(
501             linking_entry.imported_symbol.id, linking_entry.exported_symbol.id))
502       return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY)
503              << "Decorations mismatch on symbol \""
504              << linking_entry.imported_symbol.name
505              << "\" between imported variable/function %"
506              << linking_entry.imported_symbol.id
507              << " and exported variable/function %"
508              << linking_entry.exported_symbol.id << ".";
509     // TODO(pierremoreau): Decorations on function parameters should probably
510     //                     match, except for FuncParamAttr if I understand the
511     //                     spec correctly.
512     // TODO(pierremoreau): Decorations on the function return type should
513     //                     match, except for FuncParamAttr.
514   }
515 
516   return SPV_SUCCESS;
517 }
518 
RemoveLinkageSpecificInstructions(const MessageConsumer & consumer,const LinkerOptions & options,const LinkageTable & linkings_to_do,DecorationManager * decoration_manager,opt::IRContext * linked_context)519 spv_result_t RemoveLinkageSpecificInstructions(
520     const MessageConsumer& consumer, const LinkerOptions& options,
521     const LinkageTable& linkings_to_do, DecorationManager* decoration_manager,
522     opt::IRContext* linked_context) {
523   spv_position_t position = {};
524 
525   if (decoration_manager == nullptr)
526     return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_DATA)
527            << "|decoration_manager| of RemoveLinkageSpecificInstructions "
528               "should not be empty.";
529   if (linked_context == nullptr)
530     return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_DATA)
531            << "|linked_module| of RemoveLinkageSpecificInstructions should not "
532               "be empty.";
533 
534   // TODO(pierremoreau): Remove FuncParamAttr decorations of imported
535   // functions' return type.
536 
537   // Remove prototypes of imported functions
538   for (const auto& linking_entry : linkings_to_do) {
539     for (auto func_iter = linked_context->module()->begin();
540          func_iter != linked_context->module()->end();) {
541       if (func_iter->result_id() == linking_entry.imported_symbol.id)
542         func_iter = func_iter.Erase();
543       else
544         ++func_iter;
545     }
546   }
547 
548   // Remove declarations of imported variables
549   for (const auto& linking_entry : linkings_to_do) {
550     auto next = linked_context->types_values_begin();
551     for (auto inst = next; inst != linked_context->types_values_end();
552          inst = next) {
553       ++next;
554       if (inst->result_id() == linking_entry.imported_symbol.id) {
555         linked_context->KillInst(&*inst);
556       }
557     }
558   }
559 
560   // If partial linkage is allowed, we need an efficient way to check whether
561   // an imported ID had a corresponding export symbol. As uses of the imported
562   // symbol have already been replaced by the exported symbol, use the exported
563   // symbol ID.
564   // TODO(pierremoreau): This will not work if the decoration is applied
565   //                     through a group, but the linker does not support that
566   //                     either.
567   std::unordered_set<SpvId> imports;
568   if (options.GetAllowPartialLinkage()) {
569     imports.reserve(linkings_to_do.size());
570     for (const auto& linking_entry : linkings_to_do)
571       imports.emplace(linking_entry.exported_symbol.id);
572   }
573 
574   // Remove import linkage attributes
575   auto next = linked_context->annotation_begin();
576   for (auto inst = next; inst != linked_context->annotation_end();
577        inst = next) {
578     ++next;
579     // If this is an import annotation:
580     // * if we do not allow partial linkage, remove all import annotations;
581     // * otherwise, remove the annotation only if there was a corresponding
582     //   export.
583     if (inst->opcode() == SpvOpDecorate &&
584         inst->GetSingleWordOperand(1u) == SpvDecorationLinkageAttributes &&
585         inst->GetSingleWordOperand(3u) == SpvLinkageTypeImport &&
586         (!options.GetAllowPartialLinkage() ||
587          imports.find(inst->GetSingleWordOperand(0u)) != imports.end())) {
588       linked_context->KillInst(&*inst);
589     }
590   }
591 
592   // Remove export linkage attributes if making an executable
593   if (!options.GetCreateLibrary()) {
594     next = linked_context->annotation_begin();
595     for (auto inst = next; inst != linked_context->annotation_end();
596          inst = next) {
597       ++next;
598       if (inst->opcode() == SpvOpDecorate &&
599           inst->GetSingleWordOperand(1u) == SpvDecorationLinkageAttributes &&
600           inst->GetSingleWordOperand(3u) == SpvLinkageTypeExport) {
601         linked_context->KillInst(&*inst);
602       }
603     }
604   }
605 
606   // Remove Linkage capability if making an executable and partial linkage is
607   // not allowed
608   if (!options.GetCreateLibrary() && !options.GetAllowPartialLinkage()) {
609     for (auto& inst : linked_context->capabilities())
610       if (inst.GetSingleWordInOperand(0u) == SpvCapabilityLinkage) {
611         linked_context->KillInst(&inst);
612         // The RemoveDuplicatesPass did remove duplicated capabilities, so we
613         // now there aren’t more SpvCapabilityLinkage further down.
614         break;
615       }
616   }
617 
618   return SPV_SUCCESS;
619 }
620 
VerifyIds(const MessageConsumer & consumer,opt::IRContext * linked_context)621 spv_result_t VerifyIds(const MessageConsumer& consumer,
622                        opt::IRContext* linked_context) {
623   std::unordered_set<uint32_t> ids;
624   bool ok = true;
625   linked_context->module()->ForEachInst(
626       [&ids, &ok](const opt::Instruction* inst) {
627         ok &= ids.insert(inst->unique_id()).second;
628       });
629 
630   if (!ok) {
631     consumer(SPV_MSG_INTERNAL_ERROR, "", {}, "Non-unique id in merged module");
632     return SPV_ERROR_INVALID_ID;
633   }
634 
635   return SPV_SUCCESS;
636 }
637 
638 }  // namespace
639 
Link(const Context & context,const std::vector<std::vector<uint32_t>> & binaries,std::vector<uint32_t> * linked_binary,const LinkerOptions & options)640 spv_result_t Link(const Context& context,
641                   const std::vector<std::vector<uint32_t>>& binaries,
642                   std::vector<uint32_t>* linked_binary,
643                   const LinkerOptions& options) {
644   std::vector<const uint32_t*> binary_ptrs;
645   binary_ptrs.reserve(binaries.size());
646   std::vector<size_t> binary_sizes;
647   binary_sizes.reserve(binaries.size());
648 
649   for (const auto& binary : binaries) {
650     binary_ptrs.push_back(binary.data());
651     binary_sizes.push_back(binary.size());
652   }
653 
654   return Link(context, binary_ptrs.data(), binary_sizes.data(), binaries.size(),
655               linked_binary, options);
656 }
657 
Link(const Context & context,const uint32_t * const * binaries,const size_t * binary_sizes,size_t num_binaries,std::vector<uint32_t> * linked_binary,const LinkerOptions & options)658 spv_result_t Link(const Context& context, const uint32_t* const* binaries,
659                   const size_t* binary_sizes, size_t num_binaries,
660                   std::vector<uint32_t>* linked_binary,
661                   const LinkerOptions& options) {
662   spv_position_t position = {};
663   const spv_context& c_context = context.CContext();
664   const MessageConsumer& consumer = c_context->consumer;
665 
666   linked_binary->clear();
667   if (num_binaries == 0u)
668     return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY)
669            << "No modules were given.";
670 
671   std::vector<std::unique_ptr<IRContext>> ir_contexts;
672   std::vector<Module*> modules;
673   modules.reserve(num_binaries);
674   for (size_t i = 0u; i < num_binaries; ++i) {
675     const uint32_t schema = binaries[i][4u];
676     if (schema != 0u) {
677       position.index = 4u;
678       return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY)
679              << "Schema is non-zero for module " << i << ".";
680     }
681 
682     std::unique_ptr<IRContext> ir_context = BuildModule(
683         c_context->target_env, consumer, binaries[i], binary_sizes[i]);
684     if (ir_context == nullptr)
685       return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY)
686              << "Failed to build a module out of " << ir_contexts.size() << ".";
687     modules.push_back(ir_context->module());
688     ir_contexts.push_back(std::move(ir_context));
689   }
690 
691   // Phase 1: Shift the IDs used in each binary so that they occupy a disjoint
692   //          range from the other binaries, and compute the new ID bound.
693   uint32_t max_id_bound = 0u;
694   spv_result_t res = ShiftIdsInModules(consumer, &modules, &max_id_bound);
695   if (res != SPV_SUCCESS) return res;
696 
697   // Phase 2: Generate the header
698   opt::ModuleHeader header;
699   res = GenerateHeader(consumer, modules, max_id_bound, &header);
700   if (res != SPV_SUCCESS) return res;
701   IRContext linked_context(c_context->target_env, consumer);
702   linked_context.module()->SetHeader(header);
703 
704   // Phase 3: Merge all the binaries into a single one.
705   AssemblyGrammar grammar(c_context);
706   res = MergeModules(consumer, modules, grammar, &linked_context);
707   if (res != SPV_SUCCESS) return res;
708 
709   if (options.GetVerifyIds()) {
710     res = VerifyIds(consumer, &linked_context);
711     if (res != SPV_SUCCESS) return res;
712   }
713 
714   // Phase 4: Find the import/export pairs
715   LinkageTable linkings_to_do;
716   res = GetImportExportPairs(consumer, linked_context,
717                              *linked_context.get_def_use_mgr(),
718                              *linked_context.get_decoration_mgr(),
719                              options.GetAllowPartialLinkage(), &linkings_to_do);
720   if (res != SPV_SUCCESS) return res;
721 
722   // Phase 5: Ensure the import and export have the same types and decorations.
723   res =
724       CheckImportExportCompatibility(consumer, linkings_to_do, &linked_context);
725   if (res != SPV_SUCCESS) return res;
726 
727   // Phase 6: Remove duplicates
728   PassManager manager;
729   manager.SetMessageConsumer(consumer);
730   manager.AddPass<RemoveDuplicatesPass>();
731   opt::Pass::Status pass_res = manager.Run(&linked_context);
732   if (pass_res == opt::Pass::Status::Failure) return SPV_ERROR_INVALID_DATA;
733 
734   // Phase 7: Remove all names and decorations of import variables/functions
735   for (const auto& linking_entry : linkings_to_do) {
736     linked_context.KillNamesAndDecorates(linking_entry.imported_symbol.id);
737     for (const auto parameter_id :
738          linking_entry.imported_symbol.parameter_ids) {
739       linked_context.KillNamesAndDecorates(parameter_id);
740     }
741   }
742 
743   // Phase 8: Rematch import variables/functions to export variables/functions
744   for (const auto& linking_entry : linkings_to_do) {
745     linked_context.ReplaceAllUsesWith(linking_entry.imported_symbol.id,
746                                       linking_entry.exported_symbol.id);
747   }
748 
749   // Phase 9: Remove linkage specific instructions, such as import/export
750   // attributes, linkage capability, etc. if applicable
751   res = RemoveLinkageSpecificInstructions(consumer, options, linkings_to_do,
752                                           linked_context.get_decoration_mgr(),
753                                           &linked_context);
754   if (res != SPV_SUCCESS) return res;
755 
756   // Phase 10: Compact the IDs used in the module
757   manager.AddPass<opt::CompactIdsPass>();
758   pass_res = manager.Run(&linked_context);
759   if (pass_res == opt::Pass::Status::Failure) return SPV_ERROR_INVALID_DATA;
760 
761   // Phase 11: Output the module
762   linked_context.module()->ToBinary(linked_binary, true);
763 
764   return SPV_SUCCESS;
765 }
766 
767 }  // namespace spvtools
768