diff.cpp 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. // Copyright (c) 2022 The Khronos Group Inc.
  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. #if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
  15. #include <unistd.h>
  16. #endif
  17. #include "source/diff/diff.h"
  18. #include "source/opt/build_module.h"
  19. #include "source/opt/ir_context.h"
  20. #include "spirv-tools/libspirv.hpp"
  21. #include "tools/io.h"
  22. #include "tools/util/cli_consumer.h"
  23. #include "tools/util/flags.h"
  24. namespace {
  25. constexpr auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6;
  26. constexpr bool kColorIsPossible =
  27. #if SPIRV_COLOR_TERMINAL
  28. true;
  29. #else
  30. false;
  31. #endif
  32. void print_usage(const char* argv0) {
  33. printf(R"(%s - Compare two SPIR-V files
  34. Usage: %s <src_filename> <dst_filename>
  35. The SPIR-V binary is read from <src_filename> and <dst_filename>. If either
  36. file ends in .spvasm, the SPIR-V is read as text and disassembled.
  37. The contents of the SPIR-V modules are analyzed and a diff is produced showing a
  38. logical transformation from src to dst, in src's id-space.
  39. -h, --help Print this help.
  40. --version Display diff version information.
  41. --color Force color output. The default when printing to a terminal.
  42. If both --color and --no-color is present, --no-color prevails.
  43. --no-color Don't print in color. The default when output goes to
  44. something other than a terminal (e.g. a pipe, or a shell
  45. redirection).
  46. If both --color and --no-color is present, --no-color prevails.
  47. --no-indent Don't indent instructions.
  48. --no-header Don't output the header as leading comments.
  49. --with-id-map Also output the mapping between src and dst outputs.
  50. --ignore-set-binding
  51. Don't use set/binding decorations for variable matching.
  52. --ignore-location
  53. Don't use location decorations for variable matching.
  54. )",
  55. argv0, argv0);
  56. }
  57. bool is_assembly(const char* path) {
  58. const char* suffix = strrchr(path, '.');
  59. if (suffix == nullptr) {
  60. return false;
  61. }
  62. return strcmp(suffix, ".spvasm") == 0;
  63. }
  64. std::unique_ptr<spvtools::opt::IRContext> load_module(const char* path) {
  65. if (is_assembly(path)) {
  66. std::vector<char> contents;
  67. if (!ReadTextFile(path, &contents)) return {};
  68. return spvtools::BuildModule(
  69. kDefaultEnvironment, spvtools::utils::CLIMessageConsumer,
  70. std::string(contents.begin(), contents.end()),
  71. spvtools::SpirvTools::kDefaultAssembleOption |
  72. SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
  73. }
  74. std::vector<uint32_t> contents;
  75. if (!ReadBinaryFile(path, &contents)) return {};
  76. return spvtools::BuildModule(kDefaultEnvironment,
  77. spvtools::utils::CLIMessageConsumer,
  78. contents.data(), contents.size());
  79. }
  80. } // namespace
  81. // clang-format off
  82. FLAG_SHORT_bool(h, /* default_value= */ false, /* required= */ false);
  83. FLAG_LONG_bool( help, /* default_value= */ false, /* required= */false);
  84. FLAG_LONG_bool( version, /* default_value= */ false, /* required= */ false);
  85. FLAG_LONG_bool( color, /* default_value= */ false, /* required= */ false);
  86. FLAG_LONG_bool( no_color, /* default_value= */ false, /* required= */ false);
  87. FLAG_LONG_bool( no_indent, /* default_value= */ false, /* required= */ false);
  88. FLAG_LONG_bool( no_header, /* default_value= */ false, /* required= */ false);
  89. FLAG_LONG_bool( with_id_map, /* default_value= */ false, /* required= */ false);
  90. FLAG_LONG_bool( ignore_set_binding, /* default_value= */ false, /* required= */ false);
  91. FLAG_LONG_bool( ignore_location, /* default_value= */ false, /* required= */ false);
  92. // clang-format on
  93. int main(int, const char* argv[]) {
  94. if (!flags::Parse(argv)) {
  95. return 1;
  96. }
  97. if (flags::h.value() || flags::help.value()) {
  98. print_usage(argv[0]);
  99. return 0;
  100. }
  101. if (flags::version.value()) {
  102. printf("%s\n", spvSoftwareVersionDetailsString());
  103. printf("Target: %s\n", spvTargetEnvDescription(kDefaultEnvironment));
  104. return 0;
  105. }
  106. if (flags::positional_arguments.size() != 2) {
  107. fprintf(stderr, "error: two input files required.\n");
  108. return 1;
  109. }
  110. #if defined(_POSIX_VERSION)
  111. const bool output_is_tty = isatty(fileno(stdout));
  112. #else
  113. const bool output_is_tty = true;
  114. #endif
  115. const std::string& src_file = flags::positional_arguments[0];
  116. const std::string& dst_file = flags::positional_arguments[1];
  117. spvtools::diff::Options options;
  118. options.color_output = (output_is_tty || flags::color.value()) &&
  119. !flags::no_color.value() && kColorIsPossible;
  120. options.indent = !flags::no_indent.value();
  121. options.no_header = flags::no_header.value();
  122. options.dump_id_map = flags::with_id_map.value();
  123. options.ignore_set_binding = flags::ignore_set_binding.value();
  124. options.ignore_location = flags::ignore_location.value();
  125. std::unique_ptr<spvtools::opt::IRContext> src = load_module(src_file.c_str());
  126. std::unique_ptr<spvtools::opt::IRContext> dst = load_module(dst_file.c_str());
  127. if (!src) {
  128. fprintf(stderr, "error: Loading src file\n");
  129. }
  130. if (!dst) {
  131. fprintf(stderr, "error: Loading dst file\n");
  132. }
  133. if (!src || !dst) {
  134. return 1;
  135. }
  136. spvtools::diff::Diff(src.get(), dst.get(), std::cout, options);
  137. return 0;
  138. }