linker.cpp 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  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. #include "spirv-tools/linker.hpp"
  15. #include <cassert>
  16. #include <cstring>
  17. #include <iostream>
  18. #include <vector>
  19. #include "source/spirv_target_env.h"
  20. #include "source/table.h"
  21. #include "spirv-tools/libspirv.hpp"
  22. #include "tools/io.h"
  23. #include "tools/util/flags.h"
  24. namespace {
  25. constexpr auto kDefaultEnvironment = "spv1.6";
  26. void print_usage(const char* program) {
  27. std::string target_env_list = spvTargetEnvList(16, 80);
  28. // NOTE: Please maintain flags in lexicographical order.
  29. printf(
  30. R"(%s - Link SPIR-V binary files together.
  31. USAGE: %s [options] [-o <output>] <input>...
  32. The SPIR-V binaries are read from the different <input>(s).
  33. The SPIR-V resulting linked binary module is written to the file "out.spv"
  34. unless the -o option is used; if <output> is "-", it is written to the standard
  35. output.
  36. NOTE: The linker is a work in progress.
  37. Options (in lexicographical order):
  38. --allow-partial-linkage
  39. Allow partial linkage by accepting imported symbols to be
  40. unresolved.
  41. --allow-pointer-mismatch
  42. Allow pointer function parameters to mismatch the target link
  43. target. This is useful to workaround lost correct parameter type
  44. information due to LLVM's opaque pointers.
  45. --create-library
  46. Link the binaries into a library, keeping all exported symbols.
  47. -h, --help
  48. Print this help.
  49. --target-env <env>
  50. Set the environment used for interpreting the inputs. Without
  51. this option the environment defaults to spv1.6. <env> must be
  52. one of {%s}.
  53. NOTE: The SPIR-V version used by the linked binary module
  54. depends only on the version of the inputs, and is not affected
  55. by this option.
  56. --use-highest-version
  57. Upgrade the output SPIR-V version to the highest of the input
  58. files, instead of requiring all of them to have the same
  59. version.
  60. NOTE: If one of the older input files uses an instruction that
  61. is deprecated in the highest SPIR-V version, the output will
  62. be invalid.
  63. --verify-ids
  64. Verify that IDs in the resulting modules are truly unique.
  65. --version
  66. Display linker version information.
  67. )",
  68. program, program, target_env_list.c_str());
  69. }
  70. } // namespace
  71. // clang-format off
  72. FLAG_SHORT_bool( h, /* default_value= */ false, /* required= */ false);
  73. FLAG_LONG_bool( help, /* default_value= */ false, /* required= */ false);
  74. FLAG_LONG_bool( version, /* default_value= */ false, /* required= */ false);
  75. FLAG_LONG_bool( verify_ids, /* default_value= */ false, /* required= */ false);
  76. FLAG_LONG_bool( create_library, /* default_value= */ false, /* required= */ false);
  77. FLAG_LONG_bool( allow_partial_linkage, /* default_value= */ false, /* required= */ false);
  78. FLAG_LONG_bool( allow_pointer_mismatch, /* default_value= */ false, /* required= */ false);
  79. FLAG_SHORT_string(o, /* default_value= */ "", /* required= */ false);
  80. FLAG_LONG_string( target_env, /* default_value= */ kDefaultEnvironment, /* required= */ false);
  81. FLAG_LONG_bool( use_highest_version, /* default_value= */ false, /* required= */ false);
  82. // clang-format on
  83. int main(int, const char* argv[]) {
  84. if (!flags::Parse(argv)) {
  85. return 1;
  86. }
  87. if (flags::h.value() || flags::help.value()) {
  88. print_usage(argv[0]);
  89. return 0;
  90. }
  91. if (flags::version.value()) {
  92. spv_target_env target_env;
  93. bool success = spvParseTargetEnv(kDefaultEnvironment, &target_env);
  94. assert(success && "Default environment should always parse.");
  95. if (!success) {
  96. fprintf(stderr,
  97. "error: invalid default target environment. Please report this "
  98. "issue.");
  99. return 1;
  100. }
  101. printf("%s\n", spvSoftwareVersionDetailsString());
  102. printf("Target: %s\n", spvTargetEnvDescription(target_env));
  103. return 0;
  104. }
  105. spv_target_env target_env;
  106. if (!spvParseTargetEnv(flags::target_env.value().c_str(), &target_env)) {
  107. fprintf(stderr, "error: Unrecognized target env: %s\n",
  108. flags::target_env.value().c_str());
  109. return 1;
  110. }
  111. const std::string outFile =
  112. flags::o.value().empty() ? "out.spv" : flags::o.value();
  113. const std::vector<std::string>& inFiles = flags::positional_arguments;
  114. spvtools::LinkerOptions options;
  115. options.SetAllowPartialLinkage(flags::allow_partial_linkage.value());
  116. options.SetAllowPtrTypeMismatch(flags::allow_pointer_mismatch.value());
  117. options.SetCreateLibrary(flags::create_library.value());
  118. options.SetVerifyIds(flags::verify_ids.value());
  119. options.SetUseHighestVersion(flags::use_highest_version.value());
  120. if (inFiles.empty()) {
  121. fprintf(stderr, "error: No input file specified\n");
  122. return 1;
  123. }
  124. std::vector<std::vector<uint32_t>> contents(inFiles.size());
  125. for (size_t i = 0u; i < inFiles.size(); ++i) {
  126. if (!ReadBinaryFile(inFiles[i].c_str(), &contents[i])) return 1;
  127. }
  128. const spvtools::MessageConsumer consumer = [](spv_message_level_t level,
  129. const char*,
  130. const spv_position_t& position,
  131. const char* message) {
  132. switch (level) {
  133. case SPV_MSG_FATAL:
  134. case SPV_MSG_INTERNAL_ERROR:
  135. case SPV_MSG_ERROR:
  136. std::cerr << "error: " << position.index << ": " << message
  137. << std::endl;
  138. break;
  139. case SPV_MSG_WARNING:
  140. std::cout << "warning: " << position.index << ": " << message
  141. << std::endl;
  142. break;
  143. case SPV_MSG_INFO:
  144. std::cout << "info: " << position.index << ": " << message << std::endl;
  145. break;
  146. default:
  147. break;
  148. }
  149. };
  150. spvtools::Context context(target_env);
  151. context.SetMessageConsumer(consumer);
  152. std::vector<uint32_t> linkingResult;
  153. spv_result_t status = Link(context, contents, &linkingResult, options);
  154. if (status != SPV_SUCCESS && status != SPV_WARNING) return 1;
  155. if (!WriteFile<uint32_t>(outFile.c_str(), "wb", linkingResult.data(),
  156. linkingResult.size()))
  157. return 1;
  158. return 0;
  159. }