linker_fixture.h 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  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. #ifndef TEST_LINK_LINKER_FIXTURE_H_
  15. #define TEST_LINK_LINKER_FIXTURE_H_
  16. #include <iostream>
  17. #include <string>
  18. #include <vector>
  19. #include "effcee/effcee.h"
  20. #include "re2/re2.h"
  21. #include "source/spirv_constant.h"
  22. #include "spirv-tools/linker.hpp"
  23. #include "test/unit_spirv.h"
  24. namespace spvtest {
  25. using Binary = std::vector<uint32_t>;
  26. using Binaries = std::vector<Binary>;
  27. class LinkerTest : public ::testing::Test {
  28. public:
  29. LinkerTest()
  30. : context_(SPV_ENV_UNIVERSAL_1_2),
  31. tools_(SPV_ENV_UNIVERSAL_1_2),
  32. assemble_options_(spvtools::SpirvTools::kDefaultAssembleOption),
  33. disassemble_options_(spvtools::SpirvTools::kDefaultDisassembleOption) {
  34. const auto consumer = [this](spv_message_level_t level, const char*,
  35. const spv_position_t& position,
  36. const char* message) {
  37. if (!error_message_.empty()) error_message_ += "\n";
  38. switch (level) {
  39. case SPV_MSG_FATAL:
  40. case SPV_MSG_INTERNAL_ERROR:
  41. case SPV_MSG_ERROR:
  42. error_message_ += "ERROR";
  43. break;
  44. case SPV_MSG_WARNING:
  45. error_message_ += "WARNING";
  46. break;
  47. case SPV_MSG_INFO:
  48. error_message_ += "INFO";
  49. break;
  50. case SPV_MSG_DEBUG:
  51. error_message_ += "DEBUG";
  52. break;
  53. }
  54. error_message_ += ": " + std::to_string(position.index) + ": " + message;
  55. };
  56. context_.SetMessageConsumer(consumer);
  57. tools_.SetMessageConsumer(consumer);
  58. }
  59. void TearDown() override { error_message_.clear(); }
  60. // Assembles each of the given strings into SPIR-V binaries before linking
  61. // them together. SPV_ERROR_INVALID_TEXT is returned if the assembling failed
  62. // for any of the input strings, and SPV_ERROR_INVALID_POINTER if
  63. // |linked_binary| is a null pointer.
  64. spv_result_t AssembleAndLink(
  65. const std::vector<std::string>& bodies, spvtest::Binary* linked_binary,
  66. spvtools::LinkerOptions options = spvtools::LinkerOptions()) {
  67. if (!linked_binary) return SPV_ERROR_INVALID_POINTER;
  68. spvtest::Binaries binaries(bodies.size());
  69. for (size_t i = 0u; i < bodies.size(); ++i)
  70. if (!tools_.Assemble(bodies[i], binaries.data() + i, assemble_options_))
  71. return SPV_ERROR_INVALID_TEXT;
  72. return spvtools::Link(context_, binaries, linked_binary, options);
  73. }
  74. // Assembles and links a vector of SPIR-V bodies based on the |templateBody|.
  75. // Template arguments to be replaced are written as {a,b,...}.
  76. // SPV_ERROR_INVALID_TEXT is returned if the assembling failed for any of the
  77. // resulting bodies (or errors in the template), and SPV_ERROR_INVALID_POINTER
  78. // if |linked_binary| is a null pointer.
  79. spv_result_t ExpandAndLink(
  80. const std::string& templateBody, spvtest::Binary* linked_binary,
  81. spvtools::LinkerOptions options = spvtools::LinkerOptions()) {
  82. if (!linked_binary) return SPV_ERROR_INVALID_POINTER;
  83. // Find out how many template arguments there are, we assume they all have
  84. // the same number. We'll error later if they don't.
  85. re2::StringPiece temp(templateBody);
  86. re2::StringPiece x;
  87. int cnt = 0;
  88. if (!RE2::FindAndConsume(&temp, "{")) return SPV_ERROR_INVALID_TEXT;
  89. while (RE2::FindAndConsume(&temp, "([,}])", &x) && x[0] == ',') cnt++;
  90. cnt++;
  91. if (cnt <= 1) return SPV_ERROR_INVALID_TEXT;
  92. // Construct a regex for a single common strip and template expansion.
  93. std::string regex("([^{]*){");
  94. for (int i = 0; i < cnt; i++) regex += (i > 0) ? ",([^,]*)" : "([^,]*)";
  95. regex += "}";
  96. RE2 pattern(regex);
  97. // Prepare the RE2::Args for processing.
  98. re2::StringPiece common;
  99. std::vector<re2::StringPiece> variants(cnt);
  100. std::vector<RE2::Arg> args(cnt + 1);
  101. args[0] = RE2::Arg(&common);
  102. std::vector<RE2::Arg*> pargs(cnt + 1);
  103. pargs[0] = &args[0];
  104. for (int i = 0; i < cnt; i++) {
  105. args[i + 1] = RE2::Arg(&variants[i]);
  106. pargs[i + 1] = &args[i + 1];
  107. }
  108. // Reset and construct the bodies bit by bit.
  109. std::vector<std::string> bodies(cnt);
  110. re2::StringPiece temp2(templateBody);
  111. while (RE2::ConsumeN(&temp2, pattern, pargs.data(), cnt + 1)) {
  112. for (int i = 0; i < cnt; i++) {
  113. bodies[i].append(common.begin(), common.end());
  114. bodies[i].append(variants[i].begin(), variants[i].end());
  115. }
  116. }
  117. RE2::Consume(&temp2, "([^{]*)", &common);
  118. for (int i = 0; i < cnt; i++)
  119. bodies[i].append(common.begin(), common.end());
  120. // Run through the assemble and link stages of the process.
  121. return AssembleAndLink(bodies, linked_binary, options);
  122. }
  123. // Expand the |templateBody| and link the results as with ExpandAndLink,
  124. // then disassemble and test that the result matches the |expected|.
  125. void ExpandAndCheck(
  126. const std::string& templateBody, const std::string& expected,
  127. const spvtools::LinkerOptions options = spvtools::LinkerOptions()) {
  128. spvtest::Binary linked_binary;
  129. spv_result_t res = ExpandAndLink(templateBody, &linked_binary, options);
  130. EXPECT_EQ(SPV_SUCCESS, res) << GetErrorMessage() << "\nExpanded from:\n"
  131. << templateBody;
  132. if (res == SPV_SUCCESS) {
  133. std::string result;
  134. EXPECT_TRUE(
  135. tools_.Disassemble(linked_binary, &result, disassemble_options_))
  136. << GetErrorMessage();
  137. EXPECT_EQ(expected, result);
  138. }
  139. }
  140. void Match(const std::string& templateBody,
  141. const spvtest::Binary& linked_binary) {
  142. std::string result;
  143. EXPECT_TRUE(
  144. tools_.Disassemble(linked_binary, &result, disassemble_options_))
  145. << GetErrorMessage();
  146. auto match_res = effcee::Match(result, templateBody);
  147. EXPECT_EQ(effcee::Result::Status::Ok, match_res.status())
  148. << match_res.message() << "\nExpanded from:\n"
  149. << templateBody << "\nChecking result:\n"
  150. << result;
  151. }
  152. // An alternative to ExpandAndCheck, which uses the |templateBody| as the
  153. // match pattern for the disassembled linked result.
  154. void ExpandAndMatch(
  155. const std::string& templateBody,
  156. const spvtools::LinkerOptions options = spvtools::LinkerOptions()) {
  157. spvtest::Binary linked_binary;
  158. spv_result_t res = ExpandAndLink(templateBody, &linked_binary, options);
  159. EXPECT_EQ(SPV_SUCCESS, res) << GetErrorMessage() << "\nExpanded from:\n"
  160. << templateBody;
  161. if (res == SPV_SUCCESS) {
  162. Match(templateBody, linked_binary);
  163. }
  164. }
  165. // Links the given SPIR-V binaries together; SPV_ERROR_INVALID_POINTER is
  166. // returned if |linked_binary| is a null pointer.
  167. spv_result_t Link(
  168. const spvtest::Binaries& binaries, spvtest::Binary* linked_binary,
  169. spvtools::LinkerOptions options = spvtools::LinkerOptions()) {
  170. if (!linked_binary) return SPV_ERROR_INVALID_POINTER;
  171. return spvtools::Link(context_, binaries, linked_binary, options);
  172. }
  173. // Disassembles |binary| and outputs the result in |text|. If |text| is a
  174. // null pointer, SPV_ERROR_INVALID_POINTER is returned.
  175. spv_result_t Disassemble(const spvtest::Binary& binary, std::string* text) {
  176. if (!text) return SPV_ERROR_INVALID_POINTER;
  177. return tools_.Disassemble(binary, text, disassemble_options_)
  178. ? SPV_SUCCESS
  179. : SPV_ERROR_INVALID_BINARY;
  180. }
  181. // Sets the options for the assembler.
  182. void SetAssembleOptions(uint32_t assemble_options) {
  183. assemble_options_ = assemble_options;
  184. }
  185. // Sets the options used by the disassembler.
  186. void SetDisassembleOptions(uint32_t disassemble_options) {
  187. disassemble_options_ = disassemble_options;
  188. }
  189. // Returns the accumulated error messages for the test.
  190. std::string GetErrorMessage() const { return error_message_; }
  191. bool Validate(const spvtest::Binary& binary) {
  192. return tools_.Validate(binary);
  193. }
  194. private:
  195. spvtools::Context context_;
  196. spvtools::SpirvTools
  197. tools_; // An instance for calling SPIRV-Tools functionalities.
  198. uint32_t assemble_options_;
  199. uint32_t disassemble_options_;
  200. std::string error_message_;
  201. };
  202. } // namespace spvtest
  203. #endif // TEST_LINK_LINKER_FIXTURE_H_