mirror of
https://github.com/asmjit/asmjit.git
synced 2025-12-18 13:04:36 +03:00
Refactored build system macros (ASMJIT_BUILD_STATIC -> ASMJIT_STATIC)
Refactored AVX512 broadcast {1toN} - moved to operand from instruction.
Refactored naming - renamed getters to not use get prefix.
Refactored code structure - move arch-specific stuff into x86 namespace.
Refactored some compiler/arch-specific macros, respect rel/abs option in mov REG, [ADDR].
Refactored StringBuilder (Renamed to String, added small string optimization).
Refactored LabelId<->LabelEntry mapping, force label offset to 64-bits on all archs.
Renamed Runtime to Target (JitRuntime kept for now).
Renamed VirtMemManager to JitAllocator.
Renamed VirtMem to JitUtils.
Renamed FuncSignatureX to FuncSignatureBuilder.
Fixed xchg [mem], rex-lo, refactored RelocEntry.
Fixed Logger to always show abs|rel when formatting a memory operand
Fixed Logger to prefix HEX numbers with 0x prefix
Fixed Support::ctzGeneric to always return uint32_t, T doesn't matter.
Fixed LightCall to not save MMX and K registers
Fixed CpuInfo constructor to propagate NoInit (#243)
Added VAES, AVX512_VBMI2, AVX512_VNNI, and AVX512_BITALG cpu-features and instructions.
Added emscripten support (asmjit can be now compiled by emscripten).
Added asmjit.natvis for better MSVC experience
Added x86::ptr_abs|ptr_rel
Added support for multibyte nop r/m (#135)
Added support for 32-bit to 64-bit zero-extended addresses, improved validation of memory addresses, and removed wrt address type as this will be reworked
Added support for multiple sections, reworked address table support (previously known as trampolines)
Added the following x86 modifiers to the x86::Emitter - xacquire(), xrelease(), and k(kreg)
Added a possibility to use REP prefix with RET instruction
Added a possibility to relocate [rel addr] during relocate()
Added a variadic function-call support (Compiler), argument duplication (Compiler), better /dev/shm vs /tmp shared memory handling (VirtMem).
Removed imm_u imm_ptr helpers, imm() can now accept any integer and pointer.
Changed the default behavior of optimizing instructions to disabled with a possibility to enable that feature through kOptionOptimizedForSize
Use default copy construction / assignment to prevent new kind of warnings introduced by GCC 9
271 lines
6.3 KiB
C++
271 lines
6.3 KiB
C++
// [Broken]
|
|
// Lightweight Unit Testing for C++.
|
|
//
|
|
// [License]
|
|
// Public Domain (Unlicense)
|
|
|
|
#include "./broken.h"
|
|
#include <stdarg.h>
|
|
|
|
// ============================================================================
|
|
// [Broken - Global]
|
|
// ============================================================================
|
|
|
|
// Zero initialized globals.
|
|
struct BrokenGlobal {
|
|
// Application arguments.
|
|
int _argc;
|
|
const char** _argv;
|
|
|
|
// Output file.
|
|
FILE* _file;
|
|
|
|
// Unit tests.
|
|
BrokenAPI::Unit* _unitList;
|
|
BrokenAPI::Unit* _unitRunning;
|
|
|
|
bool hasArg(const char* a) const noexcept {
|
|
for (int i = 1; i < _argc; i++)
|
|
if (strcmp(_argv[i], a) == 0)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
inline FILE* file() const noexcept { return _file ? _file : stdout; }
|
|
};
|
|
static BrokenGlobal _brokenGlobal;
|
|
|
|
// ============================================================================
|
|
// [Broken - API]
|
|
// ============================================================================
|
|
|
|
// Get whether the string `a` starts with string `b`.
|
|
static bool BrokenAPI_startsWith(const char* a, const char* b) noexcept {
|
|
for (size_t i = 0; ; i++) {
|
|
if (b[i] == '\0') return true;
|
|
if (a[i] != b[i]) return false;
|
|
}
|
|
}
|
|
|
|
// Get whether the strings `a` and `b` are equal, ignoring case and treating
|
|
// `-` as `_`.
|
|
static bool BrokenAPI_matchesFilter(const char* a, const char* b) noexcept {
|
|
for (size_t i = 0; ; i++) {
|
|
int ca = (unsigned char)a[i];
|
|
int cb = (unsigned char)b[i];
|
|
|
|
// If filter is defined as wildcard the rest automatically matches.
|
|
if (cb == '*')
|
|
return true;
|
|
|
|
if (ca == '-') ca = '_';
|
|
if (cb == '-') cb = '_';
|
|
|
|
if (ca >= 'A' && ca <= 'Z') ca += 'a' - 'A';
|
|
if (cb >= 'A' && cb <= 'Z') cb += 'a' - 'A';
|
|
|
|
if (ca != cb)
|
|
return false;
|
|
|
|
if (ca == '\0')
|
|
return true;
|
|
}
|
|
}
|
|
|
|
static bool BrokenAPI_canRun(BrokenAPI::Unit* unit) noexcept {
|
|
BrokenGlobal& global = _brokenGlobal;
|
|
|
|
int i, argc = global._argc;
|
|
const char** argv = global._argv;
|
|
|
|
const char* unitName = unit->name;
|
|
bool hasFilter = false;
|
|
|
|
for (i = 1; i < argc; i++) {
|
|
const char* arg = argv[i];
|
|
|
|
if (BrokenAPI_startsWith(arg, "--run-") && strcmp(arg, "--run-all") != 0) {
|
|
hasFilter = true;
|
|
|
|
if (BrokenAPI_matchesFilter(unitName, arg + 6))
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// If no filter has been specified the default is to run.
|
|
return !hasFilter;
|
|
}
|
|
|
|
static void BrokenAPI_runUnit(BrokenAPI::Unit* unit) noexcept {
|
|
BrokenAPI::info("Running %s", unit->name);
|
|
|
|
_brokenGlobal._unitRunning = unit;
|
|
unit->entry();
|
|
_brokenGlobal._unitRunning = NULL;
|
|
}
|
|
|
|
static void BrokenAPI_runAll() noexcept {
|
|
BrokenAPI::Unit* unit = _brokenGlobal._unitList;
|
|
|
|
bool hasUnits = unit != NULL;
|
|
size_t count = 0;
|
|
|
|
while (unit != NULL) {
|
|
if (BrokenAPI_canRun(unit)) {
|
|
BrokenAPI_runUnit(unit);
|
|
count++;
|
|
}
|
|
unit = unit->next;
|
|
}
|
|
|
|
if (count) {
|
|
INFO("\nSuccess:");
|
|
INFO(" All tests passed!");
|
|
}
|
|
else {
|
|
INFO("\nWarning:");
|
|
INFO(" No units %s!", hasUnits ? "matched the filter" : "defined");
|
|
}
|
|
}
|
|
|
|
static void BrokenAPI_listAll() noexcept {
|
|
BrokenAPI::Unit* unit = _brokenGlobal._unitList;
|
|
|
|
if (unit != NULL) {
|
|
INFO("Units:");
|
|
do {
|
|
INFO(" %s", unit->name);
|
|
unit = unit->next;
|
|
} while (unit != NULL);
|
|
}
|
|
else {
|
|
INFO("Warning:");
|
|
INFO(" No units defined!");
|
|
}
|
|
}
|
|
|
|
bool BrokenAPI::hasArg(const char* name) noexcept {
|
|
return _brokenGlobal.hasArg(name);
|
|
}
|
|
|
|
void BrokenAPI::add(Unit* unit) noexcept {
|
|
Unit** pPrev = &_brokenGlobal._unitList;
|
|
Unit* current = *pPrev;
|
|
|
|
// C++ static initialization doesn't guarantee anything. We sort all units by
|
|
// name so the execution will always happen in deterministic order.
|
|
while (current != NULL) {
|
|
if (strcmp(current->name, unit->name) >= 0)
|
|
break;
|
|
|
|
pPrev = ¤t->next;
|
|
current = *pPrev;
|
|
}
|
|
|
|
*pPrev = unit;
|
|
unit->next = current;
|
|
}
|
|
|
|
void BrokenAPI::setOutputFile(FILE* file) noexcept {
|
|
BrokenGlobal& global = _brokenGlobal;
|
|
|
|
global._file = file;
|
|
}
|
|
|
|
int BrokenAPI::run(int argc, const char* argv[], Entry onBeforeRun, Entry onAfterRun) noexcept {
|
|
BrokenGlobal& global = _brokenGlobal;
|
|
|
|
global._argc = argc;
|
|
global._argv = argv;
|
|
|
|
if (global.hasArg("--help")) {
|
|
INFO("Options:");
|
|
INFO(" --help - print this usage");
|
|
INFO(" --list - list all tests");
|
|
INFO(" --run-... - run a test(s), trailing wildcards supported");
|
|
INFO(" --run-all - run all tests");
|
|
return 0;
|
|
}
|
|
|
|
if (global.hasArg("--list")) {
|
|
BrokenAPI_listAll();
|
|
return 0;
|
|
}
|
|
|
|
if (onBeforeRun)
|
|
onBeforeRun();
|
|
|
|
// We don't care about filters here, it's implemented by `runAll`.
|
|
BrokenAPI_runAll();
|
|
|
|
if (onAfterRun)
|
|
onAfterRun();
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void BrokenAPI_printMessage(const char* prefix, const char* fmt, va_list ap) noexcept {
|
|
BrokenGlobal& global = _brokenGlobal;
|
|
FILE* dst = global.file();
|
|
|
|
if (!fmt || fmt[0] == '\0') {
|
|
fprintf(dst, "\n");
|
|
}
|
|
else {
|
|
// This looks scary, but we really want to use only a single call to vfprintf()
|
|
// in multithreaded code. So we change the format a bit if necessary.
|
|
enum : unsigned { kBufferSize = 512 };
|
|
char staticBuffer[512];
|
|
|
|
size_t fmtSize = strlen(fmt);
|
|
size_t prefixSize = strlen(prefix);
|
|
|
|
char* fmtBuf = staticBuffer;
|
|
if (fmtSize > kBufferSize - 2 - prefixSize)
|
|
fmtBuf = static_cast<char*>(malloc(fmtSize + prefixSize + 2));
|
|
|
|
if (!fmtBuf) {
|
|
fprintf(dst, "%sCannot allocate buffer for vfprintf()\n", prefix);
|
|
}
|
|
else {
|
|
memcpy(fmtBuf, prefix, prefixSize);
|
|
memcpy(fmtBuf + prefixSize, fmt, fmtSize);
|
|
|
|
fmtSize += prefixSize;
|
|
if (fmtBuf[fmtSize - 1] != '\n')
|
|
fmtBuf[fmtSize++] = '\n';
|
|
fmtBuf[fmtSize] = '\0';
|
|
|
|
vfprintf(dst, fmtBuf, ap);
|
|
|
|
if (fmtBuf != staticBuffer)
|
|
free(fmtBuf);
|
|
}
|
|
}
|
|
|
|
fflush(dst);
|
|
}
|
|
|
|
void BrokenAPI::info(const char* fmt, ...) noexcept {
|
|
BrokenGlobal& global = _brokenGlobal;
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
BrokenAPI_printMessage(global._unitRunning ? " " : "", fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
void BrokenAPI::fail(const char* file, int line, const char* fmt, ...) noexcept {
|
|
BrokenGlobal& global = _brokenGlobal;
|
|
FILE* dst = global.file();
|
|
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
BrokenAPI_printMessage(" FAILED!", fmt, ap);
|
|
va_end(ap);
|
|
|
|
fprintf(dst, " File: %s (Line: %d)\n", file, line);
|
|
fflush(dst);
|
|
|
|
exit(1);
|
|
}
|