linker: Add --use-highest-version option (#5376)

Currently spirv-link fails if all input files don't use the same
SPIR-V version. Add an option to instead use the highest input
version as the output version. Note that if one of the 'old'
input files uses an opcode that is deprecated in the 'new'
version, the output spirv will be invalid.
This commit is contained in:
Jeremy Gebben 2023-08-21 17:05:33 -06:00 committed by GitHub
parent bfc94f63a7
commit 6520d83eff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 44 additions and 13 deletions

View File

@ -26,11 +26,6 @@ namespace spvtools {
class LinkerOptions {
public:
LinkerOptions()
: create_library_(false),
verify_ids_(false),
allow_partial_linkage_(false) {}
// Returns whether a library or an executable should be produced by the
// linking phase.
//
@ -63,10 +58,16 @@ class LinkerOptions {
allow_partial_linkage_ = allow_partial_linkage;
}
bool GetUseHighestVersion() const { return use_highest_version_; }
void SetUseHighestVersion(bool use_highest_vers) {
use_highest_version_ = use_highest_vers;
}
private:
bool create_library_;
bool verify_ids_;
bool allow_partial_linkage_;
bool create_library_{false};
bool verify_ids_{false};
bool allow_partial_linkage_{false};
bool use_highest_version_{false};
};
// Links one or more SPIR-V modules into a new SPIR-V module. That is, combine

View File

@ -91,7 +91,8 @@ spv_result_t ShiftIdsInModules(const MessageConsumer& consumer,
// should be non-null. |max_id_bound| should be strictly greater than 0.
spv_result_t GenerateHeader(const MessageConsumer& consumer,
const std::vector<opt::Module*>& modules,
uint32_t max_id_bound, opt::ModuleHeader* header);
uint32_t max_id_bound, opt::ModuleHeader* header,
const LinkerOptions& options);
// Merge all the modules from |in_modules| into a single module owned by
// |linked_context|.
@ -202,7 +203,8 @@ spv_result_t ShiftIdsInModules(const MessageConsumer& consumer,
spv_result_t GenerateHeader(const MessageConsumer& consumer,
const std::vector<opt::Module*>& modules,
uint32_t max_id_bound, opt::ModuleHeader* header) {
uint32_t max_id_bound, opt::ModuleHeader* header,
const LinkerOptions& options) {
spv_position_t position = {};
if (modules.empty())
@ -212,10 +214,12 @@ spv_result_t GenerateHeader(const MessageConsumer& consumer,
return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_DATA)
<< "|max_id_bound| of GenerateHeader should not be null.";
const uint32_t linked_version = modules.front()->version();
uint32_t linked_version = modules.front()->version();
for (std::size_t i = 1; i < modules.size(); ++i) {
const uint32_t module_version = modules[i]->version();
if (module_version != linked_version)
if (options.GetUseHighestVersion()) {
linked_version = std::max(linked_version, module_version);
} else if (module_version != linked_version) {
return DiagnosticStream({0, 0, 1}, consumer, "", SPV_ERROR_INTERNAL)
<< "Conflicting SPIR-V versions: "
<< SPV_SPIRV_VERSION_MAJOR_PART(linked_version) << "."
@ -224,6 +228,7 @@ spv_result_t GenerateHeader(const MessageConsumer& consumer,
<< SPV_SPIRV_VERSION_MAJOR_PART(module_version) << "."
<< SPV_SPIRV_VERSION_MINOR_PART(module_version)
<< " (input module " << (i + 1) << ").";
}
}
header->magic_number = spv::MagicNumber;
@ -753,7 +758,7 @@ spv_result_t Link(const Context& context, const uint32_t* const* binaries,
// Phase 2: Generate the header
opt::ModuleHeader header;
res = GenerateHeader(consumer, modules, max_id_bound, &header);
res = GenerateHeader(consumer, modules, max_id_bound, &header, options);
if (res != SPV_SUCCESS) return res;
IRContext linked_context(c_context->target_env, consumer);
linked_context.module()->SetHeader(header);

View File

@ -73,5 +73,21 @@ TEST_F(BinaryVersion, Mismatch) {
"through 1) vs 1.5 (input module 2)."));
}
TEST_F(BinaryVersion, UseHighest) {
// clang-format off
spvtest::Binaries binaries = {
CreateBinary(SPV_SPIRV_VERSION_WORD(1, 3)),
CreateBinary(SPV_SPIRV_VERSION_WORD(1, 5)),
};
// clang-format on
LinkerOptions options;
options.SetUseHighestVersion(true);
spvtest::Binary linked_binary;
ASSERT_EQ(SPV_SUCCESS, Link(binaries, &linked_binary, options))
<< GetErrorMessage();
EXPECT_THAT(GetErrorMessage(), std::string());
EXPECT_EQ(SPV_SPIRV_VERSION_WORD(1, 5), linked_binary[1]);
}
} // namespace
} // namespace spvtools

View File

@ -59,6 +59,13 @@ Options (in lexicographical order):
NOTE: The SPIR-V version used by the linked binary module
depends only on the version of the inputs, and is not affected
by this option.
--use-highest-version
Upgrade the output SPIR-V version to the highest of the input
files, instead of requiring all of them to have the same
version.
NOTE: If one of the older input files uses an instruction that
is deprecated in the highest SPIR-V version, the output will
be invalid.
--verify-ids
Verify that IDs in the resulting modules are truly unique.
--version
@ -78,6 +85,7 @@ FLAG_LONG_bool( create_library, /* default_value= */ false,
FLAG_LONG_bool( allow_partial_linkage, /* default_value= */ false, /* required= */ false);
FLAG_SHORT_string(o, /* default_value= */ "", /* required= */ false);
FLAG_LONG_string( target_env, /* default_value= */ kDefaultEnvironment, /* required= */ false);
FLAG_LONG_bool( use_highest_version, /* default_value= */ false, /* required= */ false);
// clang-format on
int main(int, const char* argv[]) {
@ -120,6 +128,7 @@ int main(int, const char* argv[]) {
options.SetAllowPartialLinkage(flags::allow_partial_linkage.value());
options.SetCreateLibrary(flags::create_library.value());
options.SetVerifyIds(flags::verify_ids.value());
options.SetUseHighestVersion(flags::use_highest_version.value());
if (inFiles.empty()) {
fprintf(stderr, "error: No input file specified\n");