17ec681f3Smrg/*
27ec681f3Smrg * Copyright © 2020 Valve Corporation
37ec681f3Smrg *
47ec681f3Smrg * Permission is hereby granted, free of charge, to any person obtaining a
57ec681f3Smrg * copy of this software and associated documentation files (the "Software"),
67ec681f3Smrg * to deal in the Software without restriction, including without limitation
77ec681f3Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
87ec681f3Smrg * and/or sell copies of the Software, and to permit persons to whom the
97ec681f3Smrg * Software is furnished to do so, subject to the following conditions:
107ec681f3Smrg *
117ec681f3Smrg * The above copyright notice and this permission notice (including the next
127ec681f3Smrg * paragraph) shall be included in all copies or substantial portions of the
137ec681f3Smrg * Software.
147ec681f3Smrg *
157ec681f3Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
167ec681f3Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
177ec681f3Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
187ec681f3Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
197ec681f3Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
207ec681f3Smrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
217ec681f3Smrg * IN THE SOFTWARE.
227ec681f3Smrg *
237ec681f3Smrg */
247ec681f3Smrg#include <map>
257ec681f3Smrg#include <set>
267ec681f3Smrg#include <string>
277ec681f3Smrg#include <vector>
287ec681f3Smrg#include <stdio.h>
297ec681f3Smrg#include <string.h>
307ec681f3Smrg#include <getopt.h>
317ec681f3Smrg#include <unistd.h>
327ec681f3Smrg#include <stdarg.h>
337ec681f3Smrg#include <llvm-c/Target.h>
347ec681f3Smrg#include "aco_ir.h"
357ec681f3Smrg#include "framework.h"
367ec681f3Smrg
377ec681f3Smrg#include "util/u_cpu_detect.h"
387ec681f3Smrg
397ec681f3Smrgstatic const char *help_message =
407ec681f3Smrg   "Usage: %s [-h] [-l --list] [--no-check] [TEST [TEST ...]]\n"
417ec681f3Smrg   "\n"
427ec681f3Smrg   "Run ACO unit test(s). If TEST is not provided, all tests are run.\n"
437ec681f3Smrg   "\n"
447ec681f3Smrg   "positional arguments:\n"
457ec681f3Smrg   "  TEST        Run TEST. If TEST ends with a '.', run tests with names\n"
467ec681f3Smrg   "              starting with TEST. The test variant (after the '/') can\n"
477ec681f3Smrg   "              be omitted to run all variants\n"
487ec681f3Smrg   "\n"
497ec681f3Smrg   "optional arguments:\n"
507ec681f3Smrg   "  -h, --help  Show this help message and exit.\n"
517ec681f3Smrg   "  -l --list   List unit tests.\n"
527ec681f3Smrg   "  --no-check  Print test output instead of checking it.\n";
537ec681f3Smrg
547ec681f3Smrgstd::map<std::string, TestDef> tests;
557ec681f3SmrgFILE *output = NULL;
567ec681f3Smrg
577ec681f3Smrgstatic TestDef current_test;
587ec681f3Smrgstatic unsigned tests_written = 0;
597ec681f3Smrgstatic FILE *checker_stdin = NULL;
607ec681f3Smrgstatic char *checker_stdin_data = NULL;
617ec681f3Smrgstatic size_t checker_stdin_size = 0;
627ec681f3Smrg
637ec681f3Smrgstatic char *output_data = NULL;
647ec681f3Smrgstatic size_t output_size = 0;
657ec681f3Smrgstatic size_t output_offset = 0;
667ec681f3Smrg
677ec681f3Smrgstatic char current_variant[64] = {0};
687ec681f3Smrgstatic std::set<std::string> *variant_filter = NULL;
697ec681f3Smrg
707ec681f3Smrgbool test_failed = false;
717ec681f3Smrgbool test_skipped = false;
727ec681f3Smrgstatic char fail_message[256] = {0};
737ec681f3Smrg
747ec681f3Smrgvoid write_test()
757ec681f3Smrg{
767ec681f3Smrg   if (!checker_stdin) {
777ec681f3Smrg      /* not entirely correct, but shouldn't matter */
787ec681f3Smrg      tests_written++;
797ec681f3Smrg      return;
807ec681f3Smrg   }
817ec681f3Smrg
827ec681f3Smrg   fflush(output);
837ec681f3Smrg   if (output_offset == output_size && !test_skipped && !test_failed)
847ec681f3Smrg      return;
857ec681f3Smrg
867ec681f3Smrg   char *data = output_data + output_offset;
877ec681f3Smrg   uint32_t size = output_size - output_offset;
887ec681f3Smrg
897ec681f3Smrg   fwrite("test", 1, 4, checker_stdin);
907ec681f3Smrg   fwrite(current_test.name, 1, strlen(current_test.name)+1, checker_stdin);
917ec681f3Smrg   fwrite(current_variant, 1, strlen(current_variant)+1, checker_stdin);
927ec681f3Smrg   fwrite(current_test.source_file, 1, strlen(current_test.source_file)+1, checker_stdin);
937ec681f3Smrg   if (test_failed || test_skipped) {
947ec681f3Smrg      const char *res = test_failed ? "failed" : "skipped";
957ec681f3Smrg      fwrite("\x01", 1, 1, checker_stdin);
967ec681f3Smrg      fwrite(res, 1, strlen(res)+1, checker_stdin);
977ec681f3Smrg      fwrite(fail_message, 1, strlen(fail_message)+1, checker_stdin);
987ec681f3Smrg   } else {
997ec681f3Smrg      fwrite("\x00", 1, 1, checker_stdin);
1007ec681f3Smrg   }
1017ec681f3Smrg   fwrite(&size, 4, 1, checker_stdin);
1027ec681f3Smrg   fwrite(data, 1, size, checker_stdin);
1037ec681f3Smrg
1047ec681f3Smrg   tests_written++;
1057ec681f3Smrg   output_offset += size;
1067ec681f3Smrg}
1077ec681f3Smrg
1087ec681f3Smrgbool set_variant(const char *name)
1097ec681f3Smrg{
1107ec681f3Smrg   if (variant_filter && !variant_filter->count(name))
1117ec681f3Smrg      return false;
1127ec681f3Smrg
1137ec681f3Smrg   write_test();
1147ec681f3Smrg   test_failed = false;
1157ec681f3Smrg   test_skipped = false;
1167ec681f3Smrg   strncpy(current_variant, name, sizeof(current_variant) - 1);
1177ec681f3Smrg
1187ec681f3Smrg   printf("Running '%s/%s'\n", current_test.name, name);
1197ec681f3Smrg
1207ec681f3Smrg   return true;
1217ec681f3Smrg}
1227ec681f3Smrg
1237ec681f3Smrgvoid fail_test(const char *fmt, ...)
1247ec681f3Smrg{
1257ec681f3Smrg   va_list args;
1267ec681f3Smrg   va_start(args, fmt);
1277ec681f3Smrg
1287ec681f3Smrg   test_failed = true;
1297ec681f3Smrg   vsnprintf(fail_message, sizeof(fail_message), fmt, args);
1307ec681f3Smrg
1317ec681f3Smrg   va_end(args);
1327ec681f3Smrg}
1337ec681f3Smrg
1347ec681f3Smrgvoid skip_test(const char *fmt, ...)
1357ec681f3Smrg{
1367ec681f3Smrg   va_list args;
1377ec681f3Smrg   va_start(args, fmt);
1387ec681f3Smrg
1397ec681f3Smrg   test_skipped = true;
1407ec681f3Smrg   vsnprintf(fail_message, sizeof(fail_message), fmt, args);
1417ec681f3Smrg
1427ec681f3Smrg   va_end(args);
1437ec681f3Smrg}
1447ec681f3Smrg
1457ec681f3Smrgvoid run_test(TestDef def)
1467ec681f3Smrg{
1477ec681f3Smrg   current_test = def;
1487ec681f3Smrg   output_data = NULL;
1497ec681f3Smrg   output_size = 0;
1507ec681f3Smrg   output_offset = 0;
1517ec681f3Smrg   test_failed = false;
1527ec681f3Smrg   test_skipped = false;
1537ec681f3Smrg   memset(current_variant, 0, sizeof(current_variant));
1547ec681f3Smrg
1557ec681f3Smrg   if (checker_stdin)
1567ec681f3Smrg      output = open_memstream(&output_data, &output_size);
1577ec681f3Smrg   else
1587ec681f3Smrg      output = stdout;
1597ec681f3Smrg
1607ec681f3Smrg   current_test.func();
1617ec681f3Smrg   write_test();
1627ec681f3Smrg
1637ec681f3Smrg   if (checker_stdin)
1647ec681f3Smrg      fclose(output);
1657ec681f3Smrg   free(output_data);
1667ec681f3Smrg}
1677ec681f3Smrg
1687ec681f3Smrgint check_output(char **argv)
1697ec681f3Smrg{
1707ec681f3Smrg   fflush(stdout);
1717ec681f3Smrg   fflush(stderr);
1727ec681f3Smrg
1737ec681f3Smrg   fclose(checker_stdin);
1747ec681f3Smrg
1757ec681f3Smrg   int stdin_pipe[2];
1767ec681f3Smrg   pipe(stdin_pipe);
1777ec681f3Smrg
1787ec681f3Smrg   pid_t child_pid = fork();
1797ec681f3Smrg   if (child_pid == -1) {
1807ec681f3Smrg      fprintf(stderr, "%s: fork() failed: %s\n", argv[0], strerror(errno));
1817ec681f3Smrg      return 99;
1827ec681f3Smrg   } else if (child_pid != 0) {
1837ec681f3Smrg      /* Evaluate test output externally using Python */
1847ec681f3Smrg      dup2(stdin_pipe[0], STDIN_FILENO);
1857ec681f3Smrg      close(stdin_pipe[0]);
1867ec681f3Smrg      close(stdin_pipe[1]);
1877ec681f3Smrg
1887ec681f3Smrg      execlp(ACO_TEST_PYTHON_BIN, ACO_TEST_PYTHON_BIN, ACO_TEST_SOURCE_DIR "/check_output.py", NULL);
1897ec681f3Smrg      fprintf(stderr, "%s: execlp() failed: %s\n", argv[0], strerror(errno));
1907ec681f3Smrg      return 99;
1917ec681f3Smrg   } else {
1927ec681f3Smrg      /* Feed input data to the Python process. Writing large streams to
1937ec681f3Smrg       * stdin will block eventually, so this is done in a forked process
1947ec681f3Smrg       * to let the test checker process chunks of data as they arrive */
1957ec681f3Smrg      write(stdin_pipe[1], checker_stdin_data, checker_stdin_size);
1967ec681f3Smrg      close(stdin_pipe[0]);
1977ec681f3Smrg      close(stdin_pipe[1]);
1987ec681f3Smrg      _exit(0);
1997ec681f3Smrg   }
2007ec681f3Smrg}
2017ec681f3Smrg
2027ec681f3Smrgbool match_test(std::string name, std::string pattern)
2037ec681f3Smrg{
2047ec681f3Smrg   if (name.length() < pattern.length())
2057ec681f3Smrg      return false;
2067ec681f3Smrg   if (pattern.back() == '.')
2077ec681f3Smrg      name.resize(pattern.length());
2087ec681f3Smrg   return name == pattern;
2097ec681f3Smrg}
2107ec681f3Smrg
2117ec681f3Smrgint main(int argc, char **argv)
2127ec681f3Smrg{
2137ec681f3Smrg   int print_help = 0;
2147ec681f3Smrg   int do_list = 0;
2157ec681f3Smrg   int do_check = 1;
2167ec681f3Smrg   const struct option opts[] = {
2177ec681f3Smrg      { "help",     no_argument, &print_help, 1 },
2187ec681f3Smrg      { "list",     no_argument, &do_list,    1 },
2197ec681f3Smrg      { "no-check", no_argument, &do_check,   0 },
2207ec681f3Smrg      { NULL,       0,           NULL,        0 }
2217ec681f3Smrg   };
2227ec681f3Smrg
2237ec681f3Smrg   int c;
2247ec681f3Smrg   while ((c = getopt_long(argc, argv, "hl", opts, NULL)) != -1) {
2257ec681f3Smrg      switch (c) {
2267ec681f3Smrg      case 'h':
2277ec681f3Smrg         print_help = 1;
2287ec681f3Smrg         break;
2297ec681f3Smrg      case 'l':
2307ec681f3Smrg         do_list = 1;
2317ec681f3Smrg         break;
2327ec681f3Smrg      case 0:
2337ec681f3Smrg         break;
2347ec681f3Smrg      case '?':
2357ec681f3Smrg      default:
2367ec681f3Smrg         fprintf(stderr, "%s: Invalid argument\n", argv[0]);
2377ec681f3Smrg         return 99;
2387ec681f3Smrg      }
2397ec681f3Smrg   }
2407ec681f3Smrg
2417ec681f3Smrg   if (print_help) {
2427ec681f3Smrg      fprintf(stderr, help_message, argv[0]);
2437ec681f3Smrg      return 99;
2447ec681f3Smrg   }
2457ec681f3Smrg
2467ec681f3Smrg   util_cpu_detect();
2477ec681f3Smrg
2487ec681f3Smrg   if (do_list) {
2497ec681f3Smrg      for (auto test : tests)
2507ec681f3Smrg         printf("%s\n", test.first.c_str());
2517ec681f3Smrg      return 99;
2527ec681f3Smrg   }
2537ec681f3Smrg
2547ec681f3Smrg   std::vector<std::pair<std::string, std::string>> names;
2557ec681f3Smrg   for (int i = optind; i < argc; i++) {
2567ec681f3Smrg      std::string name = argv[i];
2577ec681f3Smrg      std::string variant;
2587ec681f3Smrg      size_t pos = name.find('/');
2597ec681f3Smrg      if (pos != std::string::npos) {
2607ec681f3Smrg         variant = name.substr(pos + 1);
2617ec681f3Smrg         name = name.substr(0, pos);
2627ec681f3Smrg      }
2637ec681f3Smrg      names.emplace_back(std::pair<std::string, std::string>(name, variant));
2647ec681f3Smrg   }
2657ec681f3Smrg
2667ec681f3Smrg   if (do_check)
2677ec681f3Smrg      checker_stdin = open_memstream(&checker_stdin_data, &checker_stdin_size);
2687ec681f3Smrg
2697ec681f3Smrg	LLVMInitializeAMDGPUTargetInfo();
2707ec681f3Smrg	LLVMInitializeAMDGPUTarget();
2717ec681f3Smrg	LLVMInitializeAMDGPUTargetMC();
2727ec681f3Smrg	LLVMInitializeAMDGPUDisassembler();
2737ec681f3Smrg
2747ec681f3Smrg   aco::init();
2757ec681f3Smrg
2767ec681f3Smrg   for (auto pair : tests) {
2777ec681f3Smrg      bool found = names.empty();
2787ec681f3Smrg      bool all_variants = names.empty();
2797ec681f3Smrg      std::set<std::string> variants;
2807ec681f3Smrg      for (const std::pair<std::string, std::string>& name : names) {
2817ec681f3Smrg         if (match_test(pair.first, name.first)) {
2827ec681f3Smrg            found = true;
2837ec681f3Smrg            if (name.second.empty())
2847ec681f3Smrg               all_variants = true;
2857ec681f3Smrg            else
2867ec681f3Smrg               variants.insert(name.second);
2877ec681f3Smrg         }
2887ec681f3Smrg      }
2897ec681f3Smrg
2907ec681f3Smrg      if (found) {
2917ec681f3Smrg         variant_filter = all_variants ? NULL : &variants;
2927ec681f3Smrg         printf("Running '%s'\n", pair.first.c_str());
2937ec681f3Smrg         run_test(pair.second);
2947ec681f3Smrg      }
2957ec681f3Smrg   }
2967ec681f3Smrg   if (!tests_written) {
2977ec681f3Smrg      fprintf(stderr, "%s: No matching tests\n", argv[0]);
2987ec681f3Smrg      return 99;
2997ec681f3Smrg   }
3007ec681f3Smrg
3017ec681f3Smrg   if (checker_stdin) {
3027ec681f3Smrg      printf("\n");
3037ec681f3Smrg      return check_output(argv);
3047ec681f3Smrg   } else {
3057ec681f3Smrg      printf("Tests ran\n");
3067ec681f3Smrg      return 99;
3077ec681f3Smrg   }
3087ec681f3Smrg}
309