summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source/link/linker.cpp321
1 files changed, 162 insertions, 159 deletions
diff --git a/source/link/linker.cpp b/source/link/linker.cpp
index 16b43a28..de793765 100644
--- a/source/link/linker.cpp
+++ b/source/link/linker.cpp
@@ -36,6 +36,7 @@
#include "spirv_target_env.h"
namespace spvtools {
+namespace {
using opt::IRContext;
using opt::Instruction;
@@ -72,9 +73,9 @@ using LinkageTable = std::vector<LinkageEntry>;
// Both |modules| and |max_id_bound| should not be null, and |modules| should
// not be empty either. Furthermore |modules| should not contain any null
// pointers.
-static spv_result_t ShiftIdsInModules(const MessageConsumer& consumer,
- std::vector<opt::Module*>* modules,
- uint32_t* max_id_bound);
+spv_result_t ShiftIdsInModules(const MessageConsumer& consumer,
+ std::vector<opt::Module*>* modules,
+ uint32_t* max_id_bound);
// Generates the header for the linked module and returns it in |header|.
//
@@ -84,19 +85,18 @@ static spv_result_t ShiftIdsInModules(const MessageConsumer& consumer,
// TODO(pierremoreau): What to do when binaries use different versions of
// SPIR-V? For now, use the max of all versions found in
// the input modules.
-static spv_result_t GenerateHeader(const MessageConsumer& consumer,
- const std::vector<opt::Module*>& modules,
- uint32_t max_id_bound,
- opt::ModuleHeader* header);
+spv_result_t GenerateHeader(const MessageConsumer& consumer,
+ const std::vector<opt::Module*>& modules,
+ uint32_t max_id_bound, opt::ModuleHeader* header);
// Merge all the modules from |in_modules| into a single module owned by
// |linked_context|.
//
// |linked_context| should not be null.
-static spv_result_t MergeModules(const MessageConsumer& consumer,
- const std::vector<Module*>& in_modules,
- const AssemblyGrammar& grammar,
- IRContext* linked_context);
+spv_result_t MergeModules(const MessageConsumer& consumer,
+ const std::vector<Module*>& in_modules,
+ const AssemblyGrammar& grammar,
+ IRContext* linked_context);
// Compute all pairs of import and export and return it in |linkings_to_do|.
//
@@ -107,20 +107,21 @@ static spv_result_t MergeModules(const MessageConsumer& consumer,
// applied to a single ID.)
// TODO(pierremoreau): What should be the proper behaviour with built-in
// symbols?
-static spv_result_t 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);
+spv_result_t 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);
// Checks that for each pair of import and export, the import and export have
// the same type as well as the same decorations.
//
// TODO(pierremoreau): Decorations on functions parameters are currently not
// checked.
-static spv_result_t CheckImportExportCompatibility(
- const MessageConsumer& consumer, const LinkageTable& linkings_to_do,
- opt::IRContext* context);
+spv_result_t CheckImportExportCompatibility(const MessageConsumer& consumer,
+ const LinkageTable& linkings_to_do,
+ opt::IRContext* context);
// Remove linkage specific instructions, such as prototypes of imported
// functions, declarations of imported variables, import (and export if
@@ -134,136 +135,19 @@ static spv_result_t CheckImportExportCompatibility(
// applied to a single ID.)
// TODO(pierremoreau): Run a pass for removing dead instructions, for example
// OpName for prototypes of imported funcions.
-static spv_result_t RemoveLinkageSpecificInstructions(
+spv_result_t RemoveLinkageSpecificInstructions(
const MessageConsumer& consumer, const LinkerOptions& options,
const LinkageTable& linkings_to_do, DecorationManager* decoration_manager,
opt::IRContext* linked_context);
// Verify that the unique ids of each instruction in |linked_context| (i.e. the
// merged module) are truly unique. Does not check the validity of other ids
-static spv_result_t VerifyIds(const MessageConsumer& consumer,
- opt::IRContext* linked_context);
-
-spv_result_t Link(const Context& context,
- const std::vector<std::vector<uint32_t>>& binaries,
- std::vector<uint32_t>* linked_binary,
- const LinkerOptions& options) {
- std::vector<const uint32_t*> binary_ptrs;
- binary_ptrs.reserve(binaries.size());
- std::vector<size_t> binary_sizes;
- binary_sizes.reserve(binaries.size());
-
- for (const auto& binary : binaries) {
- binary_ptrs.push_back(binary.data());
- binary_sizes.push_back(binary.size());
- }
-
- return Link(context, binary_ptrs.data(), binary_sizes.data(), binaries.size(),
- linked_binary, options);
-}
-
-spv_result_t 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) {
- spv_position_t position = {};
- const spv_context& c_context = context.CContext();
- const MessageConsumer& consumer = c_context->consumer;
-
- linked_binary->clear();
- if (num_binaries == 0u)
- return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY)
- << "No modules were given.";
-
- std::vector<std::unique_ptr<IRContext>> ir_contexts;
- std::vector<Module*> modules;
- modules.reserve(num_binaries);
- for (size_t i = 0u; i < num_binaries; ++i) {
- const uint32_t schema = binaries[i][4u];
- if (schema != 0u) {
- position.index = 4u;
- return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY)
- << "Schema is non-zero for module " << i << ".";
- }
-
- std::unique_ptr<IRContext> ir_context = BuildModule(
- c_context->target_env, consumer, binaries[i], binary_sizes[i]);
- if (ir_context == nullptr)
- return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY)
- << "Failed to build a module out of " << ir_contexts.size() << ".";
- modules.push_back(ir_context->module());
- ir_contexts.push_back(std::move(ir_context));
- }
-
- // Phase 1: Shift the IDs used in each binary so that they occupy a disjoint
- // range from the other binaries, and compute the new ID bound.
- uint32_t max_id_bound = 0u;
- spv_result_t res = ShiftIdsInModules(consumer, &modules, &max_id_bound);
- if (res != SPV_SUCCESS) return res;
-
- // Phase 2: Generate the header
- opt::ModuleHeader header;
- res = GenerateHeader(consumer, modules, max_id_bound, &header);
- if (res != SPV_SUCCESS) return res;
- IRContext linked_context(c_context->target_env, consumer);
- linked_context.module()->SetHeader(header);
-
- // Phase 3: Merge all the binaries into a single one.
- AssemblyGrammar grammar(c_context);
- res = MergeModules(consumer, modules, grammar, &linked_context);
- if (res != SPV_SUCCESS) return res;
-
- if (options.GetVerifyIds()) {
- res = VerifyIds(consumer, &linked_context);
- if (res != SPV_SUCCESS) return res;
- }
-
- // Phase 4: Find the import/export pairs
- LinkageTable linkings_to_do;
- res = GetImportExportPairs(consumer, linked_context,
- *linked_context.get_def_use_mgr(),
- *linked_context.get_decoration_mgr(),
- options.GetAllowPartialLinkage(), &linkings_to_do);
- if (res != SPV_SUCCESS) return res;
-
- // Phase 5: Ensure the import and export have the same types and decorations.
- res =
- CheckImportExportCompatibility(consumer, linkings_to_do, &linked_context);
- if (res != SPV_SUCCESS) return res;
-
- // Phase 6: Remove duplicates
- PassManager manager;
- manager.SetMessageConsumer(consumer);
- manager.AddPass<RemoveDuplicatesPass>();
- opt::Pass::Status pass_res = manager.Run(&linked_context);
- if (pass_res == opt::Pass::Status::Failure) return SPV_ERROR_INVALID_DATA;
-
- // Phase 7: Rematch import variables/functions to export variables/functions
- for (const auto& linking_entry : linkings_to_do)
- linked_context.ReplaceAllUsesWith(linking_entry.imported_symbol.id,
- linking_entry.exported_symbol.id);
-
- // Phase 8: Remove linkage specific instructions, such as import/export
- // attributes, linkage capability, etc. if applicable
- res = RemoveLinkageSpecificInstructions(consumer, options, linkings_to_do,
- linked_context.get_decoration_mgr(),
- &linked_context);
- if (res != SPV_SUCCESS) return res;
-
- // Phase 9: Compact the IDs used in the module
- manager.AddPass<opt::CompactIdsPass>();
- pass_res = manager.Run(&linked_context);
- if (pass_res == opt::Pass::Status::Failure) return SPV_ERROR_INVALID_DATA;
-
- // Phase 10: Output the module
- linked_context.module()->ToBinary(linked_binary, true);
-
- return SPV_SUCCESS;
-}
+spv_result_t VerifyIds(const MessageConsumer& consumer,
+ opt::IRContext* linked_context);
-static spv_result_t ShiftIdsInModules(const MessageConsumer& consumer,
- std::vector<opt::Module*>* modules,
- uint32_t* max_id_bound) {
+spv_result_t ShiftIdsInModules(const MessageConsumer& consumer,
+ std::vector<opt::Module*>* modules,
+ uint32_t* max_id_bound) {
spv_position_t position = {};
if (modules == nullptr)
@@ -303,10 +187,9 @@ static spv_result_t ShiftIdsInModules(const MessageConsumer& consumer,
return SPV_SUCCESS;
}
-static spv_result_t GenerateHeader(const MessageConsumer& consumer,
- const std::vector<opt::Module*>& modules,
- uint32_t max_id_bound,
- opt::ModuleHeader* header) {
+spv_result_t GenerateHeader(const MessageConsumer& consumer,
+ const std::vector<opt::Module*>& modules,
+ uint32_t max_id_bound, opt::ModuleHeader* header) {
spv_position_t position = {};
if (modules.empty())
@@ -329,10 +212,10 @@ static spv_result_t GenerateHeader(const MessageConsumer& consumer,
return SPV_SUCCESS;
}
-static spv_result_t MergeModules(const MessageConsumer& consumer,
- const std::vector<Module*>& input_modules,
- const AssemblyGrammar& grammar,
- IRContext* linked_context) {
+spv_result_t MergeModules(const MessageConsumer& consumer,
+ const std::vector<Module*>& input_modules,
+ const AssemblyGrammar& grammar,
+ IRContext* linked_context) {
spv_position_t position = {};
if (linked_context == nullptr)
@@ -486,11 +369,12 @@ static spv_result_t MergeModules(const MessageConsumer& consumer,
return SPV_SUCCESS;
}
-static spv_result_t 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) {
+spv_result_t 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) {
spv_position_t position = {};
if (linkings_to_do == nullptr)
@@ -582,9 +466,9 @@ static spv_result_t GetImportExportPairs(
return SPV_SUCCESS;
}
-static spv_result_t CheckImportExportCompatibility(
- const MessageConsumer& consumer, const LinkageTable& linkings_to_do,
- opt::IRContext* context) {
+spv_result_t CheckImportExportCompatibility(const MessageConsumer& consumer,
+ const LinkageTable& linkings_to_do,
+ opt::IRContext* context) {
spv_position_t position = {};
// Ensure th import and export types are the same.
@@ -625,7 +509,7 @@ static spv_result_t CheckImportExportCompatibility(
return SPV_SUCCESS;
}
-static spv_result_t RemoveLinkageSpecificInstructions(
+spv_result_t RemoveLinkageSpecificInstructions(
const MessageConsumer& consumer, const LinkerOptions& options,
const LinkageTable& linkings_to_do, DecorationManager* decoration_manager,
opt::IRContext* linked_context) {
@@ -762,4 +646,123 @@ spv_result_t VerifyIds(const MessageConsumer& consumer,
return SPV_SUCCESS;
}
+} // namespace
+
+spv_result_t Link(const Context& context,
+ const std::vector<std::vector<uint32_t>>& binaries,
+ std::vector<uint32_t>* linked_binary,
+ const LinkerOptions& options) {
+ std::vector<const uint32_t*> binary_ptrs;
+ binary_ptrs.reserve(binaries.size());
+ std::vector<size_t> binary_sizes;
+ binary_sizes.reserve(binaries.size());
+
+ for (const auto& binary : binaries) {
+ binary_ptrs.push_back(binary.data());
+ binary_sizes.push_back(binary.size());
+ }
+
+ return Link(context, binary_ptrs.data(), binary_sizes.data(), binaries.size(),
+ linked_binary, options);
+}
+
+spv_result_t 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) {
+ spv_position_t position = {};
+ const spv_context& c_context = context.CContext();
+ const MessageConsumer& consumer = c_context->consumer;
+
+ linked_binary->clear();
+ if (num_binaries == 0u)
+ return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY)
+ << "No modules were given.";
+
+ std::vector<std::unique_ptr<IRContext>> ir_contexts;
+ std::vector<Module*> modules;
+ modules.reserve(num_binaries);
+ for (size_t i = 0u; i < num_binaries; ++i) {
+ const uint32_t schema = binaries[i][4u];
+ if (schema != 0u) {
+ position.index = 4u;
+ return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY)
+ << "Schema is non-zero for module " << i << ".";
+ }
+
+ std::unique_ptr<IRContext> ir_context = BuildModule(
+ c_context->target_env, consumer, binaries[i], binary_sizes[i]);
+ if (ir_context == nullptr)
+ return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY)
+ << "Failed to build a module out of " << ir_contexts.size() << ".";
+ modules.push_back(ir_context->module());
+ ir_contexts.push_back(std::move(ir_context));
+ }
+
+ // Phase 1: Shift the IDs used in each binary so that they occupy a disjoint
+ // range from the other binaries, and compute the new ID bound.
+ uint32_t max_id_bound = 0u;
+ spv_result_t res = ShiftIdsInModules(consumer, &modules, &max_id_bound);
+ if (res != SPV_SUCCESS) return res;
+
+ // Phase 2: Generate the header
+ opt::ModuleHeader header;
+ res = GenerateHeader(consumer, modules, max_id_bound, &header);
+ if (res != SPV_SUCCESS) return res;
+ IRContext linked_context(c_context->target_env, consumer);
+ linked_context.module()->SetHeader(header);
+
+ // Phase 3: Merge all the binaries into a single one.
+ AssemblyGrammar grammar(c_context);
+ res = MergeModules(consumer, modules, grammar, &linked_context);
+ if (res != SPV_SUCCESS) return res;
+
+ if (options.GetVerifyIds()) {
+ res = VerifyIds(consumer, &linked_context);
+ if (res != SPV_SUCCESS) return res;
+ }
+
+ // Phase 4: Find the import/export pairs
+ LinkageTable linkings_to_do;
+ res = GetImportExportPairs(consumer, linked_context,
+ *linked_context.get_def_use_mgr(),
+ *linked_context.get_decoration_mgr(),
+ options.GetAllowPartialLinkage(), &linkings_to_do);
+ if (res != SPV_SUCCESS) return res;
+
+ // Phase 5: Ensure the import and export have the same types and decorations.
+ res =
+ CheckImportExportCompatibility(consumer, linkings_to_do, &linked_context);
+ if (res != SPV_SUCCESS) return res;
+
+ // Phase 6: Remove duplicates
+ PassManager manager;
+ manager.SetMessageConsumer(consumer);
+ manager.AddPass<RemoveDuplicatesPass>();
+ opt::Pass::Status pass_res = manager.Run(&linked_context);
+ if (pass_res == opt::Pass::Status::Failure) return SPV_ERROR_INVALID_DATA;
+
+ // Phase 7: Rematch import variables/functions to export variables/functions
+ for (const auto& linking_entry : linkings_to_do)
+ linked_context.ReplaceAllUsesWith(linking_entry.imported_symbol.id,
+ linking_entry.exported_symbol.id);
+
+ // Phase 8: Remove linkage specific instructions, such as import/export
+ // attributes, linkage capability, etc. if applicable
+ res = RemoveLinkageSpecificInstructions(consumer, options, linkings_to_do,
+ linked_context.get_decoration_mgr(),
+ &linked_context);
+ if (res != SPV_SUCCESS) return res;
+
+ // Phase 9: Compact the IDs used in the module
+ manager.AddPass<opt::CompactIdsPass>();
+ pass_res = manager.Run(&linked_context);
+ if (pass_res == opt::Pass::Status::Failure) return SPV_ERROR_INVALID_DATA;
+
+ // Phase 10: Output the module
+ linked_context.module()->ToBinary(linked_binary, true);
+
+ return SPV_SUCCESS;
+}
+
} // namespace spvtools