odin-blend2d

Odin bindings to Blend2D
Log | Files | Refs | README | LICENSE

asmjit_test_compiler.cpp (11273B)


      1 // This file is part of AsmJit project <https://asmjit.com>
      2 //
      3 // See <asmjit/core.h> or LICENSE.md for license and copyright information
      4 // SPDX-License-Identifier: Zlib
      5 
      6 #include <asmjit/core.h>
      7 
      8 #include <stdio.h>
      9 #include <stdlib.h>
     10 #include <string.h>
     11 
     12 #include <memory>
     13 #include <vector>
     14 #include <chrono>
     15 
     16 #if !defined(ASMJIT_NO_COMPILER)
     17 
     18 #include "../commons/asmjitutils.h"
     19 #include "../commons/cmdline.h"
     20 #include "../commons/performancetimer.h"
     21 
     22 #include "asmjit_test_compiler.h"
     23 
     24 #if !defined(ASMJIT_NO_X86)
     25   #include <asmjit/x86.h>
     26   void compiler_add_x86_tests(TestApp& app);
     27 #endif // !ASMJIT_NO_X86
     28 
     29 #if !defined(ASMJIT_NO_AARCH64)
     30   #include <asmjit/a64.h>
     31   void compiler_add_a64_tests(TestApp& app);
     32 #endif // !ASMJIT_NO_AARCH64
     33 
     34 using namespace asmjit;
     35 
     36 int TestApp::handle_args(int argc, const char* const* argv) {
     37   CmdLine cmd(argc, argv);
     38   _arch = cmd.value_of("--arch", "all");
     39   _filter = cmd.value_of("--filter", nullptr);
     40 
     41   if (cmd.has_arg("--help")) _help_only = true;
     42   if (cmd.has_arg("--verbose")) _verbose = true;
     43 
     44 #ifndef ASMJIT_NO_LOGGING
     45   if (cmd.has_arg("--dump-asm")) _dump_asm = true;
     46 #endif // !ASMJIT_NO_LOGGING
     47 
     48   if (cmd.has_arg("--dump-hex")) _dump_hex = true;
     49 
     50   return 0;
     51 }
     52 
     53 void TestApp::show_info() {
     54   printf("AsmJit Compiler Test-Suite v%u.%u.%u [Arch=%s] [Mode=%s]\n\n",
     55     unsigned((ASMJIT_LIBRARY_VERSION >> 16)       ),
     56     unsigned((ASMJIT_LIBRARY_VERSION >>  8) & 0xFF),
     57     unsigned((ASMJIT_LIBRARY_VERSION      ) & 0xFF),
     58     asmjit_arch_as_string(Arch::kHost),
     59     asmjit_build_type()
     60   );
     61 
     62   printf("Usage:\n");
     63   printf("  --help          Show usage only\n");
     64   printf("  --arch=<NAME>   Select architecture to run ('all' by default)\n");
     65   printf("  --filter=<NAME> Use a filter to restrict which test is called\n");
     66   printf("  --verbose       Verbose output\n");
     67   printf("  --dump-asm      Assembler output\n");
     68   printf("  --dump-hex      Hexadecimal output (relocated, only for host arch)\n");
     69   printf("\n");
     70 }
     71 
     72 #ifndef ASMJIT_NO_LOGGING
     73 class IndentedStdoutLogger : public Logger {
     74 public:
     75   ASMJIT_NONCOPYABLE(IndentedStdoutLogger)
     76 
     77   size_t _indentation = 0;
     78 
     79   explicit IndentedStdoutLogger(size_t indentation) noexcept
     80     : _indentation(indentation) {}
     81 
     82   Error _log(const char* data, size_t size = SIZE_MAX) noexcept override {
     83     asmjit::Support::maybe_unused(size);
     84     print_indented(data, _indentation);
     85     return Error::kOk;
     86   }
     87 };
     88 #endif // !ASMJIT_NO_LOGGING
     89 
     90 bool TestApp::should_run(const TestCase* tc) {
     91   if (!_filter)
     92     return true;
     93 
     94   return strstr(tc->name(), _filter) != nullptr;
     95 }
     96 
     97 int TestApp::run() {
     98 #ifndef ASMJIT_NO_LOGGING
     99   FormatOptions format_options;
    100   format_options.add_flags(
    101     FormatFlags::kMachineCode |
    102     FormatFlags::kShowAliases |
    103     FormatFlags::kExplainImms |
    104     FormatFlags::kRegCasts   );
    105   format_options.set_indentation(FormatIndentationGroup::kCode, 2);
    106 
    107   IndentedStdoutLogger print_logger(4);
    108   print_logger.set_options(format_options);
    109 
    110   StringLogger string_logger;
    111   string_logger.set_options(format_options);
    112 
    113   auto print_string_logger_content = [&]() {
    114     if (!_verbose) {
    115       printf("%s", string_logger.data());
    116       fflush(stdout);
    117     }
    118   };
    119 #else
    120   auto print_string_logger_content = [&]() {};
    121 #endif // !ASMJIT_NO_LOGGING
    122 
    123   // maybe unused...
    124   Support::maybe_unused(print_string_logger_content);
    125 
    126 #ifndef ASMJIT_NO_JIT
    127   JitRuntime runtime;
    128 #endif // !ASMJIT_NO_JIT
    129 
    130   PerformanceTimer compile_timer;
    131   PerformanceTimer finalize_timer;
    132 
    133   double compile_time = 0;
    134   double finalize_time = 0;
    135 
    136   for (std::unique_ptr<TestCase>& test : _tests) {
    137     if (!should_run(test.get()))
    138       continue;
    139 
    140     _num_tests++;
    141 
    142     for (uint32_t pass = 0; pass < 2; pass++) {
    143       bool runnable = false;
    144       CodeHolder code;
    145       SimpleErrorHandler error_handler;
    146 
    147       const char* status_separator = " ";
    148 
    149       // Filter architecture to run.
    150       if (strcmp(_arch, "all") != 0) {
    151         switch (test->arch()) {
    152           case Arch::kX86:
    153             if (strcmp(_arch, "x86") == 0)
    154               break;
    155             continue;
    156           case Arch::kX64:
    157             if (strcmp(_arch, "x64") == 0)
    158               break;
    159             continue;
    160           case Arch::kAArch64:
    161             if (strcmp(_arch, "aarch64") == 0)
    162               break;
    163             continue;
    164           default:
    165             continue;
    166         }
    167       }
    168 
    169       // Use platform environment and CPU features when the test can run on the arch.
    170 #ifndef ASMJIT_NO_JIT
    171       if (runtime.arch() == test->arch()) {
    172         code.init(runtime.environment(), runtime.cpu_features());
    173         runnable = true;
    174       }
    175 #endif // !ASMJIT_NO_JIT
    176 
    177       if (!code.is_initialized()) {
    178         Environment custom_env;
    179         CpuFeatures features;
    180 
    181         switch (test->arch()) {
    182           case Arch::kX86:
    183           case Arch::kX64:
    184             features.add(CpuFeatures::X86::kADX,
    185                          CpuFeatures::X86::kAVX,
    186                          CpuFeatures::X86::kAVX2,
    187                          CpuFeatures::X86::kBMI,
    188                          CpuFeatures::X86::kBMI2,
    189                          CpuFeatures::X86::kCMOV,
    190                          CpuFeatures::X86::kF16C,
    191                          CpuFeatures::X86::kFMA,
    192                          CpuFeatures::X86::kFPU,
    193                          CpuFeatures::X86::kI486,
    194                          CpuFeatures::X86::kLZCNT,
    195                          CpuFeatures::X86::kMMX,
    196                          CpuFeatures::X86::kMMX2,
    197                          CpuFeatures::X86::kPOPCNT,
    198                          CpuFeatures::X86::kSSE,
    199                          CpuFeatures::X86::kSSE2,
    200                          CpuFeatures::X86::kSSE3,
    201                          CpuFeatures::X86::kSSSE3,
    202                          CpuFeatures::X86::kSSE4_1,
    203                          CpuFeatures::X86::kSSE4_2);
    204             break;
    205 
    206           case Arch::kAArch64:
    207             features.add(CpuFeatures::ARM::kAES,
    208                          CpuFeatures::ARM::kASIMD,
    209                          CpuFeatures::ARM::kIDIVA,
    210                          CpuFeatures::ARM::kIDIVT,
    211                          CpuFeatures::ARM::kPMULL);
    212             break;
    213 
    214           default:
    215             break;
    216         }
    217 
    218         custom_env.init(test->arch());
    219         code.init(custom_env, features);
    220       }
    221 
    222       code.set_error_handler(&error_handler);
    223 
    224       if (pass != 0) {
    225         printf("[Test:%s] %s", asmjit_arch_as_string(test->arch()), test->name());
    226         fflush(stdout);
    227 
    228 #ifndef ASMJIT_NO_LOGGING
    229         if (_verbose || _dump_asm || _dump_hex) {
    230           printf("\n");
    231           status_separator = "  ";
    232         }
    233 
    234         if (_verbose) {
    235           printf("  [Log]\n");
    236           code.set_logger(&print_logger);
    237         }
    238         else {
    239           string_logger.clear();
    240           code.set_logger(&string_logger);
    241         }
    242 #endif // !ASMJIT_NO_LOGGING
    243       }
    244 
    245       std::unique_ptr<BaseCompiler> cc;
    246 
    247 #ifndef ASMJIT_NO_X86
    248       if (code.arch() == Arch::kX86 || code.arch() == Arch::kX64)
    249         cc = std::make_unique<x86::Compiler>(&code);
    250 #endif // !ASMJIT_NO_X86
    251 
    252 #ifndef ASMJIT_NO_AARCH64
    253       if (code.arch() == Arch::kAArch64)
    254         cc = std::make_unique<a64::Compiler>(&code);
    255 #endif // !ASMJIT_NO_AARCH64
    256 
    257       if (!cc)
    258         continue;
    259 
    260 #ifndef ASMJIT_NO_LOGGING
    261       cc->add_diagnostic_options(DiagnosticOptions::kRAAnnotate | DiagnosticOptions::kRADebugAll);
    262 #endif // !ASMJIT_NO_LOGGING
    263 
    264       compile_timer.start();
    265       test->compile(*cc);
    266       compile_timer.stop();
    267 
    268       Error err = error_handler._err;
    269       if (err == Error::kOk) {
    270         finalize_timer.start();
    271         err = cc->finalize();
    272         finalize_timer.stop();
    273       }
    274 
    275       // The first pass is only used for timing of serialization and compilation, because otherwise it would be
    276       // biased by logging, which takes much more time than finalize() does. We want to benchmark Compiler the
    277       // way it would be used in the production.
    278       if (pass == 0) {
    279         _output_size += code.code_size();
    280         compile_time += compile_timer.duration();
    281         finalize_time += finalize_timer.duration();
    282         continue;
    283       }
    284 
    285 #ifndef ASMJIT_NO_LOGGING
    286       if (_dump_asm) {
    287         String sb;
    288         Formatter::format_node_list(sb, format_options, cc.get());
    289         printf("  [Assembly]\n");
    290         print_indented(sb.data(), 4);
    291       }
    292 #endif // !ASMJIT_NO_LOGGING
    293 
    294 #ifndef ASMJIT_NO_JIT
    295       if (runnable) {
    296         void* func = nullptr;
    297         if (err == Error::kOk)
    298           err = runtime.add(&func, &code);
    299 
    300         if (err == Error::kOk && _dump_hex) {
    301           String sb;
    302           sb.append_hex((void*)func, code.code_size());
    303           printf("  [Hex Dump]:\n");
    304           for (size_t i = 0; i < sb.size(); i += 76) {
    305             printf("    %.60s\n", sb.data() + i);
    306           }
    307         }
    308 
    309         if (_verbose)
    310           fflush(stdout);
    311 
    312         if (err == Error::kOk) {
    313           StringTmp<128> result;
    314           StringTmp<128> expect;
    315 
    316           if (test->run(func, result, expect)) {
    317             if (!_verbose)
    318               printf("%s[RUN OK]\n", status_separator);
    319           }
    320           else {
    321             if (!_verbose)
    322               printf("%s[RUN FAILED]\n", status_separator);
    323 
    324             print_string_logger_content();
    325             printf("  [Output]\n");
    326             printf("    Returned: %s\n", result.data());
    327             printf("    Expected: %s\n", expect.data());
    328             _num_failed++;
    329           }
    330 
    331           if (_dump_asm)
    332             printf("\n");
    333 
    334           runtime.release(func);
    335         }
    336         else {
    337           if (!_verbose)
    338             printf("%s[COMPILE FAILED]\n", status_separator);
    339 
    340           print_string_logger_content();
    341           printf("  [Status]\n");
    342           printf("    ERROR 0x%08X: %s\n", unsigned(err), error_handler._message.data());
    343           _num_failed++;
    344         }
    345       }
    346 #endif // !ASMJIT_NO_JIT
    347 
    348       if (!runnable) {
    349         if (err != Error::kOk) {
    350           printf("  [Status]\n");
    351           printf("    ERROR 0x%08X: %s\n", unsigned(err), error_handler._message.data());
    352           _num_failed++;
    353         }
    354         else {
    355           printf("%s[COMPILE OK]\n", status_separator);
    356         }
    357       }
    358 
    359 #ifndef ASMJIT_NO_LOGGING
    360       if (_verbose || _dump_asm || _dump_hex) {
    361         printf("\n");
    362       }
    363 #endif // !ASMJIT_NO_LOGGING
    364     }
    365   }
    366 
    367   printf("\n");
    368   printf("Summary:\n");
    369   printf("  OutputSize: %zu bytes\n", _output_size);
    370   printf("  CompileTime: %.2f ms\n", compile_time);
    371   printf("  FinalizeTime: %.2f ms\n", finalize_time);
    372   printf("\n");
    373 
    374   if (_num_failed == 0)
    375     printf("** SUCCESS: All %u tests passed **\n", _num_tests);
    376   else
    377     printf("** FAILURE: %u of %u tests failed **\n", _num_failed, _num_tests);
    378 
    379   return _num_failed == 0 ? 0 : 1;
    380 }
    381 
    382 int main(int argc, char* argv[]) {
    383   TestApp app;
    384 
    385   app.handle_args(argc, argv);
    386   app.show_info();
    387 
    388 #if !defined(ASMJIT_NO_X86)
    389   compiler_add_x86_tests(app);
    390 #endif // !ASMJIT_NO_X86
    391 
    392 #if !defined(ASMJIT_NO_AARCH64)
    393   compiler_add_a64_tests(app);
    394 #endif // !ASMJIT_NO_AARCH64
    395 
    396   return app.run();
    397 }
    398 
    399 #else
    400 
    401 int main(int argc, char* argv[]) {
    402   Support::maybe_unused(argc, argv);
    403 
    404   printf("AsmJit Compiler Test suite is disabled when compiling with ASMJIT_NO_COMPILER\n\n");
    405   return 0;
    406 }
    407 
    408 #endif // !ASMJIT_NO_COMPILER