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