mirror of
https://github.com/asmjit/asmjit.git
synced 2025-12-16 20:17:05 +03:00
* Denested src folder to root, renamed testing to asmjit-testing
* Refactored how headers are included into <asmjit/...> form. This
is necessary as compilers would never simplify a path once a ..
appears in include directory - then paths such as ../core/../core
appeared in asserts, which was ugly
* Moved support utilities into asmjit/support/... (still included
by asmjit/core.h for convenience and compatibility)
* Added CMakePresets.json for making it easy to develop AsmJit
* Reworked CMakeLists to be shorter and use CMake option(),
etc... This simplifies it and makes it using more standard
features
* ASMJIT_EMBED now creates asmjit_embed INTERFACE library,
which is accessible via asmjit::asmjit target - this simplifies
embedding and makes it the same as library targets from a CMake
perspective
* Removed ASMJIT_DEPS - this is now provided by cmake target
aliases - 'asmjit::asmjit' so users should not need this variable
* Changed meaning of ASMJIT_LIBS - this now contains only AsmJit
dependencies without asmjit::asmjit target alias. Don't rely on
ASMJIT_LIBS anymore as it's only used internally
* Removed ASMJIT_NO_DEPRECATED option - AsmJit is not going
to provide controllable deprecations in the future
* Removed ASMJIT_NO_VALIDATION in favor of ASMJIT_NO_INTROSPECTION,
which now controls query, features, and validation API presence
* Removed ASMJIT_DIR option - it was never really needed
* Removed AMX_TRANSPOSE feature from instruction database (X86).
Intel has removed it as well, so it's a feature that won't
be siliconized
408 lines
11 KiB
C++
408 lines
11 KiB
C++
// This file is part of AsmJit project <https://asmjit.com>
|
|
//
|
|
// See <asmjit/core.h> or LICENSE.md for license and copyright information
|
|
// SPDX-License-Identifier: Zlib
|
|
|
|
#include <asmjit/core.h>
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <memory>
|
|
#include <vector>
|
|
#include <chrono>
|
|
|
|
#if !defined(ASMJIT_NO_COMPILER)
|
|
|
|
#include <asmjit-testing/commons/asmjitutils.h>
|
|
#include <asmjit-testing/commons/cmdline.h>
|
|
#include <asmjit-testing/commons/performancetimer.h>
|
|
#include <asmjit-testing/tests/asmjit_test_compiler.h>
|
|
|
|
#if !defined(ASMJIT_NO_X86)
|
|
#include <asmjit/x86.h>
|
|
void compiler_add_x86_tests(TestApp& app);
|
|
#endif // !ASMJIT_NO_X86
|
|
|
|
#if !defined(ASMJIT_NO_AARCH64)
|
|
#include <asmjit/a64.h>
|
|
void compiler_add_a64_tests(TestApp& app);
|
|
#endif // !ASMJIT_NO_AARCH64
|
|
|
|
using namespace asmjit;
|
|
|
|
int TestApp::handle_args(int argc, const char* const* argv) {
|
|
CmdLine cmd(argc, argv);
|
|
_arch = cmd.value_of("--arch", "all");
|
|
_filter = cmd.value_of("--filter", nullptr);
|
|
|
|
if (cmd.has_arg("--help")) _help_only = true;
|
|
if (cmd.has_arg("--verbose")) _verbose = true;
|
|
|
|
#ifndef ASMJIT_NO_LOGGING
|
|
if (cmd.has_arg("--dump-asm")) _dump_asm = true;
|
|
#endif // !ASMJIT_NO_LOGGING
|
|
|
|
if (cmd.has_arg("--dump-hex")) _dump_hex = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void TestApp::show_info() {
|
|
printf("AsmJit Compiler Test-Suite v%u.%u.%u [Arch=%s] [Mode=%s]\n\n",
|
|
unsigned((ASMJIT_LIBRARY_VERSION >> 16) ),
|
|
unsigned((ASMJIT_LIBRARY_VERSION >> 8) & 0xFF),
|
|
unsigned((ASMJIT_LIBRARY_VERSION ) & 0xFF),
|
|
asmjit_arch_as_string(Arch::kHost),
|
|
asmjit_build_type()
|
|
);
|
|
|
|
printf("Usage:\n");
|
|
printf(" --help Show usage only\n");
|
|
printf(" --arch=<NAME> Select architecture to run ('all' by default)\n");
|
|
printf(" --filter=<NAME> Use a filter to restrict which test is called\n");
|
|
printf(" --verbose Verbose output\n");
|
|
printf(" --dump-asm Assembler output\n");
|
|
printf(" --dump-hex Hexadecimal output (relocated, only for host arch)\n");
|
|
printf("\n");
|
|
}
|
|
|
|
#ifndef ASMJIT_NO_LOGGING
|
|
class IndentedStdoutLogger : public Logger {
|
|
public:
|
|
ASMJIT_NONCOPYABLE(IndentedStdoutLogger)
|
|
|
|
size_t _indentation = 0;
|
|
|
|
explicit IndentedStdoutLogger(size_t indentation) noexcept
|
|
: _indentation(indentation) {}
|
|
|
|
Error _log(const char* data, size_t size = SIZE_MAX) noexcept override {
|
|
asmjit::Support::maybe_unused(size);
|
|
print_indented(data, _indentation);
|
|
return Error::kOk;
|
|
}
|
|
};
|
|
#endif // !ASMJIT_NO_LOGGING
|
|
|
|
bool TestApp::should_run(const TestCase* tc) {
|
|
if (!_filter)
|
|
return true;
|
|
|
|
return strstr(tc->name(), _filter) != nullptr;
|
|
}
|
|
|
|
int TestApp::run() {
|
|
#ifndef ASMJIT_NO_LOGGING
|
|
FormatOptions format_options;
|
|
format_options.add_flags(
|
|
FormatFlags::kMachineCode |
|
|
FormatFlags::kShowAliases |
|
|
FormatFlags::kExplainImms |
|
|
FormatFlags::kRegCasts );
|
|
format_options.set_indentation(FormatIndentationGroup::kCode, 2);
|
|
|
|
IndentedStdoutLogger print_logger(4);
|
|
print_logger.set_options(format_options);
|
|
|
|
StringLogger string_logger;
|
|
string_logger.set_options(format_options);
|
|
|
|
auto print_string_logger_content = [&]() {
|
|
if (!_verbose) {
|
|
printf("%s", string_logger.data());
|
|
fflush(stdout);
|
|
}
|
|
};
|
|
#else
|
|
auto print_string_logger_content = [&]() {};
|
|
#endif // !ASMJIT_NO_LOGGING
|
|
|
|
// maybe unused...
|
|
Support::maybe_unused(print_string_logger_content);
|
|
|
|
#ifndef ASMJIT_NO_JIT
|
|
JitRuntime runtime;
|
|
#endif // !ASMJIT_NO_JIT
|
|
|
|
PerformanceTimer compile_timer;
|
|
PerformanceTimer finalize_timer;
|
|
|
|
double compile_time = 0;
|
|
double finalize_time = 0;
|
|
|
|
for (std::unique_ptr<TestCase>& test : _tests) {
|
|
if (!should_run(test.get()))
|
|
continue;
|
|
|
|
_num_tests++;
|
|
|
|
for (uint32_t pass = 0; pass < 2; pass++) {
|
|
bool runnable = false;
|
|
CodeHolder code;
|
|
SimpleErrorHandler error_handler;
|
|
|
|
const char* status_separator = " ";
|
|
|
|
// Filter architecture to run.
|
|
if (strcmp(_arch, "all") != 0) {
|
|
switch (test->arch()) {
|
|
case Arch::kX86:
|
|
if (strcmp(_arch, "x86") == 0)
|
|
break;
|
|
continue;
|
|
case Arch::kX64:
|
|
if (strcmp(_arch, "x64") == 0)
|
|
break;
|
|
continue;
|
|
case Arch::kAArch64:
|
|
if (strcmp(_arch, "aarch64") == 0)
|
|
break;
|
|
continue;
|
|
default:
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Use platform environment and CPU features when the test can run on the arch.
|
|
#ifndef ASMJIT_NO_JIT
|
|
if (runtime.arch() == test->arch()) {
|
|
code.init(runtime.environment(), runtime.cpu_features());
|
|
runnable = true;
|
|
}
|
|
#endif // !ASMJIT_NO_JIT
|
|
|
|
if (!code.is_initialized()) {
|
|
Environment custom_env;
|
|
CpuFeatures features;
|
|
|
|
switch (test->arch()) {
|
|
case Arch::kX86:
|
|
case Arch::kX64:
|
|
features.add(CpuFeatures::X86::kADX,
|
|
CpuFeatures::X86::kAVX,
|
|
CpuFeatures::X86::kAVX2,
|
|
CpuFeatures::X86::kBMI,
|
|
CpuFeatures::X86::kBMI2,
|
|
CpuFeatures::X86::kCMOV,
|
|
CpuFeatures::X86::kF16C,
|
|
CpuFeatures::X86::kFMA,
|
|
CpuFeatures::X86::kFPU,
|
|
CpuFeatures::X86::kI486,
|
|
CpuFeatures::X86::kLZCNT,
|
|
CpuFeatures::X86::kMMX,
|
|
CpuFeatures::X86::kMMX2,
|
|
CpuFeatures::X86::kPOPCNT,
|
|
CpuFeatures::X86::kSSE,
|
|
CpuFeatures::X86::kSSE2,
|
|
CpuFeatures::X86::kSSE3,
|
|
CpuFeatures::X86::kSSSE3,
|
|
CpuFeatures::X86::kSSE4_1,
|
|
CpuFeatures::X86::kSSE4_2);
|
|
break;
|
|
|
|
case Arch::kAArch64:
|
|
features.add(CpuFeatures::ARM::kAES,
|
|
CpuFeatures::ARM::kASIMD,
|
|
CpuFeatures::ARM::kIDIVA,
|
|
CpuFeatures::ARM::kIDIVT,
|
|
CpuFeatures::ARM::kPMULL);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
custom_env.init(test->arch());
|
|
code.init(custom_env, features);
|
|
}
|
|
|
|
code.set_error_handler(&error_handler);
|
|
|
|
if (pass != 0) {
|
|
printf("[Test:%s] %s", asmjit_arch_as_string(test->arch()), test->name());
|
|
fflush(stdout);
|
|
|
|
#ifndef ASMJIT_NO_LOGGING
|
|
if (_verbose || _dump_asm || _dump_hex) {
|
|
printf("\n");
|
|
status_separator = " ";
|
|
}
|
|
|
|
if (_verbose) {
|
|
printf(" [Log]\n");
|
|
code.set_logger(&print_logger);
|
|
}
|
|
else {
|
|
string_logger.clear();
|
|
code.set_logger(&string_logger);
|
|
}
|
|
#endif // !ASMJIT_NO_LOGGING
|
|
}
|
|
|
|
std::unique_ptr<BaseCompiler> cc;
|
|
|
|
#ifndef ASMJIT_NO_X86
|
|
if (code.arch() == Arch::kX86 || code.arch() == Arch::kX64)
|
|
cc = std::make_unique<x86::Compiler>(&code);
|
|
#endif // !ASMJIT_NO_X86
|
|
|
|
#ifndef ASMJIT_NO_AARCH64
|
|
if (code.arch() == Arch::kAArch64)
|
|
cc = std::make_unique<a64::Compiler>(&code);
|
|
#endif // !ASMJIT_NO_AARCH64
|
|
|
|
if (!cc)
|
|
continue;
|
|
|
|
#ifndef ASMJIT_NO_LOGGING
|
|
cc->add_diagnostic_options(DiagnosticOptions::kRAAnnotate | DiagnosticOptions::kRADebugAll);
|
|
#endif // !ASMJIT_NO_LOGGING
|
|
|
|
compile_timer.start();
|
|
test->compile(*cc);
|
|
compile_timer.stop();
|
|
|
|
Error err = error_handler._err;
|
|
if (err == Error::kOk) {
|
|
finalize_timer.start();
|
|
err = cc->finalize();
|
|
finalize_timer.stop();
|
|
}
|
|
|
|
// The first pass is only used for timing of serialization and compilation, because otherwise it would be
|
|
// biased by logging, which takes much more time than finalize() does. We want to benchmark Compiler the
|
|
// way it would be used in the production.
|
|
if (pass == 0) {
|
|
_output_size += code.code_size();
|
|
compile_time += compile_timer.duration();
|
|
finalize_time += finalize_timer.duration();
|
|
continue;
|
|
}
|
|
|
|
#ifndef ASMJIT_NO_LOGGING
|
|
if (_dump_asm) {
|
|
String sb;
|
|
Formatter::format_node_list(sb, format_options, cc.get());
|
|
printf(" [Assembly]\n");
|
|
print_indented(sb.data(), 4);
|
|
}
|
|
#endif // !ASMJIT_NO_LOGGING
|
|
|
|
#ifndef ASMJIT_NO_JIT
|
|
if (runnable) {
|
|
void* func = nullptr;
|
|
if (err == Error::kOk)
|
|
err = runtime.add(&func, &code);
|
|
|
|
if (err == Error::kOk && _dump_hex) {
|
|
String sb;
|
|
sb.append_hex((void*)func, code.code_size());
|
|
printf(" [Hex Dump]:\n");
|
|
for (size_t i = 0; i < sb.size(); i += 76) {
|
|
printf(" %.60s\n", sb.data() + i);
|
|
}
|
|
}
|
|
|
|
if (_verbose)
|
|
fflush(stdout);
|
|
|
|
if (err == Error::kOk) {
|
|
StringTmp<128> result;
|
|
StringTmp<128> expect;
|
|
|
|
if (test->run(func, result, expect)) {
|
|
if (!_verbose)
|
|
printf("%s[RUN OK]\n", status_separator);
|
|
}
|
|
else {
|
|
if (!_verbose)
|
|
printf("%s[RUN FAILED]\n", status_separator);
|
|
|
|
print_string_logger_content();
|
|
printf(" [Output]\n");
|
|
printf(" Returned: %s\n", result.data());
|
|
printf(" Expected: %s\n", expect.data());
|
|
_num_failed++;
|
|
}
|
|
|
|
if (_dump_asm)
|
|
printf("\n");
|
|
|
|
runtime.release(func);
|
|
}
|
|
else {
|
|
if (!_verbose)
|
|
printf("%s[COMPILE FAILED]\n", status_separator);
|
|
|
|
print_string_logger_content();
|
|
printf(" [Status]\n");
|
|
printf(" ERROR 0x%08X: %s\n", unsigned(err), error_handler._message.data());
|
|
_num_failed++;
|
|
}
|
|
}
|
|
#endif // !ASMJIT_NO_JIT
|
|
|
|
if (!runnable) {
|
|
if (err != Error::kOk) {
|
|
printf(" [Status]\n");
|
|
printf(" ERROR 0x%08X: %s\n", unsigned(err), error_handler._message.data());
|
|
_num_failed++;
|
|
}
|
|
else {
|
|
printf("%s[COMPILE OK]\n", status_separator);
|
|
}
|
|
}
|
|
|
|
#ifndef ASMJIT_NO_LOGGING
|
|
if (_verbose || _dump_asm || _dump_hex) {
|
|
printf("\n");
|
|
}
|
|
#endif // !ASMJIT_NO_LOGGING
|
|
}
|
|
}
|
|
|
|
printf("\n");
|
|
printf("Summary:\n");
|
|
printf(" OutputSize: %zu bytes\n", _output_size);
|
|
printf(" CompileTime: %.2f ms\n", compile_time);
|
|
printf(" FinalizeTime: %.2f ms\n", finalize_time);
|
|
printf("\n");
|
|
|
|
if (_num_failed == 0)
|
|
printf("** SUCCESS: All %u tests passed **\n", _num_tests);
|
|
else
|
|
printf("** FAILURE: %u of %u tests failed **\n", _num_failed, _num_tests);
|
|
|
|
return _num_failed == 0 ? 0 : 1;
|
|
}
|
|
|
|
int main(int argc, char* argv[]) {
|
|
TestApp app;
|
|
|
|
app.handle_args(argc, argv);
|
|
app.show_info();
|
|
|
|
#if !defined(ASMJIT_NO_X86)
|
|
compiler_add_x86_tests(app);
|
|
#endif // !ASMJIT_NO_X86
|
|
|
|
#if !defined(ASMJIT_NO_AARCH64)
|
|
compiler_add_a64_tests(app);
|
|
#endif // !ASMJIT_NO_AARCH64
|
|
|
|
return app.run();
|
|
}
|
|
|
|
#else
|
|
|
|
int main(int argc, char* argv[]) {
|
|
Support::maybe_unused(argc, argv);
|
|
|
|
printf("AsmJit Compiler Test suite is disabled when compiling with ASMJIT_NO_COMPILER\n\n");
|
|
return 0;
|
|
}
|
|
|
|
#endif // !ASMJIT_NO_COMPILER
|