static spv_result_t GetImportExportPairs(
const MessageConsumer& consumer, const ir::IRContext& linked_context,
const DefUseManager& def_use_manager,
- const DecorationManager& decoration_manager, LinkageTable* linkings_to_do);
+ 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): Run a pass for removing dead instructions, for example
// OpName for prototypes of imported funcions.
static spv_result_t RemoveLinkageSpecificInstructions(
- const MessageConsumer& consumer, bool create_executable,
+ const MessageConsumer& consumer, const LinkerOptions& options,
const LinkageTable& linkings_to_do, DecorationManager* decoration_manager,
ir::IRContext* linked_context);
// 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(), &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.
// Phase 8: Remove linkage specific instructions, such as import/export
// attributes, linkage capability, etc. if applicable
- res = RemoveLinkageSpecificInstructions(
- consumer, !options.GetCreateLibrary(), linkings_to_do,
- linked_context.get_decoration_mgr(), &linked_context);
+ 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
static spv_result_t GetImportExportPairs(
const MessageConsumer& consumer, const ir::IRContext& linked_context,
const DefUseManager& def_use_manager,
- const DecorationManager& decoration_manager, LinkageTable* linkings_to_do) {
+ const DecorationManager& decoration_manager, bool allow_partial_linkage,
+ LinkageTable* linkings_to_do) {
spv_position_t position = {};
if (linkings_to_do == nullptr)
std::vector<LinkageSymbolInfo> possible_exports;
const auto& exp = exports.find(import.name);
if (exp != exports.end()) possible_exports = exp->second;
- if (possible_exports.empty())
+ if (possible_exports.empty() && !allow_partial_linkage)
return libspirv::DiagnosticStream(position, consumer,
SPV_ERROR_INVALID_BINARY)
<< "Unresolved external reference to \"" << import.name << "\".";
<< "Too many external references, " << possible_exports.size()
<< ", were found for \"" << import.name << "\".";
- linkings_to_do->emplace_back(import, possible_exports.front());
+ if (!possible_exports.empty())
+ linkings_to_do->emplace_back(import, possible_exports.front());
}
return SPV_SUCCESS;
}
static spv_result_t RemoveLinkageSpecificInstructions(
- const MessageConsumer& consumer, bool create_executable,
+ const MessageConsumer& consumer, const LinkerOptions& options,
const LinkageTable& linkings_to_do, DecorationManager* decoration_manager,
ir::IRContext* linked_context) {
spv_position_t position = {};
}
}
+ // If partial linkage is allowed, we need an efficient way to check whether
+ // an imported ID had a corresponding export symbol. As uses of the imported
+ // symbol have already been replaced by the exported symbol, use the exported
+ // symbol ID.
+ // TODO(pierremoreau): This will not work if the decoration is applied
+ // through a group, but the linker does not support that
+ // either.
+ std::unordered_set<SpvId> imports;
+ if (options.GetAllowPartialLinkage()) {
+ imports.reserve(linkings_to_do.size());
+ for (const auto& linking_entry : linkings_to_do)
+ imports.emplace(linking_entry.exported_symbol.id);
+ }
+
// Remove import linkage attributes
auto next = linked_context->annotation_begin();
for (auto inst = next; inst != linked_context->annotation_end();
inst = next) {
++next;
+ // If this is an import annotation:
+ // * if we do not allow partial linkage, remove all import annotations;
+ // * otherwise, remove the annotation only if there was a corresponding
+ // export.
if (inst->opcode() == SpvOpDecorate &&
inst->GetSingleWordOperand(1u) == SpvDecorationLinkageAttributes &&
- inst->GetSingleWordOperand(3u) == SpvLinkageTypeImport) {
+ inst->GetSingleWordOperand(3u) == SpvLinkageTypeImport &&
+ (!options.GetAllowPartialLinkage() ||
+ imports.find(inst->GetSingleWordOperand(0u)) != imports.end())) {
linked_context->KillInst(&*inst);
}
}
- // Remove export linkage attributes and Linkage capability if making an
- // executable
- if (create_executable) {
+ // Remove export linkage attributes if making an executable
+ if (!options.GetCreateLibrary()) {
next = linked_context->annotation_begin();
for (auto inst = next; inst != linked_context->annotation_end();
inst = next) {
linked_context->KillInst(&*inst);
}
}
+ }
+ // Remove Linkage capability if making an executable and partial linkage is
+ // not allowed
+ if (!options.GetCreateLibrary() && !options.GetAllowPartialLinkage()) {
for (auto& inst : linked_context->capabilities())
if (inst.GetSingleWordInOperand(0u) == SpvCapabilityLinkage) {
linked_context->KillInst(&inst);
--- /dev/null
+// Copyright (c) 2018 Pierre Moreau
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "gmock/gmock.h"
+#include "linker_fixture.h"
+
+namespace {
+
+using ::testing::HasSubstr;
+using PartialLinkage = spvtest::LinkerTest;
+
+TEST_F(PartialLinkage, Allowed) {
+ const std::string body1 = R"(
+OpCapability Linkage
+OpDecorate %1 LinkageAttributes "foo" Import
+OpDecorate %2 LinkageAttributes "bar" Import
+%3 = OpTypeFloat 32
+%1 = OpVariable %3 Uniform
+%2 = OpVariable %3 Uniform
+)";
+ const std::string body2 = R"(
+OpCapability Linkage
+OpDecorate %1 LinkageAttributes "bar" Export
+%2 = OpTypeFloat 32
+%3 = OpConstant %2 3.14159
+%1 = OpVariable %2 Uniform %3
+)";
+
+ spvtest::Binary linked_binary;
+ spvtools::LinkerOptions linker_options;
+ linker_options.SetAllowPartialLinkage(true);
+ ASSERT_EQ(SPV_SUCCESS,
+ AssembleAndLink({body1, body2}, &linked_binary, linker_options));
+
+ const std::string expected_res = R"(OpCapability Linkage
+OpDecorate %1 LinkageAttributes "foo" Import
+%2 = OpTypeFloat 32
+%1 = OpVariable %2 Uniform
+%3 = OpConstant %2 3.14159
+%4 = OpVariable %2 Uniform %3
+)";
+ std::string res_body;
+ SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
+ ASSERT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body))
+ << GetErrorMessage();
+ EXPECT_EQ(expected_res, res_body);
+}
+
+TEST_F(PartialLinkage, Disallowed) {
+ const std::string body1 = R"(
+OpCapability Linkage
+OpDecorate %1 LinkageAttributes "foo" Import
+OpDecorate %2 LinkageAttributes "bar" Import
+%3 = OpTypeFloat 32
+%1 = OpVariable %3 Uniform
+%2 = OpVariable %3 Uniform
+)";
+ const std::string body2 = R"(
+OpCapability Linkage
+OpDecorate %1 LinkageAttributes "bar" Export
+%2 = OpTypeFloat 32
+%3 = OpConstant %2 3.14159
+%1 = OpVariable %2 Uniform %3
+)";
+
+ spvtest::Binary linked_binary;
+ EXPECT_EQ(SPV_ERROR_INVALID_BINARY,
+ AssembleAndLink({body1, body2}, &linked_binary));
+ EXPECT_THAT(GetErrorMessage(),
+ HasSubstr("Unresolved external reference to \"foo\"."));
+}
+
+} // anonymous namespace
NOTE: The linker is a work in progress.
Options:
- -h, --help Print this help.
- -o Name of the resulting linked SPIR-V binary.
- --create-library Link the binaries into a library, keeping all exported symbols.
- --verify-ids Verify that IDs in the resulting modules are truly unique.
- --version Display linker version information
- --target-env {vulkan1.0|spv1.0|spv1.1|spv1.2|opencl2.1|opencl2.2}
- Use Vulkan1.0/SPIR-V1.0/SPIR-V1.1/SPIR-V1.2/OpenCL-2.1/OpenCL2.2 validation rules.
+ -h, --help Print this help.
+ -o Name of the resulting linked SPIR-V binary.
+ --create-library Link the binaries into a library, keeping all exported symbols.
+ --allow-partial-linkage Allow partial linkage by accepting imported symbols to be unresolved.
+ --verify-ids Verify that IDs in the resulting modules are truly unique.
+ --version Display linker version information
+ --target-env {vulkan1.0|spv1.0|spv1.1|spv1.2|opencl2.1|opencl2.2}
+ Use Vulkan1.0/SPIR-V1.0/SPIR-V1.1/SPIR-V1.2/OpenCL-2.1/OpenCL2.2 validation rules.
)",
argv0, argv0);
}
options.SetCreateLibrary(true);
} else if (0 == strcmp(cur_arg, "--verify-ids")) {
options.SetVerifyIds(true);
+ } else if (0 == strcmp(cur_arg, "--allow-partial-linkage")) {
+ options.SetAllowPartialLinkage(true);
} else if (0 == strcmp(cur_arg, "--version")) {
printf("%s\n", spvSoftwareVersionDetailsString());
// TODO(dneto): Add OpenCL 2.2 at least.