First draft of dynamic detours using Ayuto's DynamicHooks library

https://github.com/Ayuto/DynamicHooks
This commit is contained in:
Peace-Maker
2016-12-11 22:02:10 -07:00
parent fd8866a540
commit 2e52ab24b7
84 changed files with 57658 additions and 114 deletions
+503
View File
@@ -0,0 +1,503 @@
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Export]
#define ASMJIT_EXPORTS
// [Dependencies]
#include "../base/assembler.h"
#include "../base/utils.h"
#include "../base/vmem.h"
#include <stdarg.h>
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
// ============================================================================
// [asmjit::ErrorHandler]
// ============================================================================
ErrorHandler::ErrorHandler() noexcept {}
ErrorHandler::~ErrorHandler() noexcept {}
ErrorHandler* ErrorHandler::addRef() const noexcept {
return const_cast<ErrorHandler*>(this);
}
void ErrorHandler::release() noexcept {}
// ============================================================================
// [asmjit::ExternalTool]
// ============================================================================
ExternalTool::ExternalTool() noexcept
: _assembler(nullptr),
_exId(0),
_arch(kArchNone),
_regSize(0),
_finalized(false),
_reserved(0),
_lastError(kErrorNotInitialized) {}
ExternalTool::~ExternalTool() noexcept {}
Error ExternalTool::setLastError(Error error, const char* message) noexcept {
// Special case, reset the last error the error is `kErrorOk`.
if (error == kErrorOk) {
_lastError = kErrorOk;
return kErrorOk;
}
// Don't do anything if the code-generator doesn't have associated assembler.
Assembler* assembler = getAssembler();
if (assembler == nullptr)
return error;
if (message == nullptr)
message = DebugUtils::errorAsString(error);
// Logging is skipped if the error is handled by `ErrorHandler.
ErrorHandler* eh = assembler->getErrorHandler();
ASMJIT_TLOG("[ERROR (ExternalTool)] %s (0x%0.8u) %s\n", message,
static_cast<unsigned int>(error),
!eh ? "(Possibly unhandled?)" : "");
if (eh != nullptr && eh->handleError(error, message, this))
return error;
#if !defined(ASMJIT_DISABLE_LOGGER)
Logger* logger = assembler->getLogger();
if (logger != nullptr)
logger->logFormat(Logger::kStyleComment,
"*** ERROR (ExternalTool): %s (0x%0.8u).\n", message,
static_cast<unsigned int>(error));
#endif // !ASMJIT_DISABLE_LOGGER
// The handler->handleError() function may throw an exception or longjmp()
// to terminate the execution of `setLastError()`. This is the reason why
// we have delayed changing the `_error` member until now.
_lastError = error;
return error;
}
// ============================================================================
// [asmjit::Assembler - Construction / Destruction]
// ============================================================================
Assembler::Assembler(Runtime* runtime) noexcept
: _runtime(runtime),
_logger(nullptr),
_errorHandler(nullptr),
_arch(kArchNone),
_regSize(0),
_reserved(0),
_asmOptions(0),
_instOptions(0),
_lastError(runtime ? kErrorOk : kErrorNotInitialized),
_exIdGenerator(0),
_exCountAttached(0),
_zoneAllocator(8192 - Zone::kZoneOverhead),
_buffer(nullptr),
_end(nullptr),
_cursor(nullptr),
_trampolinesSize(0),
_comment(nullptr),
_unusedLinks(nullptr),
_labels(),
_relocations() {}
Assembler::~Assembler() noexcept {
reset(true);
if (_errorHandler != nullptr)
_errorHandler->release();
}
// ============================================================================
// [asmjit::Assembler - Reset]
// ============================================================================
void Assembler::reset(bool releaseMemory) noexcept {
_asmOptions = 0;
_instOptions = 0;
_lastError = kErrorOk;
_exIdGenerator = 0;
_exCountAttached = 0;
_zoneAllocator.reset(releaseMemory);
if (releaseMemory && _buffer != nullptr) {
ASMJIT_FREE(_buffer);
_buffer = nullptr;
_end = nullptr;
}
_cursor = _buffer;
_trampolinesSize = 0;
_comment = nullptr;
_unusedLinks = nullptr;
_sections.reset(releaseMemory);
_labels.reset(releaseMemory);
_relocations.reset(releaseMemory);
}
// ============================================================================
// [asmjit::Assembler - Logging & Error Handling]
// ============================================================================
Error Assembler::setLastError(Error error, const char* message) noexcept {
// Special case, reset the last error the error is `kErrorOk`.
if (error == kErrorOk) {
_lastError = kErrorOk;
return kErrorOk;
}
if (message == nullptr)
message = DebugUtils::errorAsString(error);
// Logging is skipped if the error is handled by `ErrorHandler`.
ErrorHandler* eh = _errorHandler;
ASMJIT_TLOG("[ERROR (Assembler)] %s (0x%0.8u) %s\n", message,
static_cast<unsigned int>(error),
!eh ? "(Possibly unhandled?)" : "");
if (eh != nullptr && eh->handleError(error, message, this))
return error;
#if !defined(ASMJIT_DISABLE_LOGGER)
Logger* logger = _logger;
if (logger != nullptr)
logger->logFormat(Logger::kStyleComment,
"*** ERROR (Assembler): %s (0x%0.8u).\n", message,
static_cast<unsigned int>(error));
#endif // !ASMJIT_DISABLE_LOGGER
// The handler->handleError() function may throw an exception or longjmp()
// to terminate the execution of `setLastError()`. This is the reason why
// we have delayed changing the `_error` member until now.
_lastError = error;
return error;
}
Error Assembler::setErrorHandler(ErrorHandler* handler) noexcept {
ErrorHandler* oldHandler = _errorHandler;
if (oldHandler != nullptr)
oldHandler->release();
if (handler != nullptr)
handler = handler->addRef();
_errorHandler = handler;
return kErrorOk;
}
// ============================================================================
// [asmjit::Assembler - Buffer]
// ============================================================================
Error Assembler::_grow(size_t n) noexcept {
size_t capacity = getCapacity();
size_t after = getOffset() + n;
// Overflow.
if (n > IntTraits<uintptr_t>::maxValue() - capacity)
return setLastError(kErrorNoHeapMemory);
// Grow is called when allocation is needed, so it shouldn't happen, but on
// the other hand it is simple to catch and it's not an error.
if (after <= capacity)
return kErrorOk;
if (capacity < kMemAllocOverhead)
capacity = kMemAllocOverhead;
else
capacity += kMemAllocOverhead;
do {
size_t oldCapacity = capacity;
if (capacity < kMemAllocGrowMax)
capacity *= 2;
else
capacity += kMemAllocGrowMax;
// Overflow.
if (oldCapacity > capacity)
return setLastError(kErrorNoHeapMemory);
} while (capacity - kMemAllocOverhead < after);
capacity -= kMemAllocOverhead;
return _reserve(capacity);
}
Error Assembler::_reserve(size_t n) noexcept {
size_t capacity = getCapacity();
if (n <= capacity)
return kErrorOk;
uint8_t* newBuffer;
if (_buffer == nullptr)
newBuffer = static_cast<uint8_t*>(ASMJIT_ALLOC(n));
else
newBuffer = static_cast<uint8_t*>(ASMJIT_REALLOC(_buffer, n));
if (newBuffer == nullptr)
return setLastError(kErrorNoHeapMemory);
size_t offset = getOffset();
_buffer = newBuffer;
_end = _buffer + n;
_cursor = newBuffer + offset;
return kErrorOk;
}
// ============================================================================
// [asmjit::Assembler - Label]
// ============================================================================
Error Assembler::_newLabelId() noexcept {
LabelData* data = _zoneAllocator.allocT<LabelData>();
data->offset = -1;
data->links = nullptr;
data->exId = 0;
data->exData = nullptr;
uint32_t id = OperandUtil::makeLabelId(static_cast<uint32_t>(_labels.getLength()));
Error error = _labels.append(data);
if (error != kErrorOk) {
setLastError(kErrorNoHeapMemory);
return kInvalidValue;
}
return id;
}
LabelLink* Assembler::_newLabelLink() noexcept {
LabelLink* link = _unusedLinks;
if (link) {
_unusedLinks = link->prev;
}
else {
link = _zoneAllocator.allocT<LabelLink>();
if (link == nullptr)
return nullptr;
}
link->prev = nullptr;
link->offset = 0;
link->displacement = 0;
link->relocId = -1;
return link;
}
Error Assembler::bind(const Label& label) noexcept {
// Get label data based on label id.
uint32_t index = label.getId();
LabelData* data = getLabelData(index);
// Label can be bound only once.
if (data->offset != -1)
return setLastError(kErrorLabelAlreadyBound);
#if !defined(ASMJIT_DISABLE_LOGGER)
if (_logger) {
StringBuilderTmp<256> sb;
sb.setFormat("L%u:", index);
size_t binSize = 0;
if (!_logger->hasOption(Logger::kOptionBinaryForm))
binSize = kInvalidIndex;
LogUtil::formatLine(sb, nullptr, binSize, 0, 0, _comment);
_logger->logString(Logger::kStyleLabel, sb.getData(), sb.getLength());
}
#endif // !ASMJIT_DISABLE_LOGGER
Error error = kErrorOk;
size_t pos = getOffset();
LabelLink* link = data->links;
LabelLink* prev = nullptr;
while (link) {
intptr_t offset = link->offset;
if (link->relocId != -1) {
// Handle RelocData - We have to update RelocData information instead of
// patching the displacement in LabelData.
_relocations[link->relocId].data += static_cast<Ptr>(pos);
}
else {
// Not using relocId, this means that we are overwriting a real
// displacement in the binary stream.
int32_t patchedValue = static_cast<int32_t>(
static_cast<intptr_t>(pos) - offset + link->displacement);
// Size of the value we are going to patch. Only BYTE/DWORD is allowed.
uint32_t size = readU8At(offset);
ASMJIT_ASSERT(size == 1 || size == 4);
if (size == 4) {
writeI32At(offset, patchedValue);
}
else {
ASMJIT_ASSERT(size == 1);
if (Utils::isInt8(patchedValue))
writeU8At(offset, static_cast<uint32_t>(patchedValue) & 0xFF);
else
error = kErrorIllegalDisplacement;
}
}
prev = link->prev;
link = prev;
}
// Chain unused links.
link = data->links;
if (link) {
if (prev == nullptr)
prev = link;
prev->prev = _unusedLinks;
_unusedLinks = link;
}
// Set as bound (offset is zero or greater and no links).
data->offset = pos;
data->links = nullptr;
if (error != kErrorOk)
return setLastError(error);
_comment = nullptr;
return error;
}
// ============================================================================
// [asmjit::Assembler - Embed]
// ============================================================================
Error Assembler::embed(const void* data, uint32_t size) noexcept {
if (getRemainingSpace() < size) {
Error error = _grow(size);
if (error != kErrorOk)
return setLastError(error);
}
uint8_t* cursor = getCursor();
::memcpy(cursor, data, size);
setCursor(cursor + size);
#if !defined(ASMJIT_DISABLE_LOGGER)
if (_logger)
_logger->logBinary(Logger::kStyleData, data, size);
#endif // !ASMJIT_DISABLE_LOGGER
return kErrorOk;
}
// ============================================================================
// [asmjit::Assembler - Reloc]
// ============================================================================
size_t Assembler::relocCode(void* dst, Ptr baseAddress) const noexcept {
if (baseAddress == kNoBaseAddress)
baseAddress = static_cast<Ptr>((uintptr_t)dst);
return _relocCode(dst, baseAddress);
}
// ============================================================================
// [asmjit::Assembler - Make]
// ============================================================================
void* Assembler::make() noexcept {
// Do nothing on error condition or if no instruction has been emitted.
if (_lastError != kErrorOk || getCodeSize() == 0)
return nullptr;
void* p;
Error error = _runtime->add(&p, this);
if (error != kErrorOk)
setLastError(error);
return p;
}
// ============================================================================
// [asmjit::Assembler - Emit (Helpers)]
// ============================================================================
#define NA noOperand
Error Assembler::emit(uint32_t code) {
return _emit(code, NA, NA, NA, NA);
}
Error Assembler::emit(uint32_t code, const Operand& o0) {
return _emit(code, o0, NA, NA, NA);
}
Error Assembler::emit(uint32_t code, const Operand& o0, const Operand& o1) {
return _emit(code, o0, o1, NA, NA);
}
Error Assembler::emit(uint32_t code, const Operand& o0, const Operand& o1, const Operand& o2) {
return _emit(code, o0, o1, o2, NA);
}
Error Assembler::emit(uint32_t code, const Operand& o0, const Operand& o1, const Operand& o2, const Operand& o3) {
return _emit(code, o0, o1, o2, o3);
}
Error Assembler::emit(uint32_t code, int o0) {
return _emit(code, Imm(o0), NA, NA, NA);
}
Error Assembler::emit(uint32_t code, const Operand& o0, int o1) {
return _emit(code, o0, Imm(o1), NA, NA);
}
Error Assembler::emit(uint32_t code, const Operand& o0, const Operand& o1, int o2) {
return _emit(code, o0, o1, Imm(o2), NA);
}
Error Assembler::emit(uint32_t code, const Operand& o0, const Operand& o1, const Operand& o2, int o3) {
return _emit(code, o0, o1, o2, Imm(o3));
}
Error Assembler::emit(uint32_t code, int64_t o0) {
return _emit(code, Imm(o0), NA, NA, NA);
}
Error Assembler::emit(uint32_t code, const Operand& o0, int64_t o1) {
return _emit(code, o0, Imm(o1), NA, NA);
}
Error Assembler::emit(uint32_t code, const Operand& o0, const Operand& o1, int64_t o2) {
return _emit(code, o0, o1, Imm(o2), NA);
}
Error Assembler::emit(uint32_t code, const Operand& o0, const Operand& o1, const Operand& o2, int64_t o3) {
return _emit(code, o0, o1, o2, Imm(o3));
}
#undef NA
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
File diff suppressed because it is too large Load Diff
+630
View File
@@ -0,0 +1,630 @@
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Export]
#define ASMJIT_EXPORTS
// [Guard]
#include "../build.h"
#if !defined(ASMJIT_DISABLE_COMPILER)
// [Dependencies]
#include "../base/assembler.h"
#include "../base/compiler.h"
#include "../base/compilercontext_p.h"
#include "../base/cpuinfo.h"
#include "../base/logger.h"
#include "../base/utils.h"
#include <stdarg.h>
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
// ============================================================================
// [Constants]
// ============================================================================
static const char noName[1] = { '\0' };
enum { kCompilerDefaultLookAhead = 64 };
// ============================================================================
// [asmjit::Compiler - Construction / Destruction]
// ============================================================================
Compiler::Compiler() noexcept
: _features(0),
_maxLookAhead(kCompilerDefaultLookAhead),
_instOptions(0),
_tokenGenerator(0),
_nodeFlowId(0),
_nodeFlags(0),
_targetVarMapping(nullptr),
_firstNode(nullptr),
_lastNode(nullptr),
_cursor(nullptr),
_func(nullptr),
_zoneAllocator(8192 - Zone::kZoneOverhead),
_varAllocator(4096 - Zone::kZoneOverhead),
_stringAllocator(4096 - Zone::kZoneOverhead),
_constAllocator(4096 - Zone::kZoneOverhead),
_localConstPool(&_constAllocator),
_globalConstPool(&_zoneAllocator) {}
Compiler::~Compiler() noexcept {}
// ============================================================================
// [asmjit::Compiler - Attach / Reset]
// ============================================================================
void Compiler::reset(bool releaseMemory) noexcept {
Assembler* assembler = getAssembler();
if (assembler != nullptr)
assembler->_detached(this);
_arch = kArchNone;
_regSize = 0;
_finalized = false;
_lastError = kErrorNotInitialized;
_features = 0;
_maxLookAhead = kCompilerDefaultLookAhead;
_instOptions = 0;
_tokenGenerator = 0;
_nodeFlowId = 0;
_nodeFlags = 0;
_firstNode = nullptr;
_lastNode = nullptr;
_cursor = nullptr;
_func = nullptr;
_localConstPool.reset();
_globalConstPool.reset();
_localConstPoolLabel.reset();
_globalConstPoolLabel.reset();
_zoneAllocator.reset(releaseMemory);
_varAllocator.reset(releaseMemory);
_stringAllocator.reset(releaseMemory);
_constAllocator.reset(releaseMemory);
_varList.reset(releaseMemory);
}
// ============================================================================
// [asmjit::Compiler - Node-Factory]
// ============================================================================
HLData* Compiler::newDataNode(const void* data, uint32_t size) noexcept {
if (size > HLData::kInlineBufferSize) {
void* clonedData = _stringAllocator.alloc(size);
if (clonedData == nullptr)
return nullptr;
if (data != nullptr)
::memcpy(clonedData, data, size);
data = clonedData;
}
return newNode<HLData>(const_cast<void*>(data), size);
}
HLAlign* Compiler::newAlignNode(uint32_t alignMode, uint32_t offset) noexcept {
return newNode<HLAlign>(alignMode, offset);
}
HLLabel* Compiler::newLabelNode() noexcept {
Assembler* assembler = getAssembler();
if (assembler == nullptr) return nullptr;
uint32_t id = assembler->_newLabelId();
LabelData* ld = assembler->getLabelData(id);
HLLabel* node = newNode<HLLabel>(id);
if (node == nullptr) return nullptr;
// These have to be zero now.
ASMJIT_ASSERT(ld->exId == 0);
ASMJIT_ASSERT(ld->exData == nullptr);
ld->exId = _exId;
ld->exData = node;
return node;
}
HLComment* Compiler::newCommentNode(const char* str) noexcept {
if (str != nullptr && str[0]) {
str = _stringAllocator.sdup(str);
if (str == nullptr)
return nullptr;
}
return newNode<HLComment>(str);
}
HLHint* Compiler::newHintNode(Var& var, uint32_t hint, uint32_t value) noexcept {
if (var.getId() == kInvalidValue)
return nullptr;
VarData* vd = getVd(var);
return newNode<HLHint>(vd, hint, value);
}
// ============================================================================
// [asmjit::Compiler - Code-Stream]
// ============================================================================
HLNode* Compiler::addFunc(HLFunc* func) noexcept {
ASMJIT_ASSERT(_func == nullptr);
_func = func;
addNode(func); // Add function node.
addNode(func->getEntryNode()); // Add function entry.
HLNode* cursor = getCursor();
addNode(func->getExitNode()); // Add function exit / epilog marker.
addNode(func->getEnd()); // Add function end.
setCursor(cursor);
return func;
}
HLNode* Compiler::addNode(HLNode* node) noexcept {
ASMJIT_ASSERT(node != nullptr);
ASMJIT_ASSERT(node->_prev == nullptr);
ASMJIT_ASSERT(node->_next == nullptr);
if (_cursor == nullptr) {
if (_firstNode == nullptr) {
_firstNode = node;
_lastNode = node;
}
else {
node->_next = _firstNode;
_firstNode->_prev = node;
_firstNode = node;
}
}
else {
HLNode* prev = _cursor;
HLNode* next = _cursor->_next;
node->_prev = prev;
node->_next = next;
prev->_next = node;
if (next)
next->_prev = node;
else
_lastNode = node;
}
_cursor = node;
return node;
}
HLNode* Compiler::addNodeBefore(HLNode* node, HLNode* ref) noexcept {
ASMJIT_ASSERT(node != nullptr);
ASMJIT_ASSERT(node->_prev == nullptr);
ASMJIT_ASSERT(node->_next == nullptr);
ASMJIT_ASSERT(ref != nullptr);
HLNode* prev = ref->_prev;
HLNode* next = ref;
node->_prev = prev;
node->_next = next;
next->_prev = node;
if (prev)
prev->_next = node;
else
_firstNode = node;
return node;
}
HLNode* Compiler::addNodeAfter(HLNode* node, HLNode* ref) noexcept {
ASMJIT_ASSERT(node != nullptr);
ASMJIT_ASSERT(node->_prev == nullptr);
ASMJIT_ASSERT(node->_next == nullptr);
ASMJIT_ASSERT(ref != nullptr);
HLNode* prev = ref;
HLNode* next = ref->_next;
node->_prev = prev;
node->_next = next;
prev->_next = node;
if (next)
next->_prev = node;
else
_lastNode = node;
return node;
}
static ASMJIT_INLINE void Compiler_nodeRemoved(Compiler* self, HLNode* node_) noexcept {
if (node_->isJmpOrJcc()) {
HLJump* node = static_cast<HLJump*>(node_);
HLLabel* label = node->getTarget();
if (label != nullptr) {
// Disconnect.
HLJump** pPrev = &label->_from;
for (;;) {
ASMJIT_ASSERT(*pPrev != nullptr);
HLJump* current = *pPrev;
if (current == nullptr)
break;
if (current == node) {
*pPrev = node->_jumpNext;
break;
}
pPrev = &current->_jumpNext;
}
label->subNumRefs();
}
}
}
HLNode* Compiler::removeNode(HLNode* node) noexcept {
HLNode* prev = node->_prev;
HLNode* next = node->_next;
if (_firstNode == node)
_firstNode = next;
else
prev->_next = next;
if (_lastNode == node)
_lastNode = prev;
else
next->_prev = prev;
node->_prev = nullptr;
node->_next = nullptr;
if (_cursor == node)
_cursor = prev;
Compiler_nodeRemoved(this, node);
return node;
}
void Compiler::removeNodes(HLNode* first, HLNode* last) noexcept {
if (first == last) {
removeNode(first);
return;
}
HLNode* prev = first->_prev;
HLNode* next = last->_next;
if (_firstNode == first)
_firstNode = next;
else
prev->_next = next;
if (_lastNode == last)
_lastNode = prev;
else
next->_prev = prev;
HLNode* node = first;
for (;;) {
HLNode* next = node->getNext();
ASMJIT_ASSERT(next != nullptr);
node->_prev = nullptr;
node->_next = nullptr;
if (_cursor == node)
_cursor = prev;
Compiler_nodeRemoved(this, node);
if (node == last)
break;
node = next;
}
}
HLNode* Compiler::setCursor(HLNode* node) noexcept {
HLNode* old = _cursor;
_cursor = node;
return old;
}
// ============================================================================
// [asmjit::Compiler - Align]
// ============================================================================
Error Compiler::align(uint32_t alignMode, uint32_t offset) noexcept {
HLAlign* node = newAlignNode(alignMode, offset);
if (node == nullptr)
return setLastError(kErrorNoHeapMemory);
addNode(node);
return kErrorOk;
}
// ============================================================================
// [asmjit::Compiler - Label]
// ============================================================================
HLLabel* Compiler::getHLLabel(uint32_t id) const noexcept {
Assembler* assembler = getAssembler();
if (assembler == nullptr) return nullptr;
LabelData* ld = assembler->getLabelData(id);
if (ld->exId == _exId)
return static_cast<HLLabel*>(ld->exData);
else
return nullptr;
}
bool Compiler::isLabelValid(uint32_t id) const noexcept {
Assembler* assembler = getAssembler();
if (assembler == nullptr) return false;
return static_cast<size_t>(id) < assembler->getLabelsCount();
}
uint32_t Compiler::_newLabelId() noexcept {
HLLabel* node = newLabelNode();
if (node == nullptr) {
setLastError(kErrorNoHeapMemory);
return kInvalidValue;
}
return node->getLabelId();
}
Error Compiler::bind(const Label& label) noexcept {
HLLabel* node = getHLLabel(label);
if (node == nullptr)
return setLastError(kErrorInvalidState);
addNode(node);
return kErrorOk;
}
// ============================================================================
// [asmjit::Compiler - Embed]
// ============================================================================
Error Compiler::embed(const void* data, uint32_t size) noexcept {
HLData* node = newDataNode(data, size);
if (node == nullptr)
return setLastError(kErrorNoHeapMemory);
addNode(node);
return kErrorOk;
}
Error Compiler::embedConstPool(const Label& label, const ConstPool& pool) noexcept {
if (label.getId() == kInvalidValue)
return kErrorInvalidState;
align(kAlignData, static_cast<uint32_t>(pool.getAlignment()));
bind(label);
HLData* embedNode = newDataNode(nullptr, static_cast<uint32_t>(pool.getSize()));
if (embedNode == nullptr)
return kErrorNoHeapMemory;
pool.fill(embedNode->getData());
addNode(embedNode);
return kErrorOk;
}
// ============================================================================
// [asmjit::Compiler - Comment]
// ============================================================================
Error Compiler::comment(const char* fmt, ...) noexcept {
char buf[256];
char* p = buf;
if (fmt) {
va_list ap;
va_start(ap, fmt);
p += vsnprintf(p, 254, fmt, ap);
va_end(ap);
}
p[0] = '\0';
HLComment* node = newCommentNode(buf);
if (node == nullptr)
return setLastError(kErrorNoHeapMemory);
addNode(node);
return kErrorOk;
}
// ============================================================================
// [asmjit::Compiler - Hint]
// ============================================================================
Error Compiler::_hint(Var& var, uint32_t hint, uint32_t value) noexcept {
if (var.getId() == kInvalidValue)
return kErrorOk;
HLHint* node = newHintNode(var, hint, value);
if (node == nullptr)
return setLastError(kErrorNoHeapMemory);
addNode(node);
return kErrorOk;
}
// ============================================================================
// [asmjit::Compiler - Vars]
// ============================================================================
VarData* Compiler::_newVd(const VarInfo& vi, const char* name) noexcept {
VarData* vd = reinterpret_cast<VarData*>(_varAllocator.alloc(sizeof(VarData)));
if (ASMJIT_UNLIKELY(vd == nullptr))
goto _NoMemory;
vd->_name = noName;
vd->_id = OperandUtil::makeVarId(static_cast<uint32_t>(_varList.getLength()));
vd->_localId = kInvalidValue;
#if !defined(ASMJIT_DISABLE_LOGGER)
if (name != nullptr && name[0] != '\0') {
vd->_name = _stringAllocator.sdup(name);
}
#endif // !ASMJIT_DISABLE_LOGGER
vd->_type = static_cast<uint8_t>(vi.getTypeId());
vd->_class = static_cast<uint8_t>(vi.getRegClass());
vd->_flags = 0;
vd->_priority = 10;
vd->_state = kVarStateNone;
vd->_regIndex = kInvalidReg;
vd->_isStack = false;
vd->_isMemArg = false;
vd->_isCalculated = false;
vd->_saveOnUnuse = false;
vd->_modified = false;
vd->_reserved0 = 0;
vd->_alignment = static_cast<uint8_t>(Utils::iMin<uint32_t>(vi.getSize(), 64));
vd->_size = vi.getSize();
vd->_homeMask = 0;
vd->_memOffset = 0;
vd->_memCell = nullptr;
vd->rReadCount = 0;
vd->rWriteCount = 0;
vd->mReadCount = 0;
vd->mWriteCount = 0;
vd->_va = nullptr;
if (ASMJIT_UNLIKELY(_varList.append(vd) != kErrorOk))
goto _NoMemory;
return vd;
_NoMemory:
setLastError(kErrorNoHeapMemory);
return nullptr;
}
Error Compiler::alloc(Var& var) noexcept {
if (var.getId() == kInvalidValue)
return kErrorOk;
return _hint(var, kVarHintAlloc, kInvalidValue);
}
Error Compiler::alloc(Var& var, uint32_t regIndex) noexcept {
if (var.getId() == kInvalidValue)
return kErrorOk;
return _hint(var, kVarHintAlloc, regIndex);
}
Error Compiler::alloc(Var& var, const Reg& reg) noexcept {
if (var.getId() == kInvalidValue)
return kErrorOk;
return _hint(var, kVarHintAlloc, reg.getRegIndex());
}
Error Compiler::save(Var& var) noexcept {
if (var.getId() == kInvalidValue)
return kErrorOk;
return _hint(var, kVarHintSave, kInvalidValue);
}
Error Compiler::spill(Var& var) noexcept {
if (var.getId() == kInvalidValue)
return kErrorOk;
return _hint(var, kVarHintSpill, kInvalidValue);
}
Error Compiler::unuse(Var& var) noexcept {
if (var.getId() == kInvalidValue)
return kErrorOk;
return _hint(var, kVarHintUnuse, kInvalidValue);
}
uint32_t Compiler::getPriority(Var& var) const noexcept {
if (var.getId() == kInvalidValue)
return kInvalidValue;
VarData* vd = getVdById(var.getId());
return vd->getPriority();
}
void Compiler::setPriority(Var& var, uint32_t priority) noexcept {
if (var.getId() == kInvalidValue)
return;
if (priority > 255)
priority = 255;
VarData* vd = getVdById(var.getId());
vd->_priority = static_cast<uint8_t>(priority);
}
bool Compiler::getSaveOnUnuse(Var& var) const noexcept {
if (var.getId() == kInvalidValue)
return false;
VarData* vd = getVdById(var.getId());
return static_cast<bool>(vd->_saveOnUnuse);
}
void Compiler::setSaveOnUnuse(Var& var, bool value) noexcept {
if (var.getId() == kInvalidValue)
return;
VarData* vd = getVdById(var.getId());
vd->_saveOnUnuse = value;
}
void Compiler::rename(Var& var, const char* fmt, ...) noexcept {
if (var.getId() == kInvalidValue)
return;
VarData* vd = getVdById(var.getId());
vd->_name = noName;
if (fmt != nullptr && fmt[0] != '\0') {
char buf[64];
va_list ap;
va_start(ap, fmt);
vsnprintf(buf, ASMJIT_ARRAY_SIZE(buf), fmt, ap);
buf[ASMJIT_ARRAY_SIZE(buf) - 1] = '\0';
vd->_name = _stringAllocator.sdup(buf);
va_end(ap);
}
}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
// [Guard]
#endif // !ASMJIT_DISABLE_COMPILER
+576
View File
@@ -0,0 +1,576 @@
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Guard]
#ifndef _ASMJIT_BASE_COMPILER_H
#define _ASMJIT_BASE_COMPILER_H
#include "../build.h"
#if !defined(ASMJIT_DISABLE_COMPILER)
// [Dependencies]
#include "../base/assembler.h"
#include "../base/compilerfunc.h"
#include "../base/constpool.h"
#include "../base/containers.h"
#include "../base/hlstream.h"
#include "../base/operand.h"
#include "../base/podvector.h"
#include "../base/utils.h"
#include "../base/zone.h"
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
// ============================================================================
// [Forward Declarations]
// ============================================================================
struct VarAttr;
struct VarData;
struct VarMap;
struct VarState;
//! \addtogroup asmjit_base
//! \{
// ============================================================================
// [asmjit::CompilerFeatures]
// ============================================================================
ASMJIT_ENUM(CompilerFeatures) {
//! Schedule instructions so they can be executed faster (`Compiler` only).
//!
//! Default `false` - has to be explicitly enabled as the scheduler needs
//! some time to run.
//!
//! X86/X64 Specific
//! ----------------
//!
//! If scheduling is enabled AsmJit will try to reorder instructions to
//! minimize the dependency chain. Scheduler always runs after the registers
//! are allocated so it doesn't change count of register allocs/spills.
//!
//! This feature is highly experimental and untested.
kCompilerFeatureEnableScheduler = 0
};
// ============================================================================
// [asmjit::ConstScope]
// ============================================================================
//! Scope of the constant.
ASMJIT_ENUM(ConstScope) {
//! Local constant, always embedded right after the current function.
kConstScopeLocal = 0,
//! Global constant, embedded at the end of the currently compiled code.
kConstScopeGlobal = 1
};
// ============================================================================
// [asmjit::VarInfo]
// ============================================================================
struct VarInfo {
// ============================================================================
// [Flags]
// ============================================================================
//! \internal
//!
//! Variable flags.
ASMJIT_ENUM(Flags) {
//! Variable contains one or more single-precision floating point.
kFlagSP = 0x10,
//! Variable contains one or more double-precision floating point.
kFlagDP = 0x20,
//! Variable is a vector, contains packed data.
kFlagSIMD = 0x80
};
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Get type id.
ASMJIT_INLINE uint32_t getTypeId() const noexcept { return _typeId; }
//! Get type name.
ASMJIT_INLINE const char* getTypeName() const noexcept { return _typeName; }
//! Get register size in bytes.
ASMJIT_INLINE uint32_t getSize() const noexcept { return _size; }
//! Get variable class, see \ref RegClass.
ASMJIT_INLINE uint32_t getRegClass() const noexcept { return _regClass; }
//! Get register type, see `X86RegType`.
ASMJIT_INLINE uint32_t getRegType() const noexcept { return _regType; }
//! Get type flags, see `VarFlag`.
ASMJIT_INLINE uint32_t getFlags() const noexcept { return _flags; }
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Variable type id.
uint8_t _typeId;
//! Variable and register size (in bytes).
uint8_t _size;
//! Register class, see `RegClass`.
uint8_t _regClass;
//! Register type the variable is mapped to.
uint8_t _regType;
//! Variable info flags, see \ref Flags.
uint32_t _flags;
//! Variable type name.
char _typeName[8];
};
// ============================================================================
// [asmjit::Compiler]
// ============================================================================
//! Compiler interface.
//!
//! \sa Assembler.
class ASMJIT_VIRTAPI Compiler : public ExternalTool {
public:
ASMJIT_NO_COPY(Compiler)
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create a new `Compiler` instance.
ASMJIT_API Compiler() noexcept;
//! Destroy the `Compiler` instance.
ASMJIT_API virtual ~Compiler() noexcept;
// --------------------------------------------------------------------------
// [Reset]
// --------------------------------------------------------------------------
//! \override
ASMJIT_API virtual void reset(bool releaseMemory) noexcept;
// --------------------------------------------------------------------------
// [Compiler Features]
// --------------------------------------------------------------------------
//! Get code-generator features.
ASMJIT_INLINE uint32_t getFeatures() const noexcept {
return _features;
}
//! Set code-generator features.
ASMJIT_INLINE void setFeatures(uint32_t features) noexcept {
_features = features;
}
//! Get code-generator `feature`.
ASMJIT_INLINE bool hasFeature(uint32_t feature) const noexcept {
ASMJIT_ASSERT(feature < 32);
return (_features & (1 << feature)) != 0;
}
//! Set code-generator `feature` to `value`.
ASMJIT_INLINE void setFeature(uint32_t feature, bool value) noexcept {
ASMJIT_ASSERT(feature < 32);
feature = static_cast<uint32_t>(value) << feature;
_features = (_features & ~feature) | feature;
}
//! Get maximum look ahead.
ASMJIT_INLINE uint32_t getMaxLookAhead() const noexcept {
return _maxLookAhead;
}
//! Set maximum look ahead to `val`.
ASMJIT_INLINE void setMaxLookAhead(uint32_t val) noexcept {
_maxLookAhead = val;
}
// --------------------------------------------------------------------------
// [Token ID]
// --------------------------------------------------------------------------
//! \internal
//!
//! Reset the token-id generator.
ASMJIT_INLINE void _resetTokenGenerator() noexcept {
_tokenGenerator = 0;
}
//! \internal
//!
//! Generate a new unique token id.
ASMJIT_INLINE uint32_t _generateUniqueToken() noexcept {
return ++_tokenGenerator;
}
// --------------------------------------------------------------------------
// [Instruction Options]
// --------------------------------------------------------------------------
//! Get options of the next instruction.
ASMJIT_INLINE uint32_t getInstOptions() const noexcept {
return _instOptions;
}
//! Set options of the next instruction.
ASMJIT_INLINE void setInstOptions(uint32_t instOptions) noexcept {
_instOptions = instOptions;
}
//! Get options of the next instruction and reset them.
ASMJIT_INLINE uint32_t getInstOptionsAndReset() {
uint32_t instOptions = _instOptions;
_instOptions = 0;
return instOptions;
};
// --------------------------------------------------------------------------
// [Node-Factory]
// --------------------------------------------------------------------------
//! \internal
template<typename T>
ASMJIT_INLINE T* newNode() noexcept {
void* p = _zoneAllocator.alloc(sizeof(T));
return new(p) T(this);
}
//! \internal
template<typename T, typename P0>
ASMJIT_INLINE T* newNode(P0 p0) noexcept {
void* p = _zoneAllocator.alloc(sizeof(T));
return new(p) T(this, p0);
}
//! \internal
template<typename T, typename P0, typename P1>
ASMJIT_INLINE T* newNode(P0 p0, P1 p1) noexcept {
void* p = _zoneAllocator.alloc(sizeof(T));
return new(p) T(this, p0, p1);
}
//! \internal
template<typename T, typename P0, typename P1, typename P2>
ASMJIT_INLINE T* newNode(P0 p0, P1 p1, P2 p2) noexcept {
void* p = _zoneAllocator.alloc(sizeof(T));
return new(p) T(this, p0, p1, p2);
}
//! \internal
//!
//! Create a new `HLData` node.
ASMJIT_API HLData* newDataNode(const void* data, uint32_t size) noexcept;
//! \internal
//!
//! Create a new `HLAlign` node.
ASMJIT_API HLAlign* newAlignNode(uint32_t alignMode, uint32_t offset) noexcept;
//! \internal
//!
//! Create a new `HLLabel` node.
ASMJIT_API HLLabel* newLabelNode() noexcept;
//! \internal
//!
//! Create a new `HLComment`.
ASMJIT_API HLComment* newCommentNode(const char* str) noexcept;
//! \internal
//!
//! Create a new `HLHint`.
ASMJIT_API HLHint* newHintNode(Var& var, uint32_t hint, uint32_t value) noexcept;
// --------------------------------------------------------------------------
// [Code-Stream]
// --------------------------------------------------------------------------
//! Add a function `node` to the stream.
ASMJIT_API HLNode* addFunc(HLFunc* func) noexcept;
//! Add node `node` after current and set current to `node`.
ASMJIT_API HLNode* addNode(HLNode* node) noexcept;
//! Insert `node` before `ref`.
ASMJIT_API HLNode* addNodeBefore(HLNode* node, HLNode* ref) noexcept;
//! Insert `node` after `ref`.
ASMJIT_API HLNode* addNodeAfter(HLNode* node, HLNode* ref) noexcept;
//! Remove `node`.
ASMJIT_API HLNode* removeNode(HLNode* node) noexcept;
//! Remove multiple nodes.
ASMJIT_API void removeNodes(HLNode* first, HLNode* last) noexcept;
//! Get the first node.
ASMJIT_INLINE HLNode* getFirstNode() const noexcept { return _firstNode; }
//! Get the last node.
ASMJIT_INLINE HLNode* getLastNode() const noexcept { return _lastNode; }
//! Get current node.
//!
//! \note If this method returns `nullptr` it means that nothing has been
//! emitted yet.
ASMJIT_INLINE HLNode* getCursor() const noexcept { return _cursor; }
//! \internal
//!
//! Set the current node without returning the previous node.
ASMJIT_INLINE void _setCursor(HLNode* node) noexcept { _cursor = node; }
//! Set the current node to `node` and return the previous one.
ASMJIT_API HLNode* setCursor(HLNode* node) noexcept;
// --------------------------------------------------------------------------
// [Func]
// --------------------------------------------------------------------------
//! Get current function.
ASMJIT_INLINE HLFunc* getFunc() const noexcept { return _func; }
// --------------------------------------------------------------------------
// [Align]
// --------------------------------------------------------------------------
//! Align target buffer to the `offset` specified.
//!
//! The sequence that is used to fill the gap between the aligned location
//! and the current depends on `alignMode`, see \ref AlignMode.
ASMJIT_API Error align(uint32_t alignMode, uint32_t offset) noexcept;
// --------------------------------------------------------------------------
// [Label]
// --------------------------------------------------------------------------
//! Get `HLLabel` by `id`.
//!
//! NOTE: The label has to be valid, see `isLabelValid()`.
ASMJIT_API HLLabel* getHLLabel(uint32_t id) const noexcept;
//! Get `HLLabel` by `label`.
//!
//! NOTE: The label has to be valid, see `isLabelValid()`.
ASMJIT_INLINE HLLabel* getHLLabel(const Label& label) noexcept {
return getHLLabel(label.getId());
}
//! Get whether the label `id` is valid.
ASMJIT_API bool isLabelValid(uint32_t id) const noexcept;
//! Get whether the `label` is valid.
ASMJIT_INLINE bool isLabelValid(const Label& label) const noexcept {
return isLabelValid(label.getId());
}
//! \internal
//!
//! Create a new label and return its ID.
ASMJIT_API uint32_t _newLabelId() noexcept;
//! Create and return a new `Label`.
ASMJIT_INLINE Label newLabel() noexcept { return Label(_newLabelId()); }
//! Bind label to the current offset.
//!
//! NOTE: Label can be bound only once!
ASMJIT_API Error bind(const Label& label) noexcept;
// --------------------------------------------------------------------------
// [Embed]
// --------------------------------------------------------------------------
//! Embed data.
ASMJIT_API Error embed(const void* data, uint32_t size) noexcept;
//! Embed a constant pool data, adding the following in order:
//! 1. Data alignment.
//! 2. Label.
//! 3. Constant pool data.
ASMJIT_API Error embedConstPool(const Label& label, const ConstPool& pool) noexcept;
// --------------------------------------------------------------------------
// [Comment]
// --------------------------------------------------------------------------
//! Emit a single comment line.
ASMJIT_API Error comment(const char* fmt, ...) noexcept;
// --------------------------------------------------------------------------
// [Hint]
// --------------------------------------------------------------------------
//! Emit a new hint (purery informational node).
ASMJIT_API Error _hint(Var& var, uint32_t hint, uint32_t value) noexcept;
// --------------------------------------------------------------------------
// [Vars]
// --------------------------------------------------------------------------
//! Get whether variable `var` is created.
ASMJIT_INLINE bool isVarValid(const Var& var) const noexcept {
return static_cast<size_t>(var.getId() & Operand::kIdIndexMask) < _varList.getLength();
}
//! \internal
//!
//! Get `VarData` by `var`.
ASMJIT_INLINE VarData* getVd(const Var& var) const noexcept {
return getVdById(var.getId());
}
//! \internal
//!
//! Get `VarData` by `id`.
ASMJIT_INLINE VarData* getVdById(uint32_t id) const noexcept {
ASMJIT_ASSERT(id != kInvalidValue);
ASMJIT_ASSERT(static_cast<size_t>(id & Operand::kIdIndexMask) < _varList.getLength());
return _varList[id & Operand::kIdIndexMask];
}
//! \internal
//!
//! Get an array of 'VarData*'.
ASMJIT_INLINE VarData** _getVdArray() const noexcept {
return const_cast<VarData**>(_varList.getData());
}
//! \internal
//!
//! Create a new `VarData`.
ASMJIT_API VarData* _newVd(const VarInfo& vi, const char* name) noexcept;
//! Alloc variable `var`.
ASMJIT_API Error alloc(Var& var) noexcept;
//! Alloc variable `var` using `regIndex` as a register index.
ASMJIT_API Error alloc(Var& var, uint32_t regIndex) noexcept;
//! Alloc variable `var` using `reg` as a register operand.
ASMJIT_API Error alloc(Var& var, const Reg& reg) noexcept;
//! Spill variable `var`.
ASMJIT_API Error spill(Var& var) noexcept;
//! Save variable `var` if the status is `modified` at this point.
ASMJIT_API Error save(Var& var) noexcept;
//! Unuse variable `var`.
ASMJIT_API Error unuse(Var& var) noexcept;
//! Get priority of variable `var`.
ASMJIT_API uint32_t getPriority(Var& var) const noexcept;
//! Set priority of variable `var` to `priority`.
ASMJIT_API void setPriority(Var& var, uint32_t priority) noexcept;
//! Get save-on-unuse `var` property.
ASMJIT_API bool getSaveOnUnuse(Var& var) const noexcept;
//! Set save-on-unuse `var` property to `value`.
ASMJIT_API void setSaveOnUnuse(Var& var, bool value) noexcept;
//! Rename variable `var` to `name`.
//!
//! NOTE: Only new name will appear in the logger.
ASMJIT_API void rename(Var& var, const char* fmt, ...) noexcept;
// --------------------------------------------------------------------------
// [Stack]
// --------------------------------------------------------------------------
//! \internal
//!
//! Create a new memory chunk allocated on the current function's stack.
virtual Error _newStack(BaseMem* mem, uint32_t size, uint32_t alignment, const char* name) noexcept = 0;
// --------------------------------------------------------------------------
// [Const]
// --------------------------------------------------------------------------
//! \internal
//!
//! Put data to a constant-pool and get a memory reference to it.
virtual Error _newConst(BaseMem* mem, uint32_t scope, const void* data, size_t size) noexcept = 0;
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Code-Generation features, used by \ref hasFeature() and \ref setFeature().
uint32_t _features;
//! Maximum count of nodes to look ahead when allocating/spilling
//! registers.
uint32_t _maxLookAhead;
//! Options affecting the next instruction.
uint32_t _instOptions;
//! Processing token generator.
//!
//! Used to get a unique token that is then used to process `HLNode`s. See
//! `Compiler::_getUniqueToken()` for more details.
uint32_t _tokenGenerator;
//! Flow id added to each node created (used only by `Context)`.
uint32_t _nodeFlowId;
//! Flags added to each node created (used only by `Context)`.
uint32_t _nodeFlags;
//! Variable mapping (translates incoming VarType into target).
const uint8_t* _targetVarMapping;
//! First node.
HLNode* _firstNode;
//! Last node.
HLNode* _lastNode;
//! Current node.
HLNode* _cursor;
//! Current function.
HLFunc* _func;
//! General purpose zone allocator.
Zone _zoneAllocator;
//! Variable zone.
Zone _varAllocator;
//! String/data zone.
Zone _stringAllocator;
//! Local constant pool zone.
Zone _constAllocator;
//! VarData list.
PodVector<VarData*> _varList;
//! Local constant pool, flushed at the end of each function.
ConstPool _localConstPool;
//! Global constant pool, flushed at the end of the compilation.
ConstPool _globalConstPool;
//! Label to start of the local constant pool.
Label _localConstPoolLabel;
//! Label to start of the global constant pool.
Label _globalConstPoolLabel;
};
//! \}
// ============================================================================
// [Defined-Later]
// ============================================================================
ASMJIT_INLINE HLNode::HLNode(Compiler* compiler, uint32_t type) noexcept {
_prev = nullptr;
_next = nullptr;
_type = static_cast<uint8_t>(type);
_opCount = 0;
_flags = static_cast<uint16_t>(compiler->_nodeFlags);
_flowId = compiler->_nodeFlowId;
_tokenId = 0;
_comment = nullptr;
_map = nullptr;
_liveness = nullptr;
_state = nullptr;
}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
// [Guard]
#endif // !ASMJIT_DISABLE_COMPILER
#endif // _ASMJIT_BASE_COMPILER_H
+653
View File
@@ -0,0 +1,653 @@
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Export]
#define ASMJIT_EXPORTS
// [Guard]
#include "../build.h"
#if !defined(ASMJIT_DISABLE_COMPILER)
// [Dependencies]
#include "../base/compilercontext_p.h"
#include "../base/utils.h"
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
// ============================================================================
// [asmjit::Context - Construction / Destruction]
// ============================================================================
Context::Context(Compiler* compiler) :
_compiler(compiler),
_zoneAllocator(8192 - Zone::kZoneOverhead),
_traceNode(nullptr),
_varMapToVaListOffset(0) {
Context::reset();
}
Context::~Context() {}
// ============================================================================
// [asmjit::Context - Reset]
// ============================================================================
void Context::reset(bool releaseMemory) {
_zoneAllocator.reset(releaseMemory);
_func = nullptr;
_start = nullptr;
_end = nullptr;
_extraBlock = nullptr;
_stop = nullptr;
_unreachableList.reset();
_returningList.reset();
_jccList.reset();
_contextVd.reset(releaseMemory);
_memVarCells = nullptr;
_memStackCells = nullptr;
_mem1ByteVarsUsed = 0;
_mem2ByteVarsUsed = 0;
_mem4ByteVarsUsed = 0;
_mem8ByteVarsUsed = 0;
_mem16ByteVarsUsed = 0;
_mem32ByteVarsUsed = 0;
_mem64ByteVarsUsed = 0;
_memStackCellsUsed = 0;
_memMaxAlign = 0;
_memVarTotal = 0;
_memStackTotal = 0;
_memAllTotal = 0;
_annotationLength = 12;
_state = nullptr;
}
// ============================================================================
// [asmjit::Context - Mem]
// ============================================================================
static ASMJIT_INLINE uint32_t BaseContext_getDefaultAlignment(uint32_t size) {
if (size > 32)
return 64;
else if (size > 16)
return 32;
else if (size > 8)
return 16;
else if (size > 4)
return 8;
else if (size > 2)
return 4;
else if (size > 1)
return 2;
else
return 1;
}
VarCell* Context::_newVarCell(VarData* vd) {
ASMJIT_ASSERT(vd->_memCell == nullptr);
VarCell* cell;
uint32_t size = vd->getSize();
if (vd->isStack()) {
cell = _newStackCell(size, vd->getAlignment());
if (cell == nullptr)
return nullptr;
}
else {
cell = static_cast<VarCell*>(_zoneAllocator.alloc(sizeof(VarCell)));
if (cell == nullptr)
goto _NoMemory;
cell->_next = _memVarCells;
_memVarCells = cell;
cell->_offset = 0;
cell->_size = size;
cell->_alignment = size;
_memMaxAlign = Utils::iMax<uint32_t>(_memMaxAlign, size);
_memVarTotal += size;
switch (size) {
case 1: _mem1ByteVarsUsed++ ; break;
case 2: _mem2ByteVarsUsed++ ; break;
case 4: _mem4ByteVarsUsed++ ; break;
case 8: _mem8ByteVarsUsed++ ; break;
case 16: _mem16ByteVarsUsed++; break;
case 32: _mem32ByteVarsUsed++; break;
case 64: _mem64ByteVarsUsed++; break;
default:
ASMJIT_NOT_REACHED();
}
}
vd->_memCell = cell;
return cell;
_NoMemory:
_compiler->setLastError(kErrorNoHeapMemory);
return nullptr;
}
VarCell* Context::_newStackCell(uint32_t size, uint32_t alignment) {
VarCell* cell = static_cast<VarCell*>(_zoneAllocator.alloc(sizeof(VarCell)));
if (cell == nullptr)
goto _NoMemory;
if (alignment == 0)
alignment = BaseContext_getDefaultAlignment(size);
if (alignment > 64)
alignment = 64;
ASMJIT_ASSERT(Utils::isPowerOf2(alignment));
size = Utils::alignTo<uint32_t>(size, alignment);
// Insert it sorted according to the alignment and size.
{
VarCell** pPrev = &_memStackCells;
VarCell* cur = *pPrev;
while (cur != nullptr) {
if ((cur->getAlignment() > alignment) ||
(cur->getAlignment() == alignment && cur->getSize() > size)) {
pPrev = &cur->_next;
cur = *pPrev;
continue;
}
break;
}
cell->_next = cur;
cell->_offset = 0;
cell->_size = size;
cell->_alignment = alignment;
*pPrev = cell;
_memStackCellsUsed++;
_memMaxAlign = Utils::iMax<uint32_t>(_memMaxAlign, alignment);
_memStackTotal += size;
}
return cell;
_NoMemory:
_compiler->setLastError(kErrorNoHeapMemory);
return nullptr;
}
Error Context::resolveCellOffsets() {
VarCell* varCell = _memVarCells;
VarCell* stackCell = _memStackCells;
uint32_t stackAlignment = 0;
if (stackCell != nullptr)
stackAlignment = stackCell->getAlignment();
uint32_t pos64 = 0;
uint32_t pos32 = pos64 + _mem64ByteVarsUsed * 64;
uint32_t pos16 = pos32 + _mem32ByteVarsUsed * 32;
uint32_t pos8 = pos16 + _mem16ByteVarsUsed * 16;
uint32_t pos4 = pos8 + _mem8ByteVarsUsed * 8 ;
uint32_t pos2 = pos4 + _mem4ByteVarsUsed * 4 ;
uint32_t pos1 = pos2 + _mem2ByteVarsUsed * 2 ;
uint32_t stackPos = pos1 + _mem1ByteVarsUsed;
uint32_t gapAlignment = stackAlignment;
uint32_t gapSize = 0;
// TODO: Not used!
if (gapAlignment)
Utils::alignDiff(stackPos, gapAlignment);
stackPos += gapSize;
uint32_t gapPos = stackPos;
uint32_t allTotal = stackPos;
// Vars - Allocated according to alignment/width.
while (varCell != nullptr) {
uint32_t size = varCell->getSize();
uint32_t offset = 0;
switch (size) {
case 1: offset = pos1 ; pos1 += 1 ; break;
case 2: offset = pos2 ; pos2 += 2 ; break;
case 4: offset = pos4 ; pos4 += 4 ; break;
case 8: offset = pos8 ; pos8 += 8 ; break;
case 16: offset = pos16; pos16 += 16; break;
case 32: offset = pos32; pos32 += 32; break;
case 64: offset = pos64; pos64 += 64; break;
default:
ASMJIT_NOT_REACHED();
}
varCell->setOffset(static_cast<int32_t>(offset));
varCell = varCell->_next;
}
// Stack - Allocated according to alignment/width.
while (stackCell != nullptr) {
uint32_t size = stackCell->getSize();
uint32_t alignment = stackCell->getAlignment();
uint32_t offset;
// Try to fill the gap between variables/stack first.
if (size <= gapSize && alignment <= gapAlignment) {
offset = gapPos;
gapSize -= size;
gapPos -= size;
if (alignment < gapAlignment)
gapAlignment = alignment;
}
else {
offset = stackPos;
stackPos += size;
allTotal += size;
}
stackCell->setOffset(offset);
stackCell = stackCell->_next;
}
_memAllTotal = allTotal;
return kErrorOk;
}
// ============================================================================
// [asmjit::Context - RemoveUnreachableCode]
// ============================================================================
Error Context::removeUnreachableCode() {
Compiler* compiler = getCompiler();
PodList<HLNode*>::Link* link = _unreachableList.getFirst();
HLNode* stop = getStop();
while (link != nullptr) {
HLNode* node = link->getValue();
if (node != nullptr && node->getPrev() != nullptr && node != stop) {
// Locate all unreachable nodes.
HLNode* first = node;
do {
if (node->isFetched())
break;
node = node->getNext();
} while (node != stop);
// Remove unreachable nodes that are neither informative nor directives.
if (node != first) {
HLNode* end = node;
node = first;
// NOTE: The strategy is as follows:
// 1. The algorithm removes everything until it finds a first label.
// 2. After the first label is found it removes only removable nodes.
bool removeEverything = true;
do {
HLNode* next = node->getNext();
bool remove = node->isRemovable();
if (!remove) {
if (node->isLabel())
removeEverything = false;
remove = removeEverything;
}
if (remove) {
ASMJIT_TSEC({
this->_traceNode(this, node, "[REMOVED UNREACHABLE] ");
});
compiler->removeNode(node);
}
node = next;
} while (node != end);
}
}
link = link->getNext();
}
return kErrorOk;
}
// ============================================================================
// [asmjit::Context - Liveness Analysis]
// ============================================================================
//! \internal
struct LivenessTarget {
//! Previous target.
LivenessTarget* prev;
//! Target node.
HLLabel* node;
//! Jumped from.
HLJump* from;
};
Error Context::livenessAnalysis() {
uint32_t bLen = static_cast<uint32_t>(
((_contextVd.getLength() + BitArray::kEntityBits - 1) / BitArray::kEntityBits));
// No variables.
if (bLen == 0)
return kErrorOk;
HLFunc* func = getFunc();
HLJump* from = nullptr;
LivenessTarget* ltCur = nullptr;
LivenessTarget* ltUnused = nullptr;
PodList<HLNode*>::Link* retPtr = _returningList.getFirst();
ASMJIT_ASSERT(retPtr != nullptr);
HLNode* node = retPtr->getValue();
size_t varMapToVaListOffset = _varMapToVaListOffset;
BitArray* bCur = newBits(bLen);
if (bCur == nullptr)
goto _NoMemory;
// Allocate bits for code visited first time.
_OnVisit:
for (;;) {
if (node->hasLiveness()) {
if (bCur->_addBitsDelSource(node->getLiveness(), bCur, bLen))
goto _OnPatch;
else
goto _OnDone;
}
BitArray* bTmp = copyBits(bCur, bLen);
if (bTmp == nullptr)
goto _NoMemory;
node->setLiveness(bTmp);
VarMap* map = node->getMap();
if (map != nullptr) {
uint32_t vaCount = map->getVaCount();
VarAttr* vaList = reinterpret_cast<VarAttr*>(((uint8_t*)map) + varMapToVaListOffset);
for (uint32_t i = 0; i < vaCount; i++) {
VarAttr* va = &vaList[i];
VarData* vd = va->getVd();
uint32_t flags = va->getFlags();
uint32_t localId = vd->getLocalId();
if ((flags & kVarAttrWAll) && !(flags & kVarAttrRAll)) {
// Write-Only.
bTmp->setBit(localId);
bCur->delBit(localId);
}
else {
// Read-Only or Read/Write.
bTmp->setBit(localId);
bCur->setBit(localId);
}
}
}
if (node->getType() == HLNode::kTypeLabel)
goto _OnTarget;
if (node == func)
goto _OnDone;
ASMJIT_ASSERT(node->getPrev());
node = node->getPrev();
}
// Patch already generated liveness bits.
_OnPatch:
for (;;) {
ASMJIT_ASSERT(node->hasLiveness());
BitArray* bNode = node->getLiveness();
if (!bNode->_addBitsDelSource(bCur, bLen))
goto _OnDone;
if (node->getType() == HLNode::kTypeLabel)
goto _OnTarget;
if (node == func)
goto _OnDone;
node = node->getPrev();
}
_OnTarget:
if (static_cast<HLLabel*>(node)->getNumRefs() != 0) {
// Push a new LivenessTarget onto the stack if needed.
if (ltCur == nullptr || ltCur->node != node) {
// Allocate a new LivenessTarget object (from pool or zone).
LivenessTarget* ltTmp = ltUnused;
if (ltTmp != nullptr) {
ltUnused = ltUnused->prev;
}
else {
ltTmp = _zoneAllocator.allocT<LivenessTarget>(
sizeof(LivenessTarget) - sizeof(BitArray) + bLen * sizeof(uintptr_t));
if (ltTmp == nullptr)
goto _NoMemory;
}
// Initialize and make current - ltTmp->from will be set later on.
ltTmp->prev = ltCur;
ltTmp->node = static_cast<HLLabel*>(node);
ltCur = ltTmp;
from = static_cast<HLLabel*>(node)->getFrom();
ASMJIT_ASSERT(from != nullptr);
}
else {
from = ltCur->from;
goto _OnJumpNext;
}
// Visit/Patch.
do {
ltCur->from = from;
bCur->copyBits(node->getLiveness(), bLen);
if (!from->hasLiveness()) {
node = from;
goto _OnVisit;
}
// Issue #25: Moved '_OnJumpNext' here since it's important to patch
// code again if there are more live variables than before.
_OnJumpNext:
if (bCur->delBits(from->getLiveness(), bLen)) {
node = from;
goto _OnPatch;
}
from = from->getJumpNext();
} while (from != nullptr);
// Pop the current LivenessTarget from the stack.
{
LivenessTarget* ltTmp = ltCur;
ltCur = ltCur->prev;
ltTmp->prev = ltUnused;
ltUnused = ltTmp;
}
}
bCur->copyBits(node->getLiveness(), bLen);
node = node->getPrev();
if (node->isJmp() || !node->isFetched())
goto _OnDone;
if (!node->hasLiveness())
goto _OnVisit;
if (bCur->delBits(node->getLiveness(), bLen))
goto _OnPatch;
_OnDone:
if (ltCur != nullptr) {
node = ltCur->node;
from = ltCur->from;
goto _OnJumpNext;
}
retPtr = retPtr->getNext();
if (retPtr != nullptr) {
node = retPtr->getValue();
goto _OnVisit;
}
return kErrorOk;
_NoMemory:
return setLastError(kErrorNoHeapMemory);
}
// ============================================================================
// [asmjit::Context - Annotate]
// ============================================================================
Error Context::formatInlineComment(StringBuilder& dst, HLNode* node) {
#if !defined(ASMJIT_DISABLE_LOGGER)
if (node->getComment())
dst.appendString(node->getComment());
if (node->hasLiveness()) {
if (dst.getLength() < _annotationLength)
dst.appendChars(' ', _annotationLength - dst.getLength());
uint32_t vdCount = static_cast<uint32_t>(_contextVd.getLength());
size_t offset = dst.getLength() + 1;
dst.appendChar('[');
dst.appendChars(' ', vdCount);
dst.appendChar(']');
BitArray* liveness = node->getLiveness();
VarMap* map = node->getMap();
uint32_t i;
for (i = 0; i < vdCount; i++) {
if (liveness->getBit(i))
dst.getData()[offset + i] = '.';
}
if (map != nullptr) {
uint32_t vaCount = map->getVaCount();
VarAttr* vaList = reinterpret_cast<VarAttr*>(((uint8_t*)map) + _varMapToVaListOffset);
for (i = 0; i < vaCount; i++) {
VarAttr* va = &vaList[i];
VarData* vd = va->getVd();
uint32_t flags = va->getFlags();
char c = 'u';
if ( (flags & kVarAttrRAll) && !(flags & kVarAttrWAll)) c = 'r';
if (!(flags & kVarAttrRAll) && (flags & kVarAttrWAll)) c = 'w';
if ( (flags & kVarAttrRAll) && (flags & kVarAttrWAll)) c = 'x';
// Uppercase if unused.
if ((flags & kVarAttrUnuse))
c -= 'a' - 'A';
ASMJIT_ASSERT(offset + vd->getLocalId() < dst.getLength());
dst._data[offset + vd->getLocalId()] = c;
}
}
}
#endif // !ASMJIT_DISABLE_LOGGER
return kErrorOk;
}
// ============================================================================
// [asmjit::Context - Cleanup]
// ============================================================================
void Context::cleanup() {
VarData** array = _contextVd.getData();
size_t length = _contextVd.getLength();
for (size_t i = 0; i < length; i++) {
VarData* vd = array[i];
vd->resetLocalId();
vd->resetRegIndex();
}
_contextVd.reset(false);
_extraBlock = nullptr;
}
// ============================================================================
// [asmjit::Context - CompileFunc]
// ============================================================================
Error Context::compile(HLFunc* func) {
HLNode* end = func->getEnd();
HLNode* stop = end->getNext();
_func = func;
_stop = stop;
_extraBlock = end;
ASMJIT_PROPAGATE_ERROR(fetch());
ASMJIT_PROPAGATE_ERROR(removeUnreachableCode());
ASMJIT_PROPAGATE_ERROR(livenessAnalysis());
Compiler* compiler = getCompiler();
#if !defined(ASMJIT_DISABLE_LOGGER)
if (compiler->getAssembler()->hasLogger())
ASMJIT_PROPAGATE_ERROR(annotate());
#endif // !ASMJIT_DISABLE_LOGGER
ASMJIT_PROPAGATE_ERROR(translate());
// We alter the compiler cursor, because it doesn't make sense to reference
// it after compilation - some nodes may disappear and it's forbidden to add
// new code after the compilation is done.
compiler->_setCursor(nullptr);
return kErrorOk;
}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
// [Guard]
#endif // !ASMJIT_DISABLE_COMPILER
+901
View File
@@ -0,0 +1,901 @@
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Guard]
#ifndef _ASMJIT_BASE_COMPILERCONTEXT_P_H
#define _ASMJIT_BASE_COMPILERCONTEXT_P_H
#include "../build.h"
#if !defined(ASMJIT_DISABLE_COMPILER)
// [Dependencies]
#include "../base/compiler.h"
#include "../base/podvector.h"
#include "../base/zone.h"
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
//! \addtogroup asmjit_base
//! \{
// ============================================================================
// [asmjit::VarAttrFlags]
// ============================================================================
//! \internal
//!
//! Variable attribute flags.
ASMJIT_ENUM(VarAttrFlags) {
//! Read from register.
kVarAttrRReg = 0x00000001,
//! Write to register.
kVarAttrWReg = 0x00000002,
//! Read/Write from/to register.
kVarAttrXReg = 0x00000003,
//! Read from memory.
kVarAttrRMem = 0x00000004,
//! Write to memory.
kVarAttrWMem = 0x00000008,
//! Read/Write from/to memory.
kVarAttrXMem = 0x0000000C,
//! Register allocator can decide if input will be in register or memory.
kVarAttrRDecide = 0x00000010,
//! Register allocator can decide if output will be in register or memory.
kVarAttrWDecide = 0x00000020,
//! Register allocator can decide if in/out will be in register or memory.
kVarAttrXDecide = 0x00000030,
//! Variable is converted to other type/class on the input.
kVarAttrRConv = 0x00000040,
//! Variable is converted from other type/class on the output.
kVarAttrWConv = 0x00000080,
//! Combination of `kVarAttrRConv` and `kVarAttrWConv`.
kVarAttrXConv = 0x000000C0,
//! Variable is a function call operand.
kVarAttrRCall = 0x00000100,
//! Variable is a function argument passed in register.
kVarAttrRFunc = 0x00000200,
//! Variable is a function return value passed in register.
kVarAttrWFunc = 0x00000400,
//! Variable should be spilled.
kVarAttrSpill = 0x00000800,
//! Variable should be unused at the end of the instruction/node.
kVarAttrUnuse = 0x00001000,
//! All in-flags.
kVarAttrRAll = kVarAttrRReg | kVarAttrRMem | kVarAttrRDecide | kVarAttrRCall | kVarAttrRFunc,
//! All out-flags.
kVarAttrWAll = kVarAttrWReg | kVarAttrWMem | kVarAttrWDecide | kVarAttrWFunc,
//! Variable is already allocated on the input.
kVarAttrAllocRDone = 0x00400000,
//! Variable is already allocated on the output.
kVarAttrAllocWDone = 0x00800000,
kVarAttrX86GpbLo = 0x10000000,
kVarAttrX86GpbHi = 0x20000000,
kVarAttrX86Fld4 = 0x40000000,
kVarAttrX86Fld8 = 0x80000000
};
// ============================================================================
// [asmjit::VarHint]
// ============================================================================
//! \internal
//!
//! Variable hint (used by `Compiler)`.
//!
//! \sa Compiler.
ASMJIT_ENUM(VarHint) {
//! Alloc variable.
kVarHintAlloc = 0,
//! Spill variable.
kVarHintSpill = 1,
//! Save variable if modified.
kVarHintSave = 2,
//! Save variable if modified and mark it as unused.
kVarHintSaveAndUnuse = 3,
//! Mark variable as unused.
kVarHintUnuse = 4
};
// ============================================================================
// [asmjit::kVarState]
// ============================================================================
// TODO: Rename `kVarState` or `VarState`.
//! \internal
//!
//! State of variable.
//!
//! NOTE: Variable states are used only during register allocation.
ASMJIT_ENUM(kVarState) {
//! Variable is currently not used.
kVarStateNone = 0,
//! Variable is currently allocated in register.
kVarStateReg = 1,
//! Variable is currently allocated in memory (or has been spilled).
kVarStateMem = 2
};
// ============================================================================
// [asmjit::VarCell]
// ============================================================================
struct VarCell {
ASMJIT_NO_COPY(VarCell)
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Get cell offset.
ASMJIT_INLINE int32_t getOffset() const { return _offset; }
//! Set cell offset.
ASMJIT_INLINE void setOffset(int32_t offset) { _offset = offset; }
//! Get cell size.
ASMJIT_INLINE uint32_t getSize() const { return _size; }
//! Set cell size.
ASMJIT_INLINE void setSize(uint32_t size) { _size = size; }
//! Get cell alignment.
ASMJIT_INLINE uint32_t getAlignment() const { return _alignment; }
//! Set cell alignment.
ASMJIT_INLINE void setAlignment(uint32_t alignment) { _alignment = alignment; }
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Next active cell.
VarCell* _next;
//! Offset, relative to base-offset.
int32_t _offset;
//! Size.
uint32_t _size;
//! Alignment.
uint32_t _alignment;
};
// ============================================================================
// [asmjit::VarData]
// ============================================================================
//! HL variable data (base).
struct VarData {
// --------------------------------------------------------------------------
// [Accessors - Base]
// --------------------------------------------------------------------------
//! Get variable name.
ASMJIT_INLINE const char* getName() const { return _name; }
//! Get variable id.
ASMJIT_INLINE uint32_t getId() const { return _id; }
//! Get variable type.
ASMJIT_INLINE uint32_t getType() const { return _type; }
//! Get variable class.
ASMJIT_INLINE uint32_t getClass() const { return _class; }
// --------------------------------------------------------------------------
// [Accessors - LocalId]
// --------------------------------------------------------------------------
//! Get whether the variable has a local id.
ASMJIT_INLINE bool hasLocalId() const { return _localId != kInvalidValue; }
//! Get a variable's local id.
ASMJIT_INLINE uint32_t getLocalId() const { return _localId; }
//! Set a variable's local id.
ASMJIT_INLINE void setLocalId(uint32_t localId) { _localId = localId; }
//! Reset a variable's local id.
ASMJIT_INLINE void resetLocalId() { _localId = kInvalidValue; }
// --------------------------------------------------------------------------
// [Accessors - Priority]
// --------------------------------------------------------------------------
//! Get variable priority, used by compiler to decide which variable to spill.
ASMJIT_INLINE uint32_t getPriority() const { return _priority; }
//! Set variable priority.
ASMJIT_INLINE void setPriority(uint32_t priority) {
ASMJIT_ASSERT(priority <= 0xFF);
_priority = static_cast<uint8_t>(priority);
}
// --------------------------------------------------------------------------
// [Accessors - State]
// --------------------------------------------------------------------------
//! Get variable state, only used by `Context`.
ASMJIT_INLINE uint32_t getState() const { return _state; }
//! Set variable state, only used by `Context`.
ASMJIT_INLINE void setState(uint32_t state) {
ASMJIT_ASSERT(state <= 0xFF);
_state = static_cast<uint8_t>(state);
}
// --------------------------------------------------------------------------
// [Accessors - RegIndex]
// --------------------------------------------------------------------------
//! Get register index.
ASMJIT_INLINE uint32_t getRegIndex() const { return _regIndex; }
//! Set register index.
ASMJIT_INLINE void setRegIndex(uint32_t regIndex) {
ASMJIT_ASSERT(regIndex <= kInvalidReg);
_regIndex = static_cast<uint8_t>(regIndex);
}
//! Reset register index.
ASMJIT_INLINE void resetRegIndex() {
_regIndex = static_cast<uint8_t>(kInvalidReg);
}
// --------------------------------------------------------------------------
// [Accessors - HomeIndex/Mask]
// --------------------------------------------------------------------------
//! Get home registers mask.
ASMJIT_INLINE uint32_t getHomeMask() const { return _homeMask; }
//! Add a home register index to the home registers mask.
ASMJIT_INLINE void addHomeIndex(uint32_t regIndex) { _homeMask |= Utils::mask(regIndex); }
// --------------------------------------------------------------------------
// [Accessors - Flags]
// --------------------------------------------------------------------------
//! Get variable flags.
ASMJIT_INLINE uint32_t getFlags() const { return _flags; }
//! Get whether the VarData is only memory allocated on the stack.
ASMJIT_INLINE bool isStack() const { return static_cast<bool>(_isStack); }
//! Get whether the variable is a function argument passed through memory.
ASMJIT_INLINE bool isMemArg() const { return static_cast<bool>(_isMemArg); }
//! Get variable content can be calculated by a simple instruction.
ASMJIT_INLINE bool isCalculated() const { return static_cast<bool>(_isCalculated); }
//! Get whether to save variable when it's unused (spill).
ASMJIT_INLINE bool saveOnUnuse() const { return static_cast<bool>(_saveOnUnuse); }
//! Get whether the variable was changed.
ASMJIT_INLINE bool isModified() const { return static_cast<bool>(_modified); }
//! Set whether the variable was changed.
ASMJIT_INLINE void setModified(bool modified) { _modified = modified; }
//! Get variable alignment.
ASMJIT_INLINE uint32_t getAlignment() const { return _alignment; }
//! Get variable size.
ASMJIT_INLINE uint32_t getSize() const { return _size; }
//! Get home memory offset.
ASMJIT_INLINE int32_t getMemOffset() const { return _memOffset; }
//! Set home memory offset.
ASMJIT_INLINE void setMemOffset(int32_t offset) { _memOffset = offset; }
//! Get home memory cell.
ASMJIT_INLINE VarCell* getMemCell() const { return _memCell; }
//! Set home memory cell.
ASMJIT_INLINE void setMemCell(VarCell* cell) { _memCell = cell; }
// --------------------------------------------------------------------------
// [Accessors - Temporary Usage]
// --------------------------------------------------------------------------
//! Get temporary VarAttr.
ASMJIT_INLINE VarAttr* getVa() const { return _va; }
//! Set temporary VarAttr.
ASMJIT_INLINE void setVa(VarAttr* va) { _va = va; }
//! Reset temporary VarAttr.
ASMJIT_INLINE void resetVa() { _va = nullptr; }
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Variable name.
const char* _name;
//! Variable id.
uint32_t _id;
//! Variable's local id (initially `kInvalidValue`).
uint32_t _localId;
//! Variable type.
uint8_t _type;
//! Variable class.
uint8_t _class;
//! Variable flags.
uint8_t _flags;
//! Variable priority.
uint8_t _priority;
//! Variable state (connected with actual `VarState)`.
uint8_t _state;
//! Actual register index (only used by `Context)`, during translate.
uint8_t _regIndex;
//! Whether the variable is only used as memory allocated on the stack.
uint8_t _isStack : 1;
//! Whether the variable is a function argument passed through memory.
uint8_t _isMemArg : 1;
//! Whether variable content can be calculated by a simple instruction.
//!
//! This is used mainly by MMX and SSE2 code. This flag indicates that
//! register allocator should never reserve memory for this variable, because
//! the content can be generated by a single instruction (for example PXOR).
uint8_t _isCalculated : 1;
//! Save on unuse (at end of the variable scope).
uint8_t _saveOnUnuse : 1;
//! Whether variable was changed (connected with actual `VarState)`.
uint8_t _modified : 1;
//! \internal
uint8_t _reserved0 : 3;
//! Variable natural alignment.
uint8_t _alignment;
//! Variable size.
uint32_t _size;
//! Mask of all registers variable has been allocated to.
uint32_t _homeMask;
//! Home memory offset.
int32_t _memOffset;
//! Home memory cell, used by `Context` (initially nullptr).
VarCell* _memCell;
//! Register read access statistics.
uint32_t rReadCount;
//! Register write access statistics.
uint32_t rWriteCount;
//! Memory read statistics.
uint32_t mReadCount;
//! Memory write statistics.
uint32_t mWriteCount;
// --------------------------------------------------------------------------
// [Members - Temporary Usage]
// --------------------------------------------------------------------------
// These variables are only used during register allocation. They are
// initialized by init() phase and reset by cleanup() phase.
union {
//! Temporary link to VarAttr* used by the `Context` used in
//! various phases, but always set back to nullptr when finished.
//!
//! This temporary data is designed to be used by algorithms that need to
//! store some data into variables themselves during compilation. But it's
//! expected that after variable is compiled & translated the data is set
//! back to zero/null. Initial value is nullptr.
VarAttr* _va;
//! \internal
//!
//! Same as `_va` just provided as `uintptr_t`.
uintptr_t _vaUInt;
};
};
// ============================================================================
// [asmjit::VarAttr]
// ============================================================================
struct VarAttr {
// --------------------------------------------------------------------------
// [Setup]
// --------------------------------------------------------------------------
ASMJIT_INLINE void setup(VarData* vd, uint32_t flags = 0, uint32_t inRegs = 0, uint32_t allocableRegs = 0) {
_vd = vd;
_flags = flags;
_varCount = 0;
_inRegIndex = kInvalidReg;
_outRegIndex = kInvalidReg;
_reserved = 0;
_inRegs = inRegs;
_allocableRegs = allocableRegs;
}
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Get VarData.
ASMJIT_INLINE VarData* getVd() const { return _vd; }
//! Set VarData.
ASMJIT_INLINE void setVd(VarData* vd) { _vd = vd; }
//! Get flags.
ASMJIT_INLINE uint32_t getFlags() const { return _flags; }
//! Set flags.
ASMJIT_INLINE void setFlags(uint32_t flags) { _flags = flags; }
//! Get whether `flag` is on.
ASMJIT_INLINE bool hasFlag(uint32_t flag) { return (_flags & flag) != 0; }
//! Add `flags`.
ASMJIT_INLINE void orFlags(uint32_t flags) { _flags |= flags; }
//! Mask `flags`.
ASMJIT_INLINE void andFlags(uint32_t flags) { _flags &= flags; }
//! Clear `flags`.
ASMJIT_INLINE void andNotFlags(uint32_t flags) { _flags &= ~flags; }
//! Get how many times the variable is used by the instruction/node.
ASMJIT_INLINE uint32_t getVarCount() const { return _varCount; }
//! Set how many times the variable is used by the instruction/node.
ASMJIT_INLINE void setVarCount(uint32_t count) { _varCount = static_cast<uint8_t>(count); }
//! Add how many times the variable is used by the instruction/node.
ASMJIT_INLINE void addVarCount(uint32_t count = 1) { _varCount += static_cast<uint8_t>(count); }
//! Get whether the variable has to be allocated in a specific input register.
ASMJIT_INLINE uint32_t hasInRegIndex() const { return _inRegIndex != kInvalidReg; }
//! Get the input register index or `kInvalidReg`.
ASMJIT_INLINE uint32_t getInRegIndex() const { return _inRegIndex; }
//! Set the input register index.
ASMJIT_INLINE void setInRegIndex(uint32_t index) { _inRegIndex = static_cast<uint8_t>(index); }
//! Reset the input register index.
ASMJIT_INLINE void resetInRegIndex() { _inRegIndex = kInvalidReg; }
//! Get whether the variable has to be allocated in a specific output register.
ASMJIT_INLINE uint32_t hasOutRegIndex() const { return _outRegIndex != kInvalidReg; }
//! Get the output register index or `kInvalidReg`.
ASMJIT_INLINE uint32_t getOutRegIndex() const { return _outRegIndex; }
//! Set the output register index.
ASMJIT_INLINE void setOutRegIndex(uint32_t index) { _outRegIndex = static_cast<uint8_t>(index); }
//! Reset the output register index.
ASMJIT_INLINE void resetOutRegIndex() { _outRegIndex = kInvalidReg; }
//! Get whether the mandatory input registers are in used.
ASMJIT_INLINE bool hasInRegs() const { return _inRegs != 0; }
//! Get mandatory input registers (mask).
ASMJIT_INLINE uint32_t getInRegs() const { return _inRegs; }
//! Set mandatory input registers (mask).
ASMJIT_INLINE void setInRegs(uint32_t mask) { _inRegs = mask; }
//! Add mandatory input registers (mask).
ASMJIT_INLINE void addInRegs(uint32_t mask) { _inRegs |= mask; }
//! And mandatory input registers (mask).
ASMJIT_INLINE void andInRegs(uint32_t mask) { _inRegs &= mask; }
//! Clear mandatory input registers (mask).
ASMJIT_INLINE void delInRegs(uint32_t mask) { _inRegs &= ~mask; }
//! Get allocable input registers (mask).
ASMJIT_INLINE uint32_t getAllocableRegs() const { return _allocableRegs; }
//! Set allocable input registers (mask).
ASMJIT_INLINE void setAllocableRegs(uint32_t mask) { _allocableRegs = mask; }
//! Add allocable input registers (mask).
ASMJIT_INLINE void addAllocableRegs(uint32_t mask) { _allocableRegs |= mask; }
//! And allocable input registers (mask).
ASMJIT_INLINE void andAllocableRegs(uint32_t mask) { _allocableRegs &= mask; }
//! Clear allocable input registers (mask).
ASMJIT_INLINE void delAllocableRegs(uint32_t mask) { _allocableRegs &= ~mask; }
// --------------------------------------------------------------------------
// [Operator Overload]
// --------------------------------------------------------------------------
ASMJIT_INLINE VarAttr& operator=(const VarAttr& other) {
::memcpy(this, &other, sizeof(VarAttr));
return *this;
}
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
VarData* _vd;
//! Flags.
uint32_t _flags;
union {
struct {
//! How many times the variable is used by the instruction/node.
uint8_t _varCount;
//! Input register index or `kInvalidReg` if it's not given.
//!
//! Even if the input register index is not given (i.e. it may by any
//! register), register allocator should assign an index that will be
//! used to persist a variable into this specific index. It's helpful
//! in situations where one variable has to be allocated in multiple
//! registers to determine the register which will be persistent.
uint8_t _inRegIndex;
//! Output register index or `kInvalidReg` if it's not given.
//!
//! Typically `kInvalidReg` if variable is only used on input.
uint8_t _outRegIndex;
//! \internal
uint8_t _reserved;
};
//! \internal
//!
//! Packed data #0.
uint32_t _packed;
};
//! Mandatory input registers.
//!
//! Mandatory input registers are required by the instruction even if
//! there are duplicates. This schema allows us to allocate one variable
//! in one or more register when needed. Required mostly by instructions
//! that have implicit register operands (imul, cpuid, ...) and function
//! call.
uint32_t _inRegs;
//! Allocable input registers.
//!
//! Optional input registers is a mask of all allocable registers for a given
//! variable where we have to pick one of them. This mask is usually not used
//! when _inRegs is set. If both masks are used then the register
//! allocator tries first to find an intersection between these and allocates
//! an extra slot if not found.
uint32_t _allocableRegs;
};
// ============================================================================
// [asmjit::VarMap]
// ============================================================================
//! Variables' map related to a single node (instruction / other node).
struct VarMap {
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Get count of variables (all).
ASMJIT_INLINE uint32_t getVaCount() const {
return _vaCount;
}
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Variables count.
uint32_t _vaCount;
};
// ============================================================================
// [asmjit::VarState]
// ============================================================================
//! Variables' state.
struct VarState {};
// ============================================================================
// [asmjit::Context]
// ============================================================================
//! \internal
//!
//! Code generation context is the logic behind `Compiler`. The context is
//! used to compile the code stored in `Compiler`.
struct Context {
ASMJIT_NO_COPY(Context)
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
Context(Compiler* compiler);
virtual ~Context();
// --------------------------------------------------------------------------
// [Reset]
// --------------------------------------------------------------------------
//! Reset the whole context.
virtual void reset(bool releaseMemory = false);
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Get compiler.
ASMJIT_INLINE Compiler* getCompiler() const { return _compiler; }
//! Get function.
ASMJIT_INLINE HLFunc* getFunc() const { return _func; }
//! Get stop node.
ASMJIT_INLINE HLNode* getStop() const { return _stop; }
//! Get start of the current scope.
ASMJIT_INLINE HLNode* getStart() const { return _start; }
//! Get end of the current scope.
ASMJIT_INLINE HLNode* getEnd() const { return _end; }
//! Get extra block.
ASMJIT_INLINE HLNode* getExtraBlock() const { return _extraBlock; }
//! Set extra block.
ASMJIT_INLINE void setExtraBlock(HLNode* node) { _extraBlock = node; }
// --------------------------------------------------------------------------
// [Error]
// --------------------------------------------------------------------------
//! Get the last error code.
ASMJIT_INLINE Error getLastError() const {
return getCompiler()->getLastError();
}
//! Set the last error code and propagate it through the error handler.
ASMJIT_INLINE Error setLastError(Error error, const char* message = nullptr) {
return getCompiler()->setLastError(error, message);
}
// --------------------------------------------------------------------------
// [State]
// --------------------------------------------------------------------------
//! Get current state.
ASMJIT_INLINE VarState* getState() const { return _state; }
//! Load current state from `target` state.
virtual void loadState(VarState* src) = 0;
//! Save current state, returning new `VarState` instance.
virtual VarState* saveState() = 0;
//! Change the current state to `target` state.
virtual void switchState(VarState* src) = 0;
//! Change the current state to the intersection of two states `a` and `b`.
virtual void intersectStates(VarState* a, VarState* b) = 0;
// --------------------------------------------------------------------------
// [Context]
// --------------------------------------------------------------------------
ASMJIT_INLINE Error _registerContextVar(VarData* vd) {
if (vd->hasLocalId())
return kErrorOk;
uint32_t cid = static_cast<uint32_t>(_contextVd.getLength());
ASMJIT_PROPAGATE_ERROR(_contextVd.append(vd));
vd->setLocalId(cid);
return kErrorOk;
}
// --------------------------------------------------------------------------
// [Mem]
// --------------------------------------------------------------------------
VarCell* _newVarCell(VarData* vd);
VarCell* _newStackCell(uint32_t size, uint32_t alignment);
ASMJIT_INLINE VarCell* getVarCell(VarData* vd) {
VarCell* cell = vd->getMemCell();
return cell ? cell : _newVarCell(vd);
}
virtual Error resolveCellOffsets();
// --------------------------------------------------------------------------
// [Bits]
// --------------------------------------------------------------------------
ASMJIT_INLINE BitArray* newBits(uint32_t len) {
return static_cast<BitArray*>(
_zoneAllocator.allocZeroed(static_cast<size_t>(len) * BitArray::kEntitySize));
}
ASMJIT_INLINE BitArray* copyBits(const BitArray* src, uint32_t len) {
return static_cast<BitArray*>(
_zoneAllocator.dup(src, static_cast<size_t>(len) * BitArray::kEntitySize));
}
// --------------------------------------------------------------------------
// [Fetch]
// --------------------------------------------------------------------------
//! Fetch.
//!
//! Fetch iterates over all nodes and gathers information about all variables
//! used. The process generates information required by register allocator,
//! variable liveness analysis and translator.
virtual Error fetch() = 0;
// --------------------------------------------------------------------------
// [Unreachable Code]
// --------------------------------------------------------------------------
//! Add unreachable-flow data to the unreachable flow list.
ASMJIT_INLINE Error addUnreachableNode(HLNode* node) {
PodList<HLNode*>::Link* link = _zoneAllocator.allocT<PodList<HLNode*>::Link>();
if (link == nullptr)
return setLastError(kErrorNoHeapMemory);
link->setValue(node);
_unreachableList.append(link);
return kErrorOk;
}
//! Remove unreachable code.
virtual Error removeUnreachableCode();
// --------------------------------------------------------------------------
// [Code-Flow]
// --------------------------------------------------------------------------
//! Add returning node (i.e. node that returns and where liveness analysis
//! should start).
ASMJIT_INLINE Error addReturningNode(HLNode* node) {
PodList<HLNode*>::Link* link = _zoneAllocator.allocT<PodList<HLNode*>::Link>();
if (link == nullptr)
return setLastError(kErrorNoHeapMemory);
link->setValue(node);
_returningList.append(link);
return kErrorOk;
}
//! Add jump-flow data to the jcc flow list.
ASMJIT_INLINE Error addJccNode(HLNode* node) {
PodList<HLNode*>::Link* link = _zoneAllocator.allocT<PodList<HLNode*>::Link>();
if (link == nullptr)
return setLastError(kErrorNoHeapMemory);
link->setValue(node);
_jccList.append(link);
return kErrorOk;
}
// --------------------------------------------------------------------------
// [Analyze]
// --------------------------------------------------------------------------
//! Perform variable liveness analysis.
//!
//! Analysis phase iterates over nodes in reverse order and generates a bit
//! array describing variables that are alive at every node in the function.
//! When the analysis start all variables are assumed dead. When a read or
//! read/write operations of a variable is detected the variable becomes
//! alive; when only write operation is detected the variable becomes dead.
//!
//! When a label is found all jumps to that label are followed and analysis
//! repeats until all variables are resolved.
virtual Error livenessAnalysis();
// --------------------------------------------------------------------------
// [Annotate]
// --------------------------------------------------------------------------
virtual Error annotate() = 0;
virtual Error formatInlineComment(StringBuilder& dst, HLNode* node);
// --------------------------------------------------------------------------
// [Translate]
// --------------------------------------------------------------------------
//! Translate code by allocating registers and handling state changes.
virtual Error translate() = 0;
// --------------------------------------------------------------------------
// [Cleanup]
// --------------------------------------------------------------------------
virtual void cleanup();
// --------------------------------------------------------------------------
// [Compile]
// --------------------------------------------------------------------------
virtual Error compile(HLFunc* func);
// --------------------------------------------------------------------------
// [Serialize]
// --------------------------------------------------------------------------
virtual Error serialize(Assembler* assembler, HLNode* start, HLNode* stop) = 0;
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Compiler.
Compiler* _compiler;
//! Function.
HLFunc* _func;
//! Zone allocator.
Zone _zoneAllocator;
//! \internal
typedef void (ASMJIT_CDECL* TraceNodeFunc)(Context* self, HLNode* node_, const char* prefix);
//! \internal
//!
//! Only non-NULL when ASMJIT_TRACE is enabled.
TraceNodeFunc _traceNode;
//! \internal
//!
//! Offset (how many bytes to add) to `VarMap` to get `VarAttr` array. Used
//! by liveness analysis shared across all backends. This is needed because
//! `VarMap` is a base class for a specialized version that liveness analysis
//! doesn't use, it just needs `VarAttr` array.
uint32_t _varMapToVaListOffset;
//! Start of the current active scope.
HLNode* _start;
//! End of the current active scope.
HLNode* _end;
//! Node that is used to insert extra code after the function body.
HLNode* _extraBlock;
//! Stop node.
HLNode* _stop;
//! Unreachable nodes.
PodList<HLNode*> _unreachableList;
//! Returning nodes.
PodList<HLNode*> _returningList;
//! Jump nodes.
PodList<HLNode*> _jccList;
//! All variables used by the current function.
PodVector<VarData*> _contextVd;
//! Memory used to spill variables.
VarCell* _memVarCells;
//! Memory used to alloc memory on the stack.
VarCell* _memStackCells;
//! Count of 1-byte cells.
uint32_t _mem1ByteVarsUsed;
//! Count of 2-byte cells.
uint32_t _mem2ByteVarsUsed;
//! Count of 4-byte cells.
uint32_t _mem4ByteVarsUsed;
//! Count of 8-byte cells.
uint32_t _mem8ByteVarsUsed;
//! Count of 16-byte cells.
uint32_t _mem16ByteVarsUsed;
//! Count of 32-byte cells.
uint32_t _mem32ByteVarsUsed;
//! Count of 64-byte cells.
uint32_t _mem64ByteVarsUsed;
//! Count of stack memory cells.
uint32_t _memStackCellsUsed;
//! Maximum memory alignment used by the function.
uint32_t _memMaxAlign;
//! Count of bytes used by variables.
uint32_t _memVarTotal;
//! Count of bytes used by stack.
uint32_t _memStackTotal;
//! Count of bytes used by variables and stack after alignment.
uint32_t _memAllTotal;
//! Default lenght of annotated instruction.
uint32_t _annotationLength;
//! Current state (used by register allocator).
VarState* _state;
};
//! \}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
// [Guard]
#endif // !ASMJIT_DISABLE_COMPILER
#endif // _ASMJIT_BASE_COMPILERCONTEXT_P_H
+679
View File
@@ -0,0 +1,679 @@
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Guard]
#ifndef _ASMJIT_BASE_COMPILERFUNC_H
#define _ASMJIT_BASE_COMPILERFUNC_H
#include "../build.h"
#if !defined(ASMJIT_DISABLE_COMPILER)
// [Dependencies]
#include "../base/operand.h"
#include "../base/utils.h"
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
//! \addtogroup asmjit_base
//! \{
// ============================================================================
// [asmjit::FuncHint]
// ============================================================================
//! Function hints.
//!
//! For a platform specific calling conventions, see:
//! - `X86FuncHint` - X86/X64 function hints.
ASMJIT_ENUM(FuncHint) {
//! Generate a naked function by omitting its prolog and epilog (default true).
//!
//! Naked functions should always result in less code required for function's
//! prolog and epilog. In addition, on X86/64 naked functions save one register
//! (ebp or rbp), which can be used by the function instead.
kFuncHintNaked = 0,
//! Generate a compact function prolog/epilog if possible (default true).
//!
//! X86/X64 Specific
//! ----------------
//!
//! Use shorter, but possible slower prolog/epilog sequence to save/restore
//! registers. At the moment this only enables emitting `leave` in function's
//! epilog to make the code shorter, however, the counterpart `enter` is not
//! used in function's prolog for performance reasons.
kFuncHintCompact = 1,
//! Emit `emms` instruction in the function's epilog.
kFuncHintX86Emms = 17,
//! Emit `sfence` instruction in the function's epilog.
kFuncHintX86SFence = 18,
//! Emit `lfence` instruction in the function's epilog.
kFuncHintX86LFence = 19
};
// ============================================================================
// [asmjit::FuncFlags]
// ============================================================================
//! Function flags.
ASMJIT_ENUM(FuncFlags) {
//! Whether the function is using naked (minimal) prolog / epilog.
kFuncFlagIsNaked = 0x00000001,
//! Whether an another function is called from this function.
kFuncFlagIsCaller = 0x00000002,
//! Whether the stack is not aligned to the required stack alignment,
//! thus it has to be aligned manually.
kFuncFlagIsStackMisaligned = 0x00000004,
//! Whether the stack pointer is adjusted by the stack size needed
//! to save registers and function variables.
//!
//! X86/X64 Specific
//! ----------------
//!
//! Stack pointer (ESP/RSP) is adjusted by 'sub' instruction in prolog and by
//! 'add' instruction in epilog (only if function is not naked). If function
//! needs to perform manual stack alignment more instructions are used to
//! adjust the stack (like "and zsp, -Alignment").
kFuncFlagIsStackAdjusted = 0x00000008,
//! Whether the function is finished using `Compiler::endFunc()`.
kFuncFlagIsFinished = 0x80000000,
//! Whether to emit `leave` instead of two instructions in case that the
//! function saves and restores the frame pointer.
kFuncFlagX86Leave = 0x00010000,
//! Whether it's required to move arguments to a new stack location,
//! because of manual aligning.
kFuncFlagX86MoveArgs = 0x00040000,
//! Whether to emit `emms` instruction in epilog (auto-detected).
kFuncFlagX86Emms = 0x01000000,
//! Whether to emit `sfence` instruction in epilog (auto-detected).
//!
//! `kFuncFlagX86SFence` with `kFuncFlagX86LFence` results in emitting `mfence`.
kFuncFlagX86SFence = 0x02000000,
//! Whether to emit `lfence` instruction in epilog (auto-detected).
//!
//! `kFuncFlagX86SFence` with `kFuncFlagX86LFence` results in emitting `mfence`.
kFuncFlagX86LFence = 0x04000000
};
// ============================================================================
// [asmjit::FuncDir]
// ============================================================================
//! Function arguments direction.
ASMJIT_ENUM(FuncDir) {
//! Arguments are passed left to right.
//!
//! This arguments direction is unusual in C, however it's used in Pascal.
kFuncDirLTR = 0,
//! Arguments are passed right ro left
//!
//! This is the default argument direction in C.
kFuncDirRTL = 1
};
// ============================================================================
// [asmjit::FuncMisc]
// ============================================================================
enum {
//! Function doesn't have variable number of arguments (`...`) (default).
kFuncNoVarArgs = 0xFF,
//! Invalid stack offset in function or function parameter.
kFuncStackInvalid = -1
};
// ============================================================================
// [asmjit::FuncArgIndex]
// ============================================================================
//! Function argument index (lo/hi).
ASMJIT_ENUM(FuncArgIndex) {
//! Maxumum number of function arguments supported by AsmJit.
kFuncArgCount = 16,
//! Extended maximum number of arguments (used internally).
kFuncArgCountLoHi = kFuncArgCount * 2,
//! Index to the LO part of function argument (default).
//!
//! This value is typically omitted and added only if there is HI argument
//! accessed.
kFuncArgLo = 0,
//! Index to the HI part of function argument.
//!
//! HI part of function argument depends on target architecture. On x86 it's
//! typically used to transfer 64-bit integers (they form a pair of 32-bit
//! integers).
kFuncArgHi = kFuncArgCount
};
// ============================================================================
// [asmjit::FuncRet]
// ============================================================================
//! Function return value (lo/hi) specification.
ASMJIT_ENUM(FuncRet) {
//! Index to the LO part of function return value.
kFuncRetLo = 0,
//! Index to the HI part of function return value.
kFuncRetHi = 1
};
// ============================================================================
// [asmjit::TypeId]
// ============================================================================
//! Function builder's `void` type.
struct Void {};
//! Function builder's `int8_t` type.
struct Int8Type {};
//! Function builder's `uint8_t` type.
struct UInt8Type {};
//! Function builder's `int16_t` type.
struct Int16Type {};
//! Function builder's `uint16_t` type.
struct UInt16Type {};
//! Function builder's `int32_t` type.
struct Int32Type {};
//! Function builder's `uint32_t` type.
struct UInt32Type {};
//! Function builder's `int64_t` type.
struct Int64Type {};
//! Function builder's `uint64_t` type.
struct UInt64Type {};
//! Function builder's `intptr_t` type.
struct IntPtrType {};
//! Function builder's `uintptr_t` type.
struct UIntPtrType {};
//! Function builder's `float` type.
struct FloatType {};
//! Function builder's `double` type.
struct DoubleType {};
#if !defined(ASMJIT_DOCGEN)
template<typename T>
struct TypeId {
// Let it fail here if `T` was not specialized.
};
template<typename T>
struct TypeId<T*> {
enum { kId = kVarTypeIntPtr };
};
template<typename T>
struct TypeIdOfInt {
enum { kId = (sizeof(T) == 1) ? (int)(IntTraits<T>::kIsSigned ? kVarTypeInt8 : kVarTypeUInt8 ) :
(sizeof(T) == 2) ? (int)(IntTraits<T>::kIsSigned ? kVarTypeInt16 : kVarTypeUInt16) :
(sizeof(T) == 4) ? (int)(IntTraits<T>::kIsSigned ? kVarTypeInt32 : kVarTypeUInt32) :
(sizeof(T) == 8) ? (int)(IntTraits<T>::kIsSigned ? kVarTypeInt64 : kVarTypeUInt64) : (int)kInvalidVar
};
};
#define ASMJIT_TYPE_ID(T, ID) \
template<> struct TypeId<T> { enum { kId = ID }; }
ASMJIT_TYPE_ID(void , kInvalidVar);
ASMJIT_TYPE_ID(signed char , TypeIdOfInt<signed char>::kId);
ASMJIT_TYPE_ID(unsigned char , TypeIdOfInt<unsigned char>::kId);
ASMJIT_TYPE_ID(short , TypeIdOfInt<short>::kId);
ASMJIT_TYPE_ID(unsigned short , TypeIdOfInt<unsigned short>::kId);
ASMJIT_TYPE_ID(int , TypeIdOfInt<int>::kId);
ASMJIT_TYPE_ID(unsigned int , TypeIdOfInt<unsigned int>::kId);
ASMJIT_TYPE_ID(long , TypeIdOfInt<long>::kId);
ASMJIT_TYPE_ID(unsigned long , TypeIdOfInt<unsigned long>::kId);
ASMJIT_TYPE_ID(float , kVarTypeFp32);
ASMJIT_TYPE_ID(double , kVarTypeFp64);
#if ASMJIT_CC_HAS_NATIVE_CHAR
ASMJIT_TYPE_ID(char , TypeIdOfInt<char>::kId);
#endif
#if ASMJIT_CC_HAS_NATIVE_WCHAR_T
ASMJIT_TYPE_ID(wchar_t , TypeIdOfInt<wchar_t>::kId);
#endif
#if ASMJIT_CC_HAS_NATIVE_CHAR16_T
ASMJIT_TYPE_ID(char16_t , TypeIdOfInt<char16_t>::kId);
#endif
#if ASMJIT_CC_HAS_NATIVE_CHAR32_T
ASMJIT_TYPE_ID(char32_t , TypeIdOfInt<char32_t>::kId);
#endif
#if ASMJIT_CC_MSC && !ASMJIT_CC_MSC_GE(16, 0, 0)
ASMJIT_TYPE_ID(__int64 , TypeIdOfInt<__int64>::kId);
ASMJIT_TYPE_ID(unsigned __int64 , TypeIdOfInt<unsigned __int64>::kId);
#else
ASMJIT_TYPE_ID(long long , TypeIdOfInt<long long>::kId);
ASMJIT_TYPE_ID(unsigned long long, TypeIdOfInt<unsigned long long>::kId);
#endif
ASMJIT_TYPE_ID(Void , kInvalidVar);
ASMJIT_TYPE_ID(Int8Type , kVarTypeInt8);
ASMJIT_TYPE_ID(UInt8Type , kVarTypeUInt8);
ASMJIT_TYPE_ID(Int16Type , kVarTypeInt16);
ASMJIT_TYPE_ID(UInt16Type , kVarTypeUInt16);
ASMJIT_TYPE_ID(Int32Type , kVarTypeInt32);
ASMJIT_TYPE_ID(UInt32Type , kVarTypeUInt32);
ASMJIT_TYPE_ID(Int64Type , kVarTypeInt64);
ASMJIT_TYPE_ID(UInt64Type , kVarTypeUInt64);
ASMJIT_TYPE_ID(IntPtrType , kVarTypeIntPtr);
ASMJIT_TYPE_ID(UIntPtrType , kVarTypeUIntPtr);
ASMJIT_TYPE_ID(FloatType , kVarTypeFp32);
ASMJIT_TYPE_ID(DoubleType , kVarTypeFp64);
#endif // !ASMJIT_DOCGEN
// ============================================================================
// [asmjit::FuncInOut]
// ============================================================================
//! Function in/out - argument or return value translated from `FuncPrototype`.
struct FuncInOut {
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
ASMJIT_INLINE uint32_t getVarType() const noexcept { return _varType; }
ASMJIT_INLINE bool hasRegIndex() const noexcept { return _regIndex != kInvalidReg; }
ASMJIT_INLINE uint32_t getRegIndex() const noexcept { return _regIndex; }
ASMJIT_INLINE bool hasStackOffset() const noexcept { return _stackOffset != kFuncStackInvalid; }
ASMJIT_INLINE int32_t getStackOffset() const noexcept { return static_cast<int32_t>(_stackOffset); }
//! Get whether the argument / return value is assigned.
ASMJIT_INLINE bool isSet() const noexcept {
return (_regIndex != kInvalidReg) | (_stackOffset != kFuncStackInvalid);
}
// --------------------------------------------------------------------------
// [Reset]
// --------------------------------------------------------------------------
//! Reset the function argument to "unassigned state".
ASMJIT_INLINE void reset() noexcept { _packed = 0xFFFFFFFFU; }
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
union {
struct {
//! Variable type, see \ref VarType.
uint8_t _varType;
//! Register index if argument / return value is a register.
uint8_t _regIndex;
//! Stack offset if argument / return value is on the stack.
int16_t _stackOffset;
};
//! All members packed into single 32-bit integer.
uint32_t _packed;
};
};
// ============================================================================
// [asmjit::FuncPrototype]
// ============================================================================
//! Function prototype.
//!
//! Function prototype contains information about function return type, count
//! of arguments and their types. Function prototype is a low level structure
//! which doesn't contain platform specific or calling convention specific
//! information. Function prototype is used to create a `FuncDecl`.
struct FuncPrototype {
// --------------------------------------------------------------------------
// [Setup]
// --------------------------------------------------------------------------
//! Setup the prototype.
ASMJIT_INLINE void setup(
uint32_t callConv,
uint32_t ret,
const uint32_t* args, uint32_t numArgs) noexcept {
ASMJIT_ASSERT(callConv <= 0xFF);
ASMJIT_ASSERT(numArgs <= 0xFF);
_callConv = static_cast<uint8_t>(callConv);
_varArgs = kFuncNoVarArgs;
_numArgs = static_cast<uint8_t>(numArgs);
_reserved = 0;
_ret = ret;
_args = args;
}
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Get the function's calling convention.
ASMJIT_INLINE uint32_t getCallConv() const noexcept { return _callConv; }
//! Get the variable arguments `...` index, `kFuncNoVarArgs` if none.
ASMJIT_INLINE uint32_t getVarArgs() const noexcept { return _varArgs; }
//! Get the number of function arguments.
ASMJIT_INLINE uint32_t getNumArgs() const noexcept { return _numArgs; }
//! Get the return value type.
ASMJIT_INLINE uint32_t getRet() const noexcept { return _ret; }
//! Get the type of the argument at index `i`.
ASMJIT_INLINE uint32_t getArg(uint32_t i) const noexcept {
ASMJIT_ASSERT(i < _numArgs);
return _args[i];
}
//! Get the array of function arguments' types.
ASMJIT_INLINE const uint32_t* getArgs() const noexcept { return _args; }
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
uint8_t _callConv;
uint8_t _varArgs;
uint8_t _numArgs;
uint8_t _reserved;
uint32_t _ret;
const uint32_t* _args;
};
// ============================================================================
// [asmjit::FuncBuilderX]
// ============================================================================
// TODO: Rename to `DynamicFuncBuilder`
//! Custom function builder for up to 32 function arguments.
struct FuncBuilderX : public FuncPrototype {
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
ASMJIT_INLINE FuncBuilderX(uint32_t callConv = kCallConvHost) noexcept {
setup(callConv, kInvalidVar, _builderArgList, 0);
}
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
ASMJIT_INLINE void setCallConv(uint32_t callConv) noexcept {
ASMJIT_ASSERT(callConv <= 0xFF);
_callConv = static_cast<uint8_t>(callConv);
}
//! Set the return type to `retType`.
ASMJIT_INLINE void setRet(uint32_t retType) noexcept {
_ret = retType;
}
//! Set the return type based on `T`.
template<typename T>
ASMJIT_INLINE void setRetT() noexcept { setRet(TypeId<T>::kId); }
//! Set the argument at index `i` to the `type`
ASMJIT_INLINE void setArg(uint32_t i, uint32_t type) noexcept {
ASMJIT_ASSERT(i < _numArgs);
_builderArgList[i] = type;
}
//! Set the argument at index `i` to the type based on `T`.
template<typename T>
ASMJIT_INLINE void setArgT(uint32_t i) noexcept { setArg(i, TypeId<T>::kId); }
//! Append an argument of `type` to the function prototype.
ASMJIT_INLINE void addArg(uint32_t type) noexcept {
ASMJIT_ASSERT(_numArgs < kFuncArgCount);
_builderArgList[_numArgs++] = type;
}
//! Append an argument of type based on `T` to the function prototype.
template<typename T>
ASMJIT_INLINE void addArgT() noexcept { addArg(TypeId<T>::kId); }
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
uint32_t _builderArgList[kFuncArgCount];
};
//! \internal
#define T(_Type_) TypeId<_Type_>::kId
//! Function prototype (no args).
template<typename RET>
struct FuncBuilder0 : public FuncPrototype {
ASMJIT_INLINE FuncBuilder0(uint32_t callConv = kCallConvHost) noexcept {
setup(callConv, T(RET), nullptr, 0);
}
};
//! Function prototype (1 argument).
template<typename RET, typename P0>
struct FuncBuilder1 : public FuncPrototype {
ASMJIT_INLINE FuncBuilder1(uint32_t callConv = kCallConvHost) noexcept {
static const uint32_t args[] = { T(P0) };
setup(callConv, T(RET), args, ASMJIT_ARRAY_SIZE(args));
}
};
//! Function prototype (2 arguments).
template<typename RET, typename P0, typename P1>
struct FuncBuilder2 : public FuncPrototype {
ASMJIT_INLINE FuncBuilder2(uint32_t callConv = kCallConvHost) noexcept {
static const uint32_t args[] = { T(P0), T(P1) };
setup(callConv, T(RET), args, ASMJIT_ARRAY_SIZE(args));
}
};
//! Function prototype (3 arguments).
template<typename RET, typename P0, typename P1, typename P2>
struct FuncBuilder3 : public FuncPrototype {
ASMJIT_INLINE FuncBuilder3(uint32_t callConv = kCallConvHost) noexcept {
static const uint32_t args[] = { T(P0), T(P1), T(P2) };
setup(callConv, T(RET), args, ASMJIT_ARRAY_SIZE(args));
}
};
//! Function prototype (4 arguments).
template<typename RET, typename P0, typename P1, typename P2, typename P3>
struct FuncBuilder4 : public FuncPrototype {
ASMJIT_INLINE FuncBuilder4(uint32_t callConv = kCallConvHost) noexcept {
static const uint32_t args[] = { T(P0), T(P1), T(P2), T(P3) };
setup(callConv, T(RET), args, ASMJIT_ARRAY_SIZE(args));
}
};
//! Function prototype (5 arguments).
template<typename RET, typename P0, typename P1, typename P2, typename P3, typename P4>
struct FuncBuilder5 : public FuncPrototype {
ASMJIT_INLINE FuncBuilder5(uint32_t callConv = kCallConvHost) noexcept {
static const uint32_t args[] = { T(P0), T(P1), T(P2), T(P3), T(P4) };
setup(callConv, T(RET), args, ASMJIT_ARRAY_SIZE(args));
}
};
//! Function prototype (6 arguments).
template<typename RET, typename P0, typename P1, typename P2, typename P3, typename P4, typename P5>
struct FuncBuilder6 : public FuncPrototype {
ASMJIT_INLINE FuncBuilder6(uint32_t callConv = kCallConvHost) noexcept {
static const uint32_t args[] = { T(P0), T(P1), T(P2), T(P3), T(P4), T(P5) };
setup(callConv, T(RET), args, ASMJIT_ARRAY_SIZE(args));
}
};
//! Function prototype (7 arguments).
template<typename RET, typename P0, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6>
struct FuncBuilder7 : public FuncPrototype {
ASMJIT_INLINE FuncBuilder7(uint32_t callConv = kCallConvHost) noexcept {
static const uint32_t args[] = { T(P0), T(P1), T(P2), T(P3), T(P4), T(P5), T(P6) };
setup(callConv, T(RET), args, ASMJIT_ARRAY_SIZE(args));
}
};
//! Function prototype (8 arguments).
template<typename RET, typename P0, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7>
struct FuncBuilder8 : public FuncPrototype {
ASMJIT_INLINE FuncBuilder8(uint32_t callConv = kCallConvHost) noexcept {
static const uint32_t args[] = { T(P0), T(P1), T(P2), T(P3), T(P4), T(P5), T(P6), T(P7) };
setup(callConv, T(RET), args, ASMJIT_ARRAY_SIZE(args));
}
};
//! Function prototype (9 arguments).
template<typename RET, typename P0, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7, typename P8>
struct FuncBuilder9 : public FuncPrototype {
ASMJIT_INLINE FuncBuilder9(uint32_t callConv = kCallConvHost) noexcept {
static const uint32_t args[] = { T(P0), T(P1), T(P2), T(P3), T(P4), T(P5), T(P6), T(P7), T(P8) };
setup(callConv, T(RET), args, ASMJIT_ARRAY_SIZE(args));
}
};
//! Function prototype (10 arguments).
template<typename RET, typename P0, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7, typename P8, typename P9>
struct FuncBuilder10 : public FuncPrototype {
ASMJIT_INLINE FuncBuilder10(uint32_t callConv = kCallConvHost) noexcept {
static const uint32_t args[] = { T(P0), T(P1), T(P2), T(P3), T(P4), T(P5), T(P6), T(P7), T(P8), T(P9) };
setup(callConv, T(RET), args, ASMJIT_ARRAY_SIZE(args));
}
};
#undef T
// ============================================================================
// [asmjit::FuncDecl]
// ============================================================================
//! Function declaration.
struct FuncDecl {
// --------------------------------------------------------------------------
// [Accessors - Calling Convention]
// --------------------------------------------------------------------------
//! Get the function's calling convention, see `CallConv`.
ASMJIT_INLINE uint32_t getCallConv() const noexcept { return _callConv; }
//! Get whether the callee pops the stack.
ASMJIT_INLINE uint32_t getCalleePopsStack() const noexcept { return _calleePopsStack; }
//! Get direction of arguments passed on the stack.
//!
//! Direction should be always `kFuncDirRTL`.
//!
//! NOTE: This is related to used calling convention, it's not affected by
//! number of function arguments or their types.
ASMJIT_INLINE uint32_t getArgsDirection() const noexcept { return _argsDirection; }
//! Get stack size needed for function arguments passed on the stack.
ASMJIT_INLINE uint32_t getArgStackSize() const noexcept { return _argStackSize; }
//! Get size of "Red Zone".
ASMJIT_INLINE uint32_t getRedZoneSize() const noexcept { return _redZoneSize; }
//! Get size of "Spill Zone".
ASMJIT_INLINE uint32_t getSpillZoneSize() const noexcept { return _spillZoneSize; }
// --------------------------------------------------------------------------
// [Accessors - Arguments and Return]
// --------------------------------------------------------------------------
//! Get whether the function has a return value.
ASMJIT_INLINE bool hasRet() const noexcept { return _retCount != 0; }
//! Get count of function return values.
ASMJIT_INLINE uint32_t getRetCount() const noexcept { return _retCount; }
//! Get function return value.
ASMJIT_INLINE FuncInOut& getRet(uint32_t index = kFuncRetLo) noexcept { return _rets[index]; }
//! Get function return value.
ASMJIT_INLINE const FuncInOut& getRet(uint32_t index = kFuncRetLo) const noexcept { return _rets[index]; }
//! Get the number of function arguments.
ASMJIT_INLINE uint32_t getNumArgs() const noexcept { return _numArgs; }
//! Get function arguments array.
ASMJIT_INLINE FuncInOut* getArgs() noexcept { return _args; }
//! Get function arguments array (const).
ASMJIT_INLINE const FuncInOut* getArgs() const noexcept { return _args; }
//! Get function argument at index `index`.
ASMJIT_INLINE FuncInOut& getArg(size_t index) noexcept {
ASMJIT_ASSERT(index < kFuncArgCountLoHi);
return _args[index];
}
//! Get function argument at index `index`.
ASMJIT_INLINE const FuncInOut& getArg(size_t index) const noexcept {
ASMJIT_ASSERT(index < kFuncArgCountLoHi);
return _args[index];
}
ASMJIT_INLINE void resetArg(size_t index) noexcept {
ASMJIT_ASSERT(index < kFuncArgCountLoHi);
_args[index].reset();
}
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Calling convention.
uint8_t _callConv;
//! Whether a callee pops stack.
uint8_t _calleePopsStack : 1;
//! Direction for arguments passed on the stack, see `FuncDir`.
uint8_t _argsDirection : 1;
//! Reserved #0 (alignment).
uint8_t _reserved0 : 6;
//! Number of function arguments.
uint8_t _numArgs;
//! Number of function return values.
uint8_t _retCount;
//! Count of bytes consumed by arguments on the stack (aligned).
uint32_t _argStackSize;
//! Size of "Red Zone".
//!
//! NOTE: Used by AMD64-ABI (128 bytes).
uint16_t _redZoneSize;
//! Size of "Spill Zone".
//!
//! NOTE: Used by WIN64-ABI (32 bytes).
uint16_t _spillZoneSize;
//! Function arguments (LO & HI) mapped to physical registers and stack.
FuncInOut _args[kFuncArgCountLoHi];
//! Function return value(s).
FuncInOut _rets[2];
};
//! \}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
// [Guard]
#endif // !ASMJIT_DISABLE_COMPILER
#endif // _ASMJIT_BASE_COMPILERFUNC_H
+523
View File
@@ -0,0 +1,523 @@
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Export]
#define ASMJIT_EXPORTS
// [Dependencies]
#include "../base/constpool.h"
#include "../base/utils.h"
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
// Binary tree code is based on Julienne Walker's "Andersson Binary Trees"
// article and implementation. However, only three operations are implemented -
// get, insert and traverse.
// ============================================================================
// [asmjit::ConstPool::Tree - Ops]
// ============================================================================
//! \internal
//!
//! Remove left horizontal links.
static ASMJIT_INLINE ConstPool::Node* ConstPoolTree_skewNode(ConstPool::Node* node) noexcept {
ConstPool::Node* link = node->_link[0];
uint32_t level = node->_level;
if (level != 0 && link != nullptr && link->_level == level) {
node->_link[0] = link->_link[1];
link->_link[1] = node;
node = link;
}
return node;
}
//! \internal
//!
//! Remove consecutive horizontal links.
static ASMJIT_INLINE ConstPool::Node* ConstPoolTree_splitNode(ConstPool::Node* node) noexcept {
ConstPool::Node* link = node->_link[1];
uint32_t level = node->_level;
if (level != 0 && link != nullptr && link->_link[1] != nullptr && link->_link[1]->_level == level) {
node->_link[1] = link->_link[0];
link->_link[0] = node;
node = link;
node->_level++;
}
return node;
}
ConstPool::Node* ConstPool::Tree::get(const void* data) noexcept {
ConstPool::Node* node = _root;
size_t dataSize = _dataSize;
while (node != nullptr) {
int c = ::memcmp(node->getData(), data, dataSize);
if (c == 0)
return node;
node = node->_link[c < 0];
}
return nullptr;
}
void ConstPool::Tree::put(ConstPool::Node* newNode) noexcept {
size_t dataSize = _dataSize;
_length++;
if (_root == nullptr) {
_root = newNode;
return;
}
ConstPool::Node* node = _root;
ConstPool::Node* stack[kHeightLimit];
unsigned int top = 0;
unsigned int dir;
// Find a spot and save the stack.
for (;;) {
stack[top++] = node;
dir = ::memcmp(node->getData(), newNode->getData(), dataSize) < 0;
ConstPool::Node* link = node->_link[dir];
if (link == nullptr)
break;
node = link;
}
// Link and rebalance.
node->_link[dir] = newNode;
while (top > 0) {
// Which child?
node = stack[--top];
if (top != 0) {
dir = stack[top - 1]->_link[1] == node;
}
node = ConstPoolTree_skewNode(node);
node = ConstPoolTree_splitNode(node);
// Fix the parent.
if (top != 0)
stack[top - 1]->_link[dir] = node;
else
_root = node;
}
}
// ============================================================================
// [asmjit::ConstPool - Construction / Destruction]
// ============================================================================
ConstPool::ConstPool(Zone* zone) noexcept {
_zone = zone;
size_t dataSize = 1;
for (size_t i = 0; i < ASMJIT_ARRAY_SIZE(_tree); i++) {
_tree[i].setDataSize(dataSize);
_gaps[i] = nullptr;
dataSize <<= 1;
}
_gapPool = nullptr;
_size = 0;
_alignment = 0;
}
ConstPool::~ConstPool() noexcept {}
// ============================================================================
// [asmjit::ConstPool - Reset]
// ============================================================================
void ConstPool::reset() noexcept {
for (size_t i = 0; i < ASMJIT_ARRAY_SIZE(_tree); i++) {
_tree[i].reset();
_gaps[i] = nullptr;
}
_gapPool = nullptr;
_size = 0;
_alignment = 0;
}
// ============================================================================
// [asmjit::ConstPool - Ops]
// ============================================================================
static ASMJIT_INLINE ConstPool::Gap* ConstPool_allocGap(ConstPool* self) noexcept {
ConstPool::Gap* gap = self->_gapPool;
if (gap == nullptr)
return self->_zone->allocT<ConstPool::Gap>();
self->_gapPool = gap->_next;
return gap;
}
static ASMJIT_INLINE void ConstPool_freeGap(ConstPool* self, ConstPool::Gap* gap) noexcept {
gap->_next = self->_gapPool;
self->_gapPool = gap;
}
static void ConstPool_addGap(ConstPool* self, size_t offset, size_t length) noexcept {
ASMJIT_ASSERT(length > 0);
while (length > 0) {
size_t gapIndex;
size_t gapLength;
if (length >= 16 && Utils::isAligned<size_t>(offset, 16)) {
gapIndex = ConstPool::kIndex16;
gapLength = 16;
}
else if (length >= 8 && Utils::isAligned<size_t>(offset, 8)) {
gapIndex = ConstPool::kIndex8;
gapLength = 8;
}
else if (length >= 4 && Utils::isAligned<size_t>(offset, 4)) {
gapIndex = ConstPool::kIndex4;
gapLength = 4;
}
else if (length >= 2 && Utils::isAligned<size_t>(offset, 2)) {
gapIndex = ConstPool::kIndex2;
gapLength = 2;
}
else {
gapIndex = ConstPool::kIndex1;
gapLength = 1;
}
// We don't have to check for errors here, if this failed nothing really
// happened (just the gap won't be visible) and it will fail again at
// place where checking will cause kErrorNoHeapMemory.
ConstPool::Gap* gap = ConstPool_allocGap(self);
if (gap == nullptr)
return;
gap->_next = self->_gaps[gapIndex];
self->_gaps[gapIndex] = gap;
gap->_offset = offset;
gap->_length = gapLength;
offset += gapLength;
length -= gapLength;
}
}
Error ConstPool::add(const void* data, size_t size, size_t& dstOffset) noexcept {
size_t treeIndex;
if (size == 32)
treeIndex = kIndex32;
else if (size == 16)
treeIndex = kIndex16;
else if (size == 8)
treeIndex = kIndex8;
else if (size == 4)
treeIndex = kIndex4;
else if (size == 2)
treeIndex = kIndex2;
else if (size == 1)
treeIndex = kIndex1;
else
return kErrorInvalidArgument;
ConstPool::Node* node = _tree[treeIndex].get(data);
if (node != nullptr) {
dstOffset = node->_offset;
return kErrorOk;
}
// Before incrementing the current offset try if there is a gap that can
// be used for the requested data.
size_t offset = ~static_cast<size_t>(0);
size_t gapIndex = treeIndex;
while (gapIndex != kIndexCount - 1) {
ConstPool::Gap* gap = _gaps[treeIndex];
// Check if there is a gap.
if (gap != nullptr) {
size_t gapOffset = gap->_offset;
size_t gapLength = gap->_length;
// Destroy the gap for now.
_gaps[treeIndex] = gap->_next;
ConstPool_freeGap(this, gap);
offset = gapOffset;
ASMJIT_ASSERT(Utils::isAligned<size_t>(offset, size));
gapLength -= size;
if (gapLength > 0)
ConstPool_addGap(this, gapOffset, gapLength);
}
gapIndex++;
}
if (offset == ~static_cast<size_t>(0)) {
// Get how many bytes have to be skipped so the address is aligned accordingly
// to the 'size'.
size_t diff = Utils::alignDiff<size_t>(_size, size);
if (diff != 0) {
ConstPool_addGap(this, _size, diff);
_size += diff;
}
offset = _size;
_size += size;
}
// Add the initial node to the right index.
node = ConstPool::Tree::_newNode(_zone, data, size, offset, false);
if (node == nullptr)
return kErrorNoHeapMemory;
_tree[treeIndex].put(node);
_alignment = Utils::iMax<size_t>(_alignment, size);
dstOffset = offset;
// Now create a bunch of shared constants that are based on the data pattern.
// We stop at size 4, it probably doesn't make sense to split constants down
// to 1 byte.
size_t pCount = 1;
while (size > 4) {
size >>= 1;
pCount <<= 1;
ASMJIT_ASSERT(treeIndex != 0);
treeIndex--;
const uint8_t* pData = static_cast<const uint8_t*>(data);
for (size_t i = 0; i < pCount; i++, pData += size) {
node = _tree[treeIndex].get(pData);
if (node != nullptr)
continue;
node = ConstPool::Tree::_newNode(_zone, pData, size, offset + (i * size), true);
_tree[treeIndex].put(node);
}
}
return kErrorOk;
}
// ============================================================================
// [asmjit::ConstPool - Reset]
// ============================================================================
struct ConstPoolFill {
ASMJIT_INLINE ConstPoolFill(uint8_t* dst, size_t dataSize) noexcept :
_dst(dst),
_dataSize(dataSize) {}
ASMJIT_INLINE void visit(const ConstPool::Node* node) noexcept {
if (!node->_shared)
::memcpy(_dst + node->_offset, node->getData(), _dataSize);
}
uint8_t* _dst;
size_t _dataSize;
};
void ConstPool::fill(void* dst) const noexcept {
// Clears possible gaps, asmjit should never emit garbage to the output.
::memset(dst, 0, _size);
ConstPoolFill filler(static_cast<uint8_t*>(dst), 1);
for (size_t i = 0; i < ASMJIT_ARRAY_SIZE(_tree); i++) {
_tree[i].iterate(filler);
filler._dataSize <<= 1;
}
}
// ============================================================================
// [asmjit::ConstPool - Test]
// ============================================================================
#if defined(ASMJIT_TEST)
UNIT(base_constpool) {
Zone zone(32384 - Zone::kZoneOverhead);
ConstPool pool(&zone);
uint32_t i;
uint32_t kCount = 1000000;
INFO("Adding %u constants to the pool.", kCount);
{
size_t prevOffset;
size_t curOffset;
uint64_t c = ASMJIT_UINT64_C(0x0101010101010101);
EXPECT(pool.add(&c, 8, prevOffset) == kErrorOk,
"pool.add() - Returned error.");
EXPECT(prevOffset == 0,
"pool.add() - First constant should have zero offset.");
for (i = 1; i < kCount; i++) {
c++;
EXPECT(pool.add(&c, 8, curOffset) == kErrorOk,
"pool.add() - Returned error.");
EXPECT(prevOffset + 8 == curOffset,
"pool.add() - Returned incorrect curOffset.");
EXPECT(pool.getSize() == (i + 1) * 8,
"pool.getSize() - Reported incorrect size.");
prevOffset = curOffset;
}
EXPECT(pool.getAlignment() == 8,
"pool.getAlignment() - Expected 8-byte alignment.");
}
INFO("Retrieving %u constants from the pool.", kCount);
{
uint64_t c = ASMJIT_UINT64_C(0x0101010101010101);
for (i = 0; i < kCount; i++) {
size_t offset;
EXPECT(pool.add(&c, 8, offset) == kErrorOk,
"pool.add() - Returned error.");
EXPECT(offset == i * 8,
"pool.add() - Should have reused constant.");
c++;
}
}
INFO("Checking if the constants were split into 4-byte patterns.");
{
uint32_t c = 0x01010101;
for (i = 0; i < kCount; i++) {
size_t offset;
EXPECT(pool.add(&c, 4, offset) == kErrorOk,
"pool.add() - Returned error.");
EXPECT(offset == i * 8,
"pool.add() - Should reuse existing constant.");
c++;
}
}
INFO("Adding 2 byte constant to misalign the current offset.");
{
uint16_t c = 0xFFFF;
size_t offset;
EXPECT(pool.add(&c, 2, offset) == kErrorOk,
"pool.add() - Returned error.");
EXPECT(offset == kCount * 8,
"pool.add() - Didn't return expected position.");
EXPECT(pool.getAlignment() == 8,
"pool.getAlignment() - Expected 8-byte alignment.");
}
INFO("Adding 8 byte constant to check if pool gets aligned again.");
{
uint64_t c = ASMJIT_UINT64_C(0xFFFFFFFFFFFFFFFF);
size_t offset;
EXPECT(pool.add(&c, 8, offset) == kErrorOk,
"pool.add() - Returned error.");
EXPECT(offset == kCount * 8 + 8,
"pool.add() - Didn't return aligned offset.");
}
INFO("Adding 2 byte constant to verify the gap is filled.");
{
uint16_t c = 0xFFFE;
size_t offset;
EXPECT(pool.add(&c, 2, offset) == kErrorOk,
"pool.add() - Returned error.");
EXPECT(offset == kCount * 8 + 2,
"pool.add() - Didn't fill the gap.");
EXPECT(pool.getAlignment() == 8,
"pool.getAlignment() - Expected 8-byte alignment.");
}
INFO("Checking reset functionality.");
{
pool.reset();
EXPECT(pool.getSize() == 0,
"pool.getSize() - Expected pool size to be zero.");
EXPECT(pool.getAlignment() == 0,
"pool.getSize() - Expected pool alignment to be zero.");
}
INFO("Checking pool alignment when combined constants are added.");
{
uint8_t bytes[32] = { 0 };
size_t offset;
pool.add(bytes, 1, offset);
EXPECT(pool.getSize() == 1,
"pool.getSize() - Expected pool size to be 1 byte.");
EXPECT(pool.getAlignment() == 1,
"pool.getSize() - Expected pool alignment to be 1 byte.");
EXPECT(offset == 0,
"pool.getSize() - Expected offset returned to be zero.");
pool.add(bytes, 2, offset);
EXPECT(pool.getSize() == 4,
"pool.getSize() - Expected pool size to be 4 bytes.");
EXPECT(pool.getAlignment() == 2,
"pool.getSize() - Expected pool alignment to be 2 bytes.");
EXPECT(offset == 2,
"pool.getSize() - Expected offset returned to be 2.");
pool.add(bytes, 4, offset);
EXPECT(pool.getSize() == 8,
"pool.getSize() - Expected pool size to be 8 bytes.");
EXPECT(pool.getAlignment() == 4,
"pool.getSize() - Expected pool alignment to be 4 bytes.");
EXPECT(offset == 4,
"pool.getSize() - Expected offset returned to be 4.");
pool.add(bytes, 4, offset);
EXPECT(pool.getSize() == 8,
"pool.getSize() - Expected pool size to be 8 bytes.");
EXPECT(pool.getAlignment() == 4,
"pool.getSize() - Expected pool alignment to be 4 bytes.");
EXPECT(offset == 4,
"pool.getSize() - Expected offset returned to be 8.");
pool.add(bytes, 32, offset);
EXPECT(pool.getSize() == 64,
"pool.getSize() - Expected pool size to be 64 bytes.");
EXPECT(pool.getAlignment() == 32,
"pool.getSize() - Expected pool alignment to be 32 bytes.");
EXPECT(offset == 32,
"pool.getSize() - Expected offset returned to be 32.");
}
}
#endif // ASMJIT_TEST
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
+283
View File
@@ -0,0 +1,283 @@
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Guard]
#ifndef _ASMJIT_BASE_CONSTPOOL_H
#define _ASMJIT_BASE_CONSTPOOL_H
// [Dependencies]
#include "../base/zone.h"
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
//! \addtogroup asmjit_base
//! \{
// ============================================================================
// [asmjit::ConstPool]
// ============================================================================
//! Constant pool.
class ConstPool {
public:
ASMJIT_NO_COPY(ConstPool)
enum {
kIndex1 = 0,
kIndex2 = 1,
kIndex4 = 2,
kIndex8 = 3,
kIndex16 = 4,
kIndex32 = 5,
kIndexCount = 6
};
// --------------------------------------------------------------------------
// [Gap]
// --------------------------------------------------------------------------
//! \internal
//!
//! Zone-allocated const-pool gap.
struct Gap {
//! Link to the next gap
Gap* _next;
//! Offset of the gap.
size_t _offset;
//! Remaining bytes of the gap (basically a gap size).
size_t _length;
};
// --------------------------------------------------------------------------
// [Node]
// --------------------------------------------------------------------------
//! \internal
//!
//! Zone-allocated const-pool node.
struct Node {
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
ASMJIT_INLINE void* getData() const noexcept {
return static_cast<void*>(const_cast<ConstPool::Node*>(this) + 1);
}
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Left/Right nodes.
Node* _link[2];
//! Horizontal level for balance.
uint32_t _level : 31;
//! Whether this constant is shared with another.
uint32_t _shared : 1;
//! Data offset from the beginning of the pool.
uint32_t _offset;
};
// --------------------------------------------------------------------------
// [Tree]
// --------------------------------------------------------------------------
//! \internal
//!
//! Zone-allocated const-pool tree.
struct Tree {
enum {
//! Maximum tree height == log2(1 << 64).
kHeightLimit = 64
};
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
ASMJIT_INLINE Tree(size_t dataSize = 0) noexcept
: _root(nullptr),
_length(0),
_dataSize(dataSize) {}
ASMJIT_INLINE ~Tree() {}
// --------------------------------------------------------------------------
// [Reset]
// --------------------------------------------------------------------------
ASMJIT_INLINE void reset() noexcept {
_root = nullptr;
_length = 0;
}
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
ASMJIT_INLINE bool isEmpty() const noexcept { return _length == 0; }
ASMJIT_INLINE size_t getLength() const noexcept { return _length; }
ASMJIT_INLINE void setDataSize(size_t dataSize) noexcept {
ASMJIT_ASSERT(isEmpty());
_dataSize = dataSize;
}
// --------------------------------------------------------------------------
// [Ops]
// --------------------------------------------------------------------------
ASMJIT_API Node* get(const void* data) noexcept;
ASMJIT_API void put(Node* node) noexcept;
// --------------------------------------------------------------------------
// [Iterate]
// --------------------------------------------------------------------------
template<typename Visitor>
ASMJIT_INLINE void iterate(Visitor& visitor) const noexcept {
Node* node = const_cast<Node*>(_root);
if (node == nullptr)
return;
Node* stack[kHeightLimit];
size_t top = 0;
for (;;) {
Node* left = node->_link[0];
if (left != nullptr) {
ASMJIT_ASSERT(top != kHeightLimit);
stack[top++] = node;
node = left;
continue;
}
L_Visit:
visitor.visit(node);
node = node->_link[1];
if (node != nullptr)
continue;
if (top == 0)
return;
node = stack[--top];
goto L_Visit;
}
}
// --------------------------------------------------------------------------
// [Helpers]
// --------------------------------------------------------------------------
static ASMJIT_INLINE Node* _newNode(Zone* zone, const void* data, size_t size, size_t offset, bool shared) noexcept {
Node* node = zone->allocT<Node>(sizeof(Node) + size);
if (node == nullptr)
return nullptr;
node->_link[0] = nullptr;
node->_link[1] = nullptr;
node->_level = 1;
node->_shared = shared;
node->_offset = static_cast<uint32_t>(offset);
::memcpy(node->getData(), data, size);
return node;
}
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Root of the tree
Node* _root;
//! Length of the tree (count of nodes).
size_t _length;
//! Size of the data.
size_t _dataSize;
};
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
ASMJIT_API ConstPool(Zone* zone) noexcept;
ASMJIT_API ~ConstPool() noexcept;
// --------------------------------------------------------------------------
// [Reset]
// --------------------------------------------------------------------------
ASMJIT_API void reset() noexcept;
// --------------------------------------------------------------------------
// [Ops]
// --------------------------------------------------------------------------
//! Get whether the constant-pool is empty.
ASMJIT_INLINE bool isEmpty() const noexcept { return _size == 0; }
//! Get the size of the constant-pool in bytes.
ASMJIT_INLINE size_t getSize() const noexcept { return _size; }
//! Get minimum alignment.
ASMJIT_INLINE size_t getAlignment() const noexcept { return _alignment; }
//! Add a constant to the constant pool.
//!
//! The constant must have known size, which is 1, 2, 4, 8, 16 or 32 bytes.
//! The constant is added to the pool only if it doesn't not exist, otherwise
//! cached value is returned.
//!
//! AsmJit is able to subdivide added constants, so for example if you add
//! 8-byte constant 0x1122334455667788 it will create the following slots:
//!
//! 8-byte: 0x1122334455667788
//! 4-byte: 0x11223344, 0x55667788
//!
//! The reason is that when combining MMX/SSE/AVX code some patterns are used
//! frequently. However, AsmJit is not able to reallocate a constant that has
//! been already added. For example if you try to add 4-byte constant and then
//! 8-byte constant having the same 4-byte pattern as the previous one, two
//! independent slots will be generated by the pool.
ASMJIT_API Error add(const void* data, size_t size, size_t& dstOffset) noexcept;
// --------------------------------------------------------------------------
// [Fill]
// --------------------------------------------------------------------------
//! Fill the destination with the constants from the pool.
ASMJIT_API void fill(void* dst) const noexcept;
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Zone allocator.
Zone* _zone;
//! Tree per size.
Tree _tree[kIndexCount];
//! Gaps per size.
Gap* _gaps[kIndexCount];
//! Gaps pool
Gap* _gapPool;
//! Size of the pool (in bytes).
size_t _size;
//! Alignemnt.
size_t _alignment;
};
//! \}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
// [Guard]
#endif // _ASMJIT_BASE_CONSTPOOL_H
+374
View File
@@ -0,0 +1,374 @@
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Export]
#define ASMJIT_EXPORTS
// [Dependencies]
#include "../base/containers.h"
#include "../base/utils.h"
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
// ============================================================================
// [asmjit::StringBuilder - Construction / Destruction]
// ============================================================================
// Should be placed in read-only memory.
static const char StringBuilder_empty[4] = { 0 };
StringBuilder::StringBuilder() noexcept
: _data(const_cast<char*>(StringBuilder_empty)),
_length(0),
_capacity(0),
_canFree(false) {}
StringBuilder::~StringBuilder() noexcept {
if (_canFree)
ASMJIT_FREE(_data);
}
// ============================================================================
// [asmjit::StringBuilder - Prepare / Reserve]
// ============================================================================
char* StringBuilder::prepare(uint32_t op, size_t len) noexcept {
// --------------------------------------------------------------------------
// [Set]
// --------------------------------------------------------------------------
if (op == kStringOpSet) {
// We don't care here, but we can't return a NULL pointer since it indicates
// failure in memory allocation.
if (len == 0) {
if (_data != StringBuilder_empty)
_data[0] = 0;
_length = 0;
return _data;
}
if (_capacity < len) {
if (len >= IntTraits<size_t>::maxValue() - sizeof(intptr_t) * 2)
return nullptr;
size_t to = Utils::alignTo<size_t>(len, sizeof(intptr_t));
if (to < 256 - sizeof(intptr_t))
to = 256 - sizeof(intptr_t);
char* newData = static_cast<char*>(ASMJIT_ALLOC(to + sizeof(intptr_t)));
if (newData == nullptr) {
clear();
return nullptr;
}
if (_canFree)
ASMJIT_FREE(_data);
_data = newData;
_capacity = to + sizeof(intptr_t) - 1;
_canFree = true;
}
_data[len] = 0;
_length = len;
ASMJIT_ASSERT(_length <= _capacity);
return _data;
}
// --------------------------------------------------------------------------
// [Append]
// --------------------------------------------------------------------------
else {
// We don't care here, but we can't return a nullptr pointer since it indicates
// failure in memory allocation.
if (len == 0)
return _data + _length;
// Overflow.
if (IntTraits<size_t>::maxValue() - sizeof(intptr_t) * 2 - _length < len)
return nullptr;
size_t after = _length + len;
if (_capacity < after) {
size_t to = _capacity;
if (to < 256)
to = 256;
while (to < 1024 * 1024 && to < after)
to *= 2;
if (to < after) {
to = after;
if (to < (IntTraits<size_t>::maxValue() - 1024 * 32))
to = Utils::alignTo<size_t>(to, 1024 * 32);
}
to = Utils::alignTo<size_t>(to, sizeof(intptr_t));
char* newData = static_cast<char*>(ASMJIT_ALLOC(to + sizeof(intptr_t)));
if (newData == nullptr)
return nullptr;
::memcpy(newData, _data, _length);
if (_canFree)
ASMJIT_FREE(_data);
_data = newData;
_capacity = to + sizeof(intptr_t) - 1;
_canFree = true;
}
char* ret = _data + _length;
_data[after] = 0;
_length = after;
ASMJIT_ASSERT(_length <= _capacity);
return ret;
}
}
bool StringBuilder::reserve(size_t to) noexcept {
if (_capacity >= to)
return true;
if (to >= IntTraits<size_t>::maxValue() - sizeof(intptr_t) * 2)
return false;
to = Utils::alignTo<size_t>(to, sizeof(intptr_t));
char* newData = static_cast<char*>(ASMJIT_ALLOC(to + sizeof(intptr_t)));
if (newData == nullptr)
return false;
::memcpy(newData, _data, _length + 1);
if (_canFree)
ASMJIT_FREE(_data);
_data = newData;
_capacity = to + sizeof(intptr_t) - 1;
_canFree = true;
return true;
}
// ============================================================================
// [asmjit::StringBuilder - Clear]
// ============================================================================
void StringBuilder::clear() noexcept {
if (_data != StringBuilder_empty)
_data[0] = 0;
_length = 0;
}
// ============================================================================
// [asmjit::StringBuilder - Methods]
// ============================================================================
bool StringBuilder::_opString(uint32_t op, const char* str, size_t len) noexcept {
if (len == kInvalidIndex)
len = str != nullptr ? ::strlen(str) : static_cast<size_t>(0);
char* p = prepare(op, len);
if (p == nullptr)
return false;
::memcpy(p, str, len);
return true;
}
bool StringBuilder::_opChar(uint32_t op, char c) noexcept {
char* p = prepare(op, 1);
if (p == nullptr)
return false;
*p = c;
return true;
}
bool StringBuilder::_opChars(uint32_t op, char c, size_t len) noexcept {
char* p = prepare(op, len);
if (p == nullptr)
return false;
::memset(p, c, len);
return true;
}
static const char StringBuilder_numbers[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
bool StringBuilder::_opNumber(uint32_t op, uint64_t i, uint32_t base, size_t width, uint32_t flags) noexcept {
if (base < 2 || base > 36)
base = 10;
char buf[128];
char* p = buf + ASMJIT_ARRAY_SIZE(buf);
uint64_t orig = i;
char sign = '\0';
// --------------------------------------------------------------------------
// [Sign]
// --------------------------------------------------------------------------
if ((flags & kStringFormatSigned) != 0 && static_cast<int64_t>(i) < 0) {
i = static_cast<uint64_t>(-static_cast<int64_t>(i));
sign = '-';
}
else if ((flags & kStringFormatShowSign) != 0) {
sign = '+';
}
else if ((flags & kStringFormatShowSpace) != 0) {
sign = ' ';
}
// --------------------------------------------------------------------------
// [Number]
// --------------------------------------------------------------------------
do {
uint64_t d = i / base;
uint64_t r = i % base;
*--p = StringBuilder_numbers[r];
i = d;
} while (i);
size_t numberLength = (size_t)(buf + ASMJIT_ARRAY_SIZE(buf) - p);
// --------------------------------------------------------------------------
// [Alternate Form]
// --------------------------------------------------------------------------
if ((flags & kStringFormatAlternate) != 0) {
if (base == 8) {
if (orig != 0)
*--p = '0';
}
if (base == 16) {
*--p = 'x';
*--p = '0';
}
}
// --------------------------------------------------------------------------
// [Width]
// --------------------------------------------------------------------------
if (sign != 0)
*--p = sign;
if (width > 256)
width = 256;
if (width <= numberLength)
width = 0;
else
width -= numberLength;
// --------------------------------------------------------------------------
// Write]
// --------------------------------------------------------------------------
size_t prefixLength = (size_t)(buf + ASMJIT_ARRAY_SIZE(buf) - p) - numberLength;
char* data = prepare(op, prefixLength + width + numberLength);
if (data == nullptr)
return false;
::memcpy(data, p, prefixLength);
data += prefixLength;
::memset(data, '0', width);
data += width;
::memcpy(data, p + prefixLength, numberLength);
return true;
}
bool StringBuilder::_opHex(uint32_t op, const void* data, size_t len) noexcept {
if (len >= IntTraits<size_t>::maxValue() / 2)
return false;
char* dst = prepare(op, len * 2);
if (dst == nullptr)
return false;
const char* src = static_cast<const char*>(data);
for (size_t i = 0; i < len; i++, dst += 2, src += 1)
{
dst[0] = StringBuilder_numbers[(src[0] >> 4) & 0xF];
dst[1] = StringBuilder_numbers[(src[0] ) & 0xF];
}
return true;
}
bool StringBuilder::_opVFormat(uint32_t op, const char* fmt, va_list ap) noexcept {
char buf[1024];
vsnprintf(buf, ASMJIT_ARRAY_SIZE(buf), fmt, ap);
buf[ASMJIT_ARRAY_SIZE(buf) - 1] = '\0';
return _opString(op, buf);
}
bool StringBuilder::setFormat(const char* fmt, ...) noexcept {
bool result;
va_list ap;
va_start(ap, fmt);
result = _opVFormat(kStringOpSet, fmt, ap);
va_end(ap);
return result;
}
bool StringBuilder::appendFormat(const char* fmt, ...) noexcept {
bool result;
va_list ap;
va_start(ap, fmt);
result = _opVFormat(kStringOpAppend, fmt, ap);
va_end(ap);
return result;
}
bool StringBuilder::eq(const char* str, size_t len) const noexcept {
const char* aData = _data;
const char* bData = str;
size_t aLength = _length;
size_t bLength = len;
if (bLength == kInvalidIndex) {
size_t i;
for (i = 0; i < aLength; i++) {
if (aData[i] != bData[i] || bData[i] == 0)
return false;
}
return bData[i] == 0;
}
else {
if (aLength != bLength)
return false;
return ::memcmp(aData, bData, aLength) == 0;
}
}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
+550
View File
@@ -0,0 +1,550 @@
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Guard]
#ifndef _ASMJIT_BASE_CONTAINERS_H
#define _ASMJIT_BASE_CONTAINERS_H
// [Dependencies]
#include "../base/globals.h"
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
//! \addtogroup asmjit_base
//! \{
// ============================================================================
// [asmjit::BitArray]
// ============================================================================
//! Fixed size bit-array.
//!
//! Used by variable liveness analysis.
struct BitArray {
// --------------------------------------------------------------------------
// [Enums]
// --------------------------------------------------------------------------
enum {
kEntitySize = static_cast<int>(sizeof(uintptr_t)),
kEntityBits = kEntitySize * 8
};
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
ASMJIT_INLINE uintptr_t getBit(uint32_t index) const noexcept {
return (data[index / kEntityBits] >> (index % kEntityBits)) & 1;
}
ASMJIT_INLINE void setBit(uint32_t index) noexcept {
data[index / kEntityBits] |= static_cast<uintptr_t>(1) << (index % kEntityBits);
}
ASMJIT_INLINE void delBit(uint32_t index) noexcept {
data[index / kEntityBits] &= ~(static_cast<uintptr_t>(1) << (index % kEntityBits));
}
// --------------------------------------------------------------------------
// [Interface]
// --------------------------------------------------------------------------
//! Copy bits from `s0`, returns `true` if at least one bit is set in `s0`.
ASMJIT_INLINE bool copyBits(const BitArray* s0, uint32_t len) noexcept {
uintptr_t r = 0;
for (uint32_t i = 0; i < len; i++) {
uintptr_t t = s0->data[i];
data[i] = t;
r |= t;
}
return r != 0;
}
ASMJIT_INLINE bool addBits(const BitArray* s0, uint32_t len) noexcept {
return addBits(this, s0, len);
}
ASMJIT_INLINE bool addBits(const BitArray* s0, const BitArray* s1, uint32_t len) noexcept {
uintptr_t r = 0;
for (uint32_t i = 0; i < len; i++) {
uintptr_t t = s0->data[i] | s1->data[i];
data[i] = t;
r |= t;
}
return r != 0;
}
ASMJIT_INLINE bool andBits(const BitArray* s1, uint32_t len) noexcept {
return andBits(this, s1, len);
}
ASMJIT_INLINE bool andBits(const BitArray* s0, const BitArray* s1, uint32_t len) noexcept {
uintptr_t r = 0;
for (uint32_t i = 0; i < len; i++) {
uintptr_t t = s0->data[i] & s1->data[i];
data[i] = t;
r |= t;
}
return r != 0;
}
ASMJIT_INLINE bool delBits(const BitArray* s1, uint32_t len) noexcept {
return delBits(this, s1, len);
}
ASMJIT_INLINE bool delBits(const BitArray* s0, const BitArray* s1, uint32_t len) noexcept {
uintptr_t r = 0;
for (uint32_t i = 0; i < len; i++) {
uintptr_t t = s0->data[i] & ~s1->data[i];
data[i] = t;
r |= t;
}
return r != 0;
}
ASMJIT_INLINE bool _addBitsDelSource(BitArray* s1, uint32_t len) noexcept {
return _addBitsDelSource(this, s1, len);
}
ASMJIT_INLINE bool _addBitsDelSource(const BitArray* s0, BitArray* s1, uint32_t len) noexcept {
uintptr_t r = 0;
for (uint32_t i = 0; i < len; i++) {
uintptr_t a = s0->data[i];
uintptr_t b = s1->data[i];
this->data[i] = a | b;
b &= ~a;
s1->data[i] = b;
r |= b;
}
return r != 0;
}
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
uintptr_t data[1];
};
// ============================================================================
// [asmjit::PodList<T>]
// ============================================================================
//! \internal
template <typename T>
class PodList {
public:
ASMJIT_NO_COPY(PodList<T>)
// --------------------------------------------------------------------------
// [Link]
// --------------------------------------------------------------------------
struct Link {
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Get next node.
ASMJIT_INLINE Link* getNext() const noexcept { return _next; }
//! Get value.
ASMJIT_INLINE T getValue() const noexcept { return _value; }
//! Set value to `value`.
ASMJIT_INLINE void setValue(const T& value) noexcept { _value = value; }
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
Link* _next;
T _value;
};
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
ASMJIT_INLINE PodList() noexcept : _first(nullptr), _last(nullptr) {}
ASMJIT_INLINE ~PodList() noexcept {}
// --------------------------------------------------------------------------
// [Data]
// --------------------------------------------------------------------------
ASMJIT_INLINE bool isEmpty() const noexcept { return _first != nullptr; }
ASMJIT_INLINE Link* getFirst() const noexcept { return _first; }
ASMJIT_INLINE Link* getLast() const noexcept { return _last; }
// --------------------------------------------------------------------------
// [Ops]
// --------------------------------------------------------------------------
ASMJIT_INLINE void reset() noexcept {
_first = nullptr;
_last = nullptr;
}
ASMJIT_INLINE void prepend(Link* link) noexcept {
link->_next = _first;
if (_first == nullptr)
_last = link;
_first = link;
}
ASMJIT_INLINE void append(Link* link) noexcept {
link->_next = nullptr;
if (_first == nullptr)
_first = link;
else
_last->_next = link;
_last = link;
}
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
Link* _first;
Link* _last;
};
// ============================================================================
// [asmjit::StringBuilder]
// ============================================================================
//! String builder.
//!
//! String builder was designed to be able to build a string using append like
//! operation to append numbers, other strings, or signle characters. It can
//! allocate it's own buffer or use a buffer created on the stack.
//!
//! String builder contains method specific to AsmJit functionality, used for
//! logging or HTML output.
class StringBuilder {
public:
ASMJIT_NO_COPY(StringBuilder)
// --------------------------------------------------------------------------
// [Enums]
// --------------------------------------------------------------------------
//! \internal
//!
//! String operation.
ASMJIT_ENUM(StringOp) {
//! Replace the current string by a given content.
kStringOpSet = 0,
//! Append a given content to the current string.
kStringOpAppend = 1
};
//! \internal
//!
//! String format flags.
ASMJIT_ENUM(StringFormatFlags) {
kStringFormatShowSign = 0x00000001,
kStringFormatShowSpace = 0x00000002,
kStringFormatAlternate = 0x00000004,
kStringFormatSigned = 0x80000000
};
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
ASMJIT_API StringBuilder() noexcept;
ASMJIT_API ~StringBuilder() noexcept;
ASMJIT_INLINE StringBuilder(const _NoInit&) noexcept {}
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Get string builder capacity.
ASMJIT_INLINE size_t getCapacity() const noexcept { return _capacity; }
//! Get length.
ASMJIT_INLINE size_t getLength() const noexcept { return _length; }
//! Get null-terminated string data.
ASMJIT_INLINE char* getData() noexcept { return _data; }
//! Get null-terminated string data (const).
ASMJIT_INLINE const char* getData() const noexcept { return _data; }
// --------------------------------------------------------------------------
// [Prepare / Reserve]
// --------------------------------------------------------------------------
//! Prepare to set/append.
ASMJIT_API char* prepare(uint32_t op, size_t len) noexcept;
//! Reserve `to` bytes in string builder.
ASMJIT_API bool reserve(size_t to) noexcept;
// --------------------------------------------------------------------------
// [Clear]
// --------------------------------------------------------------------------
//! Clear the content in String builder.
ASMJIT_API void clear() noexcept;
// --------------------------------------------------------------------------
// [Op]
// --------------------------------------------------------------------------
ASMJIT_API bool _opString(uint32_t op, const char* str, size_t len = kInvalidIndex) noexcept;
ASMJIT_API bool _opVFormat(uint32_t op, const char* fmt, va_list ap) noexcept;
ASMJIT_API bool _opChar(uint32_t op, char c) noexcept;
ASMJIT_API bool _opChars(uint32_t op, char c, size_t len) noexcept;
ASMJIT_API bool _opNumber(uint32_t op, uint64_t i, uint32_t base = 0, size_t width = 0, uint32_t flags = 0) noexcept;
ASMJIT_API bool _opHex(uint32_t op, const void* data, size_t len) noexcept;
// --------------------------------------------------------------------------
// [Set]
// --------------------------------------------------------------------------
//! Replace the current content by `str` of `len`.
ASMJIT_INLINE bool setString(const char* str, size_t len = kInvalidIndex) noexcept {
return _opString(kStringOpSet, str, len);
}
//! Replace the current content by formatted string `fmt`.
ASMJIT_INLINE bool setVFormat(const char* fmt, va_list ap) noexcept {
return _opVFormat(kStringOpSet, fmt, ap);
}
//! Replace the current content by formatted string `fmt`.
ASMJIT_API bool setFormat(const char* fmt, ...) noexcept;
//! Replace the current content by `c` character.
ASMJIT_INLINE bool setChar(char c) noexcept {
return _opChar(kStringOpSet, c);
}
//! Replace the current content by `c` of `len`.
ASMJIT_INLINE bool setChars(char c, size_t len) noexcept {
return _opChars(kStringOpSet, c, len);
}
//! Replace the current content by formatted integer `i`.
ASMJIT_INLINE bool setInt(uint64_t i, uint32_t base = 0, size_t width = 0, uint32_t flags = 0) noexcept {
return _opNumber(kStringOpSet, i, base, width, flags | kStringFormatSigned);
}
//! Replace the current content by formatted integer `i`.
ASMJIT_INLINE bool setUInt(uint64_t i, uint32_t base = 0, size_t width = 0, uint32_t flags = 0) noexcept {
return _opNumber(kStringOpSet, i, base, width, flags);
}
//! Replace the current content by the given `data` converted to a HEX string.
ASMJIT_INLINE bool setHex(const void* data, size_t len) noexcept {
return _opHex(kStringOpSet, data, len);
}
// --------------------------------------------------------------------------
// [Append]
// --------------------------------------------------------------------------
//! Append `str` of `len`.
ASMJIT_INLINE bool appendString(const char* str, size_t len = kInvalidIndex) noexcept {
return _opString(kStringOpAppend, str, len);
}
//! Append a formatted string `fmt` to the current content.
ASMJIT_INLINE bool appendVFormat(const char* fmt, va_list ap) noexcept {
return _opVFormat(kStringOpAppend, fmt, ap);
}
//! Append a formatted string `fmt` to the current content.
ASMJIT_API bool appendFormat(const char* fmt, ...) noexcept;
//! Append `c` character.
ASMJIT_INLINE bool appendChar(char c) noexcept {
return _opChar(kStringOpAppend, c);
}
//! Append `c` of `len`.
ASMJIT_INLINE bool appendChars(char c, size_t len) noexcept {
return _opChars(kStringOpAppend, c, len);
}
//! Append `i`.
ASMJIT_INLINE bool appendInt(int64_t i, uint32_t base = 0, size_t width = 0, uint32_t flags = 0) noexcept {
return _opNumber(kStringOpAppend, static_cast<uint64_t>(i), base, width, flags | kStringFormatSigned);
}
//! Append `i`.
ASMJIT_INLINE bool appendUInt(uint64_t i, uint32_t base = 0, size_t width = 0, uint32_t flags = 0) noexcept {
return _opNumber(kStringOpAppend, i, base, width, flags);
}
//! Append the given `data` converted to a HEX string.
ASMJIT_INLINE bool appendHex(const void* data, size_t len) noexcept {
return _opHex(kStringOpAppend, data, len);
}
// --------------------------------------------------------------------------
// [_Append]
// --------------------------------------------------------------------------
//! Append `str` of `len`, inlined, without buffer overflow check.
ASMJIT_INLINE void _appendString(const char* str, size_t len = kInvalidIndex) noexcept {
// len should be a constant if we are inlining.
if (len == kInvalidIndex) {
char* p = &_data[_length];
while (*str) {
ASMJIT_ASSERT(p < _data + _capacity);
*p++ = *str++;
}
*p = '\0';
_length = (size_t)(p - _data);
}
else {
ASMJIT_ASSERT(_capacity - _length >= len);
char* p = &_data[_length];
char* pEnd = p + len;
while (p < pEnd)
*p++ = *str++;
*p = '\0';
_length += len;
}
}
//! Append `c` character, inlined, without buffer overflow check.
ASMJIT_INLINE void _appendChar(char c) noexcept {
ASMJIT_ASSERT(_capacity - _length >= 1);
_data[_length] = c;
_length++;
_data[_length] = '\0';
}
//! Append `c` of `len`, inlined, without buffer overflow check.
ASMJIT_INLINE void _appendChars(char c, size_t len) noexcept {
ASMJIT_ASSERT(_capacity - _length >= len);
char* p = &_data[_length];
char* pEnd = p + len;
while (p < pEnd)
*p++ = c;
*p = '\0';
_length += len;
}
ASMJIT_INLINE void _appendUInt32(uint32_t i) noexcept {
char buf_[32];
char* pEnd = buf_ + ASMJIT_ARRAY_SIZE(buf_);
char* pBuf = pEnd;
do {
uint32_t d = i / 10;
uint32_t r = i % 10;
*--pBuf = static_cast<uint8_t>(r + '0');
i = d;
} while (i);
ASMJIT_ASSERT(_capacity - _length >= (size_t)(pEnd - pBuf));
char* p = &_data[_length];
do {
*p++ = *pBuf;
} while (++pBuf != pEnd);
*p = '\0';
_length = (size_t)(p - _data);
}
// --------------------------------------------------------------------------
// [Eq]
// --------------------------------------------------------------------------
//! Check for equality with other `str` of `len`.
ASMJIT_API bool eq(const char* str, size_t len = kInvalidIndex) const noexcept;
//! Check for equality with `other`.
ASMJIT_INLINE bool eq(const StringBuilder& other) const noexcept { return eq(other._data); }
// --------------------------------------------------------------------------
// [Operator Overload]
// --------------------------------------------------------------------------
ASMJIT_INLINE bool operator==(const StringBuilder& other) const noexcept { return eq(other); }
ASMJIT_INLINE bool operator!=(const StringBuilder& other) const noexcept { return !eq(other); }
ASMJIT_INLINE bool operator==(const char* str) const noexcept { return eq(str); }
ASMJIT_INLINE bool operator!=(const char* str) const noexcept { return !eq(str); }
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! String data.
char* _data;
//! Length.
size_t _length;
//! Capacity.
size_t _capacity;
//! Whether the string can be freed.
size_t _canFree;
};
// ============================================================================
// [asmjit::StringBuilderTmp]
// ============================================================================
//! Temporary string builder, has statically allocated `N` bytes.
template<size_t N>
class StringBuilderTmp : public StringBuilder {
public:
ASMJIT_NO_COPY(StringBuilderTmp<N>)
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
ASMJIT_INLINE StringBuilderTmp() noexcept : StringBuilder(NoInit) {
_data = _embeddedData;
_data[0] = 0;
_length = 0;
_capacity = N;
_canFree = false;
}
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Embedded data.
char _embeddedData[static_cast<size_t>(
N + 1 + sizeof(intptr_t)) & ~static_cast<size_t>(sizeof(intptr_t) - 1)];
};
//! \}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
// [Guard]
#endif // _ASMJIT_BASE_CONTAINERS_H
+643
View File
@@ -0,0 +1,643 @@
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Export]
#define ASMJIT_EXPORTS
// [Dependencies]
#include "../base/cpuinfo.h"
#include "../base/utils.h"
#if ASMJIT_OS_POSIX
# include <errno.h>
# include <sys/statvfs.h>
# include <sys/utsname.h>
# include <unistd.h>
#endif // ASMJIT_OS_POSIX
#if ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64
# if ASMJIT_CC_MSC_GE(14, 0, 0)
# include <intrin.h> // Required by `__cpuid()` and `_xgetbv()`.
# endif // _MSC_VER >= 1400
#endif
#if ASMJIT_ARCH_ARM32 || ASMJIT_ARCH_ARM64
# if ASMJIT_OS_LINUX
# include <sys/auxv.h> // Required by `getauxval()`.
# endif
#endif
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
// ============================================================================
// [asmjit::CpuInfo - Detect ARM & ARM64]
// ============================================================================
// ARM information has to be retrieved by the OS (this is how ARM was designed).
#if ASMJIT_ARCH_ARM32 || ASMJIT_ARCH_ARM64
#if ASMJIT_ARCH_ARM64
static void armPopulateBaseline64Features(CpuInfo* cpuInfo) noexcept {
// Thumb (including all variations) is only supported on ARM32.
// ARM64 is based on ARMv8 and newer.
cpuInfo->addFeature(CpuInfo::kArmFeatureV6);
cpuInfo->addFeature(CpuInfo::kArmFeatureV7);
cpuInfo->addFeature(CpuInfo::kArmFeatureV8);
// ARM64 comes with these features by default.
cpuInfo->addFeature(CpuInfo::kArmFeatureDSP);
cpuInfo->addFeature(CpuInfo::kArmFeatureIDIV);
cpuInfo->addFeature(CpuInfo::kArmFeatureVFP2);
cpuInfo->addFeature(CpuInfo::kArmFeatureVFP3);
cpuInfo->addFeature(CpuInfo::kArmFeatureVFP4);
}
#endif // ASMJIT_ARCH_ARM64
#if ASMJIT_OS_WINDOWS
//! \internal
//!
//! Detect ARM CPU features on Windows.
//!
//! The detection is based on `IsProcessorFeaturePresent()` API call.
static void armDetectCpuInfoOnWindows(CpuInfo* cpuInfo) noexcept {
#if ASMJIT_ARCH_ARM32
cpuInfo->setArch(kArchArm32);
// Windows for ARM requires at least ARMv7 with DSP extensions.
cpuInfo->addFeature(CpuInfo::kArmFeatureV6);
cpuInfo->addFeature(CpuInfo::kArmFeatureV7);
cpuInfo->addFeature(CpuInfo::kArmFeatureDSP);
// Windows for ARM requires VFP3.
cpuInfo->addFeature(CpuInfo::kArmFeatureVFP2);
cpuInfo->addFeature(CpuInfo::kArmFeatureVFP3);
// Windows for ARM requires and uses THUMB2.
cpuInfo->addFeature(CpuInfo::kArmFeatureTHUMB);
cpuInfo->addFeature(CpuInfo::kArmFeatureTHUMB2);
#else
cpuInfo->setArch(kArchArm64);
armPopulateBaseline64Features(cpuInfo);
#endif
// Windows for ARM requires NEON.
cpuInfo->addFeature(CpuInfo::kArmFeatureNEON);
// Detect additional CPU features by calling `IsProcessorFeaturePresent()`.
struct WinPFPMapping {
uint32_t pfpId, featureId;
};
static const WinPFPMapping mapping[] = {
{ PF_ARM_FMAC_INSTRUCTIONS_AVAILABLE , CpuInfo::kArmFeatureVFP4 },
{ PF_ARM_VFP_32_REGISTERS_AVAILABLE , CpuInfo::kArmFeatureVFP_D32 },
{ PF_ARM_DIVIDE_INSTRUCTION_AVAILABLE, CpuInfo::kArmFeatureIDIV },
{ PF_ARM_64BIT_LOADSTORE_ATOMIC , CpuInfo::kArmFeatureAtomics64 }
};
for (uint32_t i = 0; i < ASMJIT_ARRAY_SIZE(mapping); i++)
if (::IsProcessorFeaturePresent(mapping[i].pfpId))
cpuInfo->addFeature(mapping[i].featureId);
}
#endif // ASMJIT_OS_WINDOWS
#if ASMJIT_OS_LINUX
struct LinuxHWCapMapping {
uint32_t hwcapMask, featureId;
};
static void armDetectHWCaps(CpuInfo* cpuInfo,
unsigned long type, const LinuxHWCapMapping* mapping, size_t length) noexcept {
unsigned long mask = getauxval(type);
for (size_t i = 0; i < length; i++)
if ((mask & mapping[i].hwcapMask) == mapping[i].hwcapMask)
cpuInfo->addFeature(mapping[i].featureId);
}
//! \internal
//!
//! Detect ARM CPU features on Linux.
//!
//! The detection is based on `getauxval()`.
static void armDetectCpuInfoOnLinux(CpuInfo* cpuInfo) noexcept {
#if ASMJIT_ARCH_ARM32
cpuInfo->setArch(kArchArm32);
// `AT_HWCAP` provides ARMv7 (and less) related flags.
static const LinuxHWCapMapping hwCapMapping[] = {
{ /* HWCAP_VFPv3 */ (1 << 13), CpuInfo::kArmFeatureVFP3 },
{ /* HWCAP_VFPv4 */ (1 << 16), CpuInfo::kArmFeatureVFP4 },
{ /* HWCAP_IDIVA */ (3 << 17), CpuInfo::kArmFeatureIDIV },
{ /* HWCAP_VFPD32 */ (1 << 19), CpuInfo::kArmFeatureVFP_D32 },
{ /* HWCAP_NEON */ (1 << 12), CpuInfo::kArmFeatureNEON },
{ /* HWCAP_EDSP */ (1 << 7), CpuInfo::kArmFeatureDSP }
};
armDetectHWCaps(cpuInfo, AT_HWCAP, hwCapMapping, ASMJIT_ARRAY_SIZE(hwCapMapping));
// VFP3 implies VFP2.
if (cpuInfo->hasFeature(CpuInfo::kArmFeatureVFP3))
cpuInfo->addFeature(CpuInfo::kArmFeatureVFP2);
// VFP2 implies ARMv6.
if (cpuInfo->hasFeature(CpuInfo::kArmFeatureVFP2))
cpuInfo->addFeature(CpuInfo::kArmFeatureV6);
// VFP3 or NEON implies ARMv7.
if (cpuInfo->hasFeature(CpuInfo::kArmFeatureVFP3) ||
cpuInfo->hasFeature(CpuInfo::kArmFeatureNEON))
cpuInfo->addFeature(CpuInfo::kArmFeatureV7);
// `AT_HWCAP2` provides ARMv8 related flags.
static const LinuxHWCapMapping hwCap2Mapping[] = {
{ /* HWCAP2_AES */ (1 << 0), CpuInfo::kArmFeatureAES },
{ /* HWCAP2_CRC32 */ (1 << 4), CpuInfo::kArmFeatureCRC32 },
{ /* HWCAP2_PMULL */ (1 << 1), CpuInfo::kArmFeaturePMULL },
{ /* HWCAP2_SHA1 */ (1 << 2), CpuInfo::kArmFeatureSHA1 },
{ /* HWCAP2_SHA2 */ (1 << 3), CpuInfo::kArmFeatureSHA256 }
};
armDetectHWCaps(cpuInfo, AT_HWCAP2, hwCap2Mapping, ASMJIT_ARRAY_SIZE(hwCap2Mapping));
if (cpuInfo->hasFeature(CpuInfo::kArmFeatureAES ) ||
cpuInfo->hasFeature(CpuInfo::kArmFeatureCRC32 ) ||
cpuInfo->hasFeature(CpuInfo::kArmFeaturePMULL ) ||
cpuInfo->hasFeature(CpuInfo::kArmFeatureSHA1 ) ||
cpuInfo->hasFeature(CpuInfo::kArmFeatureSHA256)) {
cpuInfo->addFeature(CpuInfo::kArmFeatureV8);
}
#else
cpuInfo->setArch(kArchArm64);
armPopulateBaseline64Features(cpuInfo);
// `AT_HWCAP` provides ARMv8 related flags.
static const LinuxHWCapMapping hwCapMapping[] = {
{ /* HWCAP_ASIMD */ (1 << 1), CpuInfo::kArmFeatureNEON },
{ /* HWCAP_AES */ (1 << 3), CpuInfo::kArmFeatureAES },
{ /* HWCAP_CRC32 */ (1 << 7), CpuInfo::kArmFeatureCRC32 },
{ /* HWCAP_PMULL */ (1 << 4), CpuInfo::kArmFeaturePMULL },
{ /* HWCAP_SHA1 */ (1 << 5), CpuInfo::kArmFeatureSHA1 },
{ /* HWCAP_SHA2 */ (1 << 6), CpuInfo::kArmFeatureSHA256 }
{ /* HWCAP_ATOMICS */ (1 << 8), CpuInfo::kArmFeatureAtomics64 }
};
armDetectHWCaps(cpuInfo, AT_HWCAP, hwCapMapping, ASMJIT_ARRAY_SIZE(hwCapMapping));
// `AT_HWCAP2` is not used at the moment.
#endif
}
#endif // ASMJIT_OS_LINUX
static void armDetectCpuInfo(CpuInfo* cpuInfo) noexcept {
#if ASMJIT_OS_WINDOWS
armDetectCpuInfoOnWindows(cpuInfo);
#elif ASMJIT_OS_LINUX
armDetectCpuInfoOnLinux(cpuInfo);
#else
# error "[asmjit] armDetectCpuInfo() - Unsupported OS."
#endif
}
#endif // ASMJIT_ARCH_ARM32 || ASMJIT_ARCH_ARM64
// ============================================================================
// [asmjit::CpuInfo - Detect X86 & X64]
// ============================================================================
#if ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64
//! \internal
//!
//! X86 CPUID result.
struct CpuIdResult {
uint32_t eax, ebx, ecx, edx;
};
//! \internal
//!
//! Content of XCR register, result of XGETBV instruction.
struct XGetBVResult {
uint32_t eax, edx;
};
#if ASMJIT_CC_MSC && !ASMJIT_CC_MSC_GE(15, 0, 30729) && ASMJIT_ARCH_X64
//! \internal
//!
//! HACK: VS2008 or less, 64-bit mode - `__cpuidex` doesn't exist! However,
//! 64-bit calling convention specifies the first parameter to be passed in
//! ECX, so we may be lucky if compiler doesn't move the register, otherwise
//! the result would be wrong.
static void ASMJIT_NOINLINE void x86CallCpuIdWorkaround(uint32_t inEcx, uint32_t inEax, CpuIdResult* result) noexcept {
__cpuid(reinterpret_cast<int*>(result), inEax);
}
#endif
//! \internal
//!
//! Wrapper to call `cpuid` instruction.
static void ASMJIT_INLINE x86CallCpuId(CpuIdResult* result, uint32_t inEax, uint32_t inEcx = 0) noexcept {
#if ASMJIT_CC_MSC && ASMJIT_CC_MSC_GE(15, 0, 30729)
__cpuidex(reinterpret_cast<int*>(result), inEax, inEcx);
#elif ASMJIT_CC_MSC && ASMJIT_ARCH_X64
x86CallCpuIdWorkaround(inEcx, inEax, result);
#elif ASMJIT_CC_MSC && ASMJIT_ARCH_X86
uint32_t paramEax = inEax;
uint32_t paramEcx = inEcx;
uint32_t* out = reinterpret_cast<uint32_t*>(result);
__asm {
mov eax, paramEax
mov ecx, paramEcx
mov edi, out
cpuid
mov dword ptr[edi + 0], eax
mov dword ptr[edi + 4], ebx
mov dword ptr[edi + 8], ecx
mov dword ptr[edi + 12], edx
}
#elif (ASMJIT_CC_GCC || ASMJIT_CC_CLANG) && ASMJIT_ARCH_X86
__asm__ __volatile__(
"mov %%ebx, %%edi\n"
"cpuid\n"
"xchg %%edi, %%ebx\n"
: "=a"(result->eax),
"=D"(result->ebx),
"=c"(result->ecx),
"=d"(result->edx)
: "a"(inEax),
"c"(inEcx)
);
#elif (ASMJIT_CC_GCC || ASMJIT_CC_CLANG) && ASMJIT_ARCH_X64
__asm__ __volatile__( \
"mov %%rbx, %%rdi\n"
"cpuid\n"
"xchg %%rdi, %%rbx\n"
: "=a"(result->eax),
"=D"(result->ebx),
"=c"(result->ecx),
"=d"(result->edx)
: "a"(inEax),
"c"(inEcx)
);
#else
# error "[asmjit] x86CallCpuid() - Unsupported compiler."
#endif
}
//! \internal
//!
//! Wrapper to call `xgetbv` instruction.
static void x86CallXGetBV(XGetBVResult* result, uint32_t inEcx) noexcept {
#if ASMJIT_CC_MSC_GE(16, 0, 40219) // 2010SP1+
uint64_t value = _xgetbv(inEcx);
result->eax = static_cast<uint32_t>(value & 0xFFFFFFFFU);
result->edx = static_cast<uint32_t>(value >> 32);
#elif ASMJIT_CC_GCC || ASMJIT_CC_CLANG
uint32_t outEax;
uint32_t outEdx;
// Replaced, because the world is not perfect:
// __asm__ __volatile__("xgetbv" : "=a"(outEax), "=d"(outEdx) : "c"(inEcx));
__asm__ __volatile__(".byte 0x0F, 0x01, 0xd0" : "=a"(outEax), "=d"(outEdx) : "c"(inEcx));
result->eax = outEax;
result->edx = outEdx;
#else
result->eax = 0;
result->edx = 0;
#endif
}
//! \internal
//!
//! Map a 12-byte vendor string returned by `cpuid` into a `CpuInfo::Vendor` ID.
static uint32_t x86GetCpuVendorID(const char* vendorString) noexcept {
struct VendorData {
uint32_t id;
char text[12];
};
static const VendorData vendorList[] = {
{ CpuInfo::kVendorIntel , { 'G', 'e', 'n', 'u', 'i', 'n', 'e', 'I', 'n', 't', 'e', 'l' } },
{ CpuInfo::kVendorAMD , { 'A', 'u', 't', 'h', 'e', 'n', 't', 'i', 'c', 'A', 'M', 'D' } },
{ CpuInfo::kVendorVIA , { 'V', 'I', 'A', 0 , 'V', 'I', 'A', 0 , 'V', 'I', 'A', 0 } },
{ CpuInfo::kVendorVIA , { 'C', 'e', 'n', 't', 'a', 'u', 'r', 'H', 'a', 'u', 'l', 's' } }
};
uint32_t dw0 = reinterpret_cast<const uint32_t*>(vendorString)[0];
uint32_t dw1 = reinterpret_cast<const uint32_t*>(vendorString)[1];
uint32_t dw2 = reinterpret_cast<const uint32_t*>(vendorString)[2];
for (uint32_t i = 0; i < ASMJIT_ARRAY_SIZE(vendorList); i++) {
if (dw0 == reinterpret_cast<const uint32_t*>(vendorList[i].text)[0] &&
dw1 == reinterpret_cast<const uint32_t*>(vendorList[i].text)[1] &&
dw2 == reinterpret_cast<const uint32_t*>(vendorList[i].text)[2])
return vendorList[i].id;
}
return CpuInfo::kVendorNone;
}
static ASMJIT_INLINE void x86SimplifyBrandString(char* s) noexcept {
// Used to always clear the current character to ensure that the result
// doesn't contain garbage after the new zero terminator.
char* d = s;
char prev = 0;
char curr = s[0];
s[0] = '\0';
for (;;) {
if (curr == 0)
break;
if (curr == ' ') {
if (prev == '@' || s[1] == ' ' || s[1] == '@')
goto L_Skip;
}
d[0] = curr;
d++;
prev = curr;
L_Skip:
curr = *++s;
s[0] = '\0';
}
d[0] = '\0';
}
static void x86DetectCpuInfo(CpuInfo* cpuInfo) noexcept {
uint32_t i, maxId;
CpuIdResult regs;
XGetBVResult xcr0 = { 0, 0 };
// Architecture is known at compile-time.
cpuInfo->setArch(ASMJIT_ARCH_X86 ? kArchX86 : kArchX64);
// --------------------------------------------------------------------------
// [CPUID EAX=0x0]
// --------------------------------------------------------------------------
// Get vendor string/id.
x86CallCpuId(&regs, 0x0);
maxId = regs.eax;
::memcpy(cpuInfo->_vendorString + 0, &regs.ebx, 4);
::memcpy(cpuInfo->_vendorString + 4, &regs.edx, 4);
::memcpy(cpuInfo->_vendorString + 8, &regs.ecx, 4);
cpuInfo->_vendorId = x86GetCpuVendorID(cpuInfo->_vendorString);
// --------------------------------------------------------------------------
// [CPUID EAX=0x1]
// --------------------------------------------------------------------------
if (maxId >= 0x1) {
// Get feature flags in ECX/EDX and family/model in EAX.
x86CallCpuId(&regs, 0x1);
// Fill family and model fields.
cpuInfo->_family = (regs.eax >> 8) & 0x0F;
cpuInfo->_model = (regs.eax >> 4) & 0x0F;
cpuInfo->_stepping = (regs.eax ) & 0x0F;
// Use extended family and model fields.
if (cpuInfo->_family == 0x0F) {
cpuInfo->_family += ((regs.eax >> 20) & 0xFF);
cpuInfo->_model += ((regs.eax >> 16) & 0x0F) << 4;
}
cpuInfo->_x86Data._processorType = ((regs.eax >> 12) & 0x03);
cpuInfo->_x86Data._brandIndex = ((regs.ebx ) & 0xFF);
cpuInfo->_x86Data._flushCacheLineSize = ((regs.ebx >> 8) & 0xFF) * 8;
cpuInfo->_x86Data._maxLogicalProcessors = ((regs.ebx >> 16) & 0xFF);
if (regs.ecx & 0x00000001U) cpuInfo->addFeature(CpuInfo::kX86FeatureSSE3);
if (regs.ecx & 0x00000002U) cpuInfo->addFeature(CpuInfo::kX86FeaturePCLMULQDQ);
if (regs.ecx & 0x00000008U) cpuInfo->addFeature(CpuInfo::kX86FeatureMONITOR);
if (regs.ecx & 0x00000200U) cpuInfo->addFeature(CpuInfo::kX86FeatureSSSE3);
if (regs.ecx & 0x00002000U) cpuInfo->addFeature(CpuInfo::kX86FeatureCMPXCHG16B);
if (regs.ecx & 0x00080000U) cpuInfo->addFeature(CpuInfo::kX86FeatureSSE4_1);
if (regs.ecx & 0x00100000U) cpuInfo->addFeature(CpuInfo::kX86FeatureSSE4_2);
if (regs.ecx & 0x00400000U) cpuInfo->addFeature(CpuInfo::kX86FeatureMOVBE);
if (regs.ecx & 0x00800000U) cpuInfo->addFeature(CpuInfo::kX86FeaturePOPCNT);
if (regs.ecx & 0x02000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAESNI);
if (regs.ecx & 0x04000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureXSAVE);
if (regs.ecx & 0x08000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureXSAVE_OS);
if (regs.ecx & 0x40000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureRDRAND);
if (regs.edx & 0x00000010U) cpuInfo->addFeature(CpuInfo::kX86FeatureRDTSC);
if (regs.edx & 0x00000100U) cpuInfo->addFeature(CpuInfo::kX86FeatureCMPXCHG8B);
if (regs.edx & 0x00008000U) cpuInfo->addFeature(CpuInfo::kX86FeatureCMOV);
if (regs.edx & 0x00080000U) cpuInfo->addFeature(CpuInfo::kX86FeatureCLFLUSH);
if (regs.edx & 0x00800000U) cpuInfo->addFeature(CpuInfo::kX86FeatureMMX);
if (regs.edx & 0x01000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureFXSR);
if (regs.edx & 0x02000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureSSE)
.addFeature(CpuInfo::kX86FeatureMMX2);
if (regs.edx & 0x04000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureSSE)
.addFeature(CpuInfo::kX86FeatureSSE2);
if (regs.edx & 0x10000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureMT);
// AMD sets multi-threading ON if it has two or more cores.
if (cpuInfo->_hwThreadsCount == 1 && cpuInfo->_vendorId == CpuInfo::kVendorAMD && (regs.edx & 0x10000000U))
cpuInfo->_hwThreadsCount = 2;
// Get the content of XCR0 if supported by CPU and enabled by OS.
if ((regs.ecx & 0x0C000000U) == 0x0C000000U)
x86CallXGetBV(&xcr0, 0);
// Detect AVX+.
if (regs.ecx & 0x10000000U) {
// - XCR0[2:1] == 11b
// XMM & YMM states need to be enabled by OS.
if ((xcr0.eax & 0x00000006U) == 0x00000006U) {
cpuInfo->addFeature(CpuInfo::kX86FeatureAVX);
if (regs.ecx & 0x00004000U) cpuInfo->addFeature(CpuInfo::kX86FeatureFMA3);
if (regs.ecx & 0x20000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureF16C);
}
}
}
// --------------------------------------------------------------------------
// [CPUID EAX=0x7 ECX=0x0]
// --------------------------------------------------------------------------
// Detect new features if the processor supports CPUID-07.
bool maybeMPX = false;
if (maxId >= 0x7) {
x86CallCpuId(&regs, 0x7);
if (regs.ebx & 0x00000001U) cpuInfo->addFeature(CpuInfo::kX86FeatureFSGSBASE);
if (regs.ebx & 0x00000008U) cpuInfo->addFeature(CpuInfo::kX86FeatureBMI);
if (regs.ebx & 0x00000010U) cpuInfo->addFeature(CpuInfo::kX86FeatureHLE);
if (regs.ebx & 0x00000080U) cpuInfo->addFeature(CpuInfo::kX86FeatureSMEP);
if (regs.ebx & 0x00000100U) cpuInfo->addFeature(CpuInfo::kX86FeatureBMI2);
if (regs.ebx & 0x00000200U) cpuInfo->addFeature(CpuInfo::kX86FeatureERMS);
if (regs.ebx & 0x00000800U) cpuInfo->addFeature(CpuInfo::kX86FeatureRTM);
if (regs.ebx & 0x00004000U) maybeMPX = true;
if (regs.ebx & 0x00040000U) cpuInfo->addFeature(CpuInfo::kX86FeatureRDSEED);
if (regs.ebx & 0x00080000U) cpuInfo->addFeature(CpuInfo::kX86FeatureADX);
if (regs.ebx & 0x00100000U) cpuInfo->addFeature(CpuInfo::kX86FeatureSMAP);
if (regs.ebx & 0x00400000U) cpuInfo->addFeature(CpuInfo::kX86FeaturePCOMMIT);
if (regs.ebx & 0x00800000U) cpuInfo->addFeature(CpuInfo::kX86FeatureCLFLUSH_OPT);
if (regs.ebx & 0x01000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureCLWB);
if (regs.ebx & 0x20000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureSHA);
if (regs.ecx & 0x00000001U) cpuInfo->addFeature(CpuInfo::kX86FeaturePREFETCHWT1);
// Detect AVX2.
if (cpuInfo->hasFeature(CpuInfo::kX86FeatureAVX))
if (regs.ebx & 0x00000020U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX2);
// Detect AVX-512+.
if (regs.ebx & 0x00010000U) {
// - XCR0[2:1] == 11b
// XMM/YMM states need to be enabled by OS.
// - XCR0[7:5] == 111b
// Upper 256-bit of ZMM0-XMM15 and ZMM16-ZMM31 need to be enabled by the OS.
if ((xcr0.eax & 0x000000E6U) == 0x000000E6U) {
cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512F);
if (regs.ebx & 0x00020000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512DQ);
if (regs.ebx & 0x00200000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512IFMA);
if (regs.ebx & 0x04000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512PF);
if (regs.ebx & 0x08000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512ER);
if (regs.ebx & 0x10000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512CD);
if (regs.ebx & 0x40000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512BW);
if (regs.ebx & 0x80000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512VL);
if (regs.ecx & 0x00000002U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512VBMI);
}
}
}
// --------------------------------------------------------------------------
// [CPUID EAX=0xD, ECX=0x0]
// --------------------------------------------------------------------------
if (maxId >= 0xD && maybeMPX) {
x86CallCpuId(&regs, 0xD);
// Both CPUID result and XCR0 has to be enabled to have support for MPX.
if (((regs.eax & xcr0.eax) & 0x00000018U) == 0x00000018U) {
cpuInfo->addFeature(CpuInfo::kX86FeatureMPX);
}
}
// --------------------------------------------------------------------------
// [CPUID EAX=0x80000000...maxId]
// --------------------------------------------------------------------------
// Several CPUID calls are required to get the whole branc string. It's easy
// to copy one DWORD at a time instead of performing a byte copy.
uint32_t* brand = reinterpret_cast<uint32_t*>(cpuInfo->_brandString);
i = maxId = 0x80000000U;
do {
x86CallCpuId(&regs, i);
switch (i) {
case 0x80000000U:
maxId = Utils::iMin<uint32_t>(regs.eax, 0x80000004);
break;
case 0x80000001U:
if (regs.ecx & 0x00000001U) cpuInfo->addFeature(CpuInfo::kX86FeatureLAHF_SAHF);
if (regs.ecx & 0x00000020U) cpuInfo->addFeature(CpuInfo::kX86FeatureLZCNT);
if (regs.ecx & 0x00000040U) cpuInfo->addFeature(CpuInfo::kX86FeatureSSE4A);
if (regs.ecx & 0x00000080U) cpuInfo->addFeature(CpuInfo::kX86FeatureMSSE);
if (regs.ecx & 0x00000100U) cpuInfo->addFeature(CpuInfo::kX86FeaturePREFETCH);
if (regs.ecx & 0x00200000U) cpuInfo->addFeature(CpuInfo::kX86FeatureTBM);
if (regs.edx & 0x00100000U) cpuInfo->addFeature(CpuInfo::kX86FeatureNX);
if (regs.edx & 0x00200000U) cpuInfo->addFeature(CpuInfo::kX86FeatureFXSR_OPT);
if (regs.edx & 0x00400000U) cpuInfo->addFeature(CpuInfo::kX86FeatureMMX2);
if (regs.edx & 0x08000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureRDTSCP);
if (regs.edx & 0x40000000U) cpuInfo->addFeature(CpuInfo::kX86Feature3DNOW2)
.addFeature(CpuInfo::kX86FeatureMMX2);
if (regs.edx & 0x80000000U) cpuInfo->addFeature(CpuInfo::kX86Feature3DNOW);
if (cpuInfo->hasFeature(CpuInfo::kX86FeatureAVX)) {
if (regs.ecx & 0x00000800U) cpuInfo->addFeature(CpuInfo::kX86FeatureXOP);
if (regs.ecx & 0x00010000U) cpuInfo->addFeature(CpuInfo::kX86FeatureFMA4);
}
break;
case 0x80000002U:
case 0x80000003U:
case 0x80000004U:
*brand++ = regs.eax;
*brand++ = regs.ebx;
*brand++ = regs.ecx;
*brand++ = regs.edx;
break;
default:
// Stop the loop, additional features can be detected in the future.
i = maxId;
break;
}
} while (i++ < maxId);
// Simplify CPU brand string by removing unnecessary spaces.
x86SimplifyBrandString(cpuInfo->_brandString);
}
#endif // ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64
// ============================================================================
// [asmjit::CpuInfo - Detect - HWThreadsCount]
// ============================================================================
static uint32_t cpuDetectHWThreadsCount() noexcept {
#if ASMJIT_OS_WINDOWS
SYSTEM_INFO info;
::GetSystemInfo(&info);
return info.dwNumberOfProcessors;
#elif ASMJIT_OS_POSIX && defined(_SC_NPROCESSORS_ONLN)
long res = ::sysconf(_SC_NPROCESSORS_ONLN);
if (res <= 0) return 1;
return static_cast<uint32_t>(res);
#else
return 1;
#endif
}
// ============================================================================
// [asmjit::CpuInfo - Detect]
// ============================================================================
void CpuInfo::detect() noexcept {
reset();
// Detect the number of hardware threads available.
_hwThreadsCount = cpuDetectHWThreadsCount();
#if ASMJIT_ARCH_ARM32 || ASMJIT_ARCH_ARM64
armDetectCpuInfo(this);
#endif // ASMJIT_ARCH_ARM32 || ASMJIT_ARCH_ARM64
#if ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64
x86DetectCpuInfo(this);
#endif // ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64
}
// ============================================================================
// [asmjit::CpuInfo - GetHost]
// ============================================================================
struct HostCpuInfo : public CpuInfo {
ASMJIT_INLINE HostCpuInfo() noexcept : CpuInfo() { detect(); }
};
const CpuInfo& CpuInfo::getHost() noexcept {
static HostCpuInfo host;
return host;
}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
+316
View File
@@ -0,0 +1,316 @@
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Guard]
#ifndef _ASMJIT_BASE_CPUINFO_H
#define _ASMJIT_BASE_CPUINFO_H
// [Dependencies]
#include "../base/globals.h"
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
//! \addtogroup asmjit_base
//! \{
// ============================================================================
// [asmjit::CpuInfo]
// ============================================================================
//! CPU information.
class CpuInfo {
public:
// --------------------------------------------------------------------------
// [Vendor]
// --------------------------------------------------------------------------
//! CPU vendor ID.
ASMJIT_ENUM(Vendor) {
kVendorNone = 0, //!< Generic or unknown.
kVendorIntel = 1, //!< Intel vendor.
kVendorAMD = 2, //!< AMD vendor.
kVendorVIA = 3 //!< VIA vendor.
};
// --------------------------------------------------------------------------
// [ArmFeatures]
// --------------------------------------------------------------------------
//! ARM/ARM64 CPU features.
ASMJIT_ENUM(ArmFeatures) {
kArmFeatureV6, //!< ARMv6 instruction set.
kArmFeatureV7, //!< ARMv7 instruction set.
kArmFeatureV8, //!< ARMv8 instruction set.
kArmFeatureTHUMB, //!< CPU provides THUMB v1 instruction set (ARM only).
kArmFeatureTHUMB2, //!< CPU provides THUMB v2 instruction set (ARM only).
kArmFeatureVFP2, //!< CPU provides VFPv2 instruction set.
kArmFeatureVFP3, //!< CPU provides VFPv3 instruction set.
kArmFeatureVFP4, //!< CPU provides VFPv4 instruction set.
kArmFeatureVFP_D32, //!< CPU provides 32 VFP-D (64-bit) registers.
kArmFeatureNEON, //!< CPU provides NEON instruction set.
kArmFeatureDSP, //!< CPU provides DSP extensions.
kArmFeatureIDIV, //!< CPU provides hardware support for SDIV and UDIV.
kArmFeatureAES, //!< CPU provides AES instructions (ARM64 only).
kArmFeatureCRC32, //!< CPU provides CRC32 instructions (ARM64 only).
kArmFeaturePMULL, //!< CPU provides PMULL instructions (ARM64 only).
kArmFeatureSHA1, //!< CPU provides SHA1 instructions (ARM64 only).
kArmFeatureSHA256, //!< CPU provides SHA256 instructions (ARM64 only).
kArmFeatureAtomics64, //!< CPU provides 64-bit load/store atomics (ARM64 only).
kArmFeaturesCount //!< Count of ARM/ARM64 CPU features.
};
// --------------------------------------------------------------------------
// [X86Features]
// --------------------------------------------------------------------------
//! X86/X64 CPU features.
ASMJIT_ENUM(X86Features) {
kX86FeatureNX = 0, //!< CPU has Not-Execute-Bit.
kX86FeatureMT, //!< CPU has multi-threading.
kX86FeatureRDTSC, //!< CPU has RDTSC.
kX86FeatureRDTSCP, //!< CPU has RDTSCP.
kX86FeatureCMOV, //!< CPU has CMOV.
kX86FeatureCMPXCHG8B, //!< CPU has CMPXCHG8B.
kX86FeatureCMPXCHG16B, //!< CPU has CMPXCHG16B (x64).
kX86FeatureCLFLUSH, //!< CPU has CLFUSH.
kX86FeatureCLFLUSH_OPT, //!< CPU has CLFUSH (optimized).
kX86FeatureCLWB, //!< CPU has CLWB.
kX86FeaturePCOMMIT, //!< CPU has PCOMMIT.
kX86FeaturePREFETCH, //!< CPU has PREFETCH.
kX86FeaturePREFETCHWT1, //!< CPU has PREFETCHWT1.
kX86FeatureLAHF_SAHF, //!< CPU has LAHF/SAHF.
kX86FeatureFXSR, //!< CPU has FXSAVE/FXRSTOR.
kX86FeatureFXSR_OPT, //!< CPU has FXSAVE/FXRSTOR (optimized).
kX86FeatureMMX, //!< CPU has MMX.
kX86FeatureMMX2, //!< CPU has extended MMX.
kX86Feature3DNOW, //!< CPU has 3dNow!
kX86Feature3DNOW2, //!< CPU has enhanced 3dNow!
kX86FeatureSSE, //!< CPU has SSE.
kX86FeatureSSE2, //!< CPU has SSE2.
kX86FeatureSSE3, //!< CPU has SSE3.
kX86FeatureSSSE3, //!< CPU has SSSE3.
kX86FeatureSSE4A, //!< CPU has SSE4.A.
kX86FeatureSSE4_1, //!< CPU has SSE4.1.
kX86FeatureSSE4_2, //!< CPU has SSE4.2.
kX86FeatureMSSE, //!< CPU has Misaligned SSE (MSSE).
kX86FeatureMONITOR, //!< CPU has MONITOR and MWAIT.
kX86FeatureMOVBE, //!< CPU has MOVBE.
kX86FeaturePOPCNT, //!< CPU has POPCNT.
kX86FeatureLZCNT, //!< CPU has LZCNT.
kX86FeatureAESNI, //!< CPU has AESNI.
kX86FeaturePCLMULQDQ, //!< CPU has PCLMULQDQ.
kX86FeatureRDRAND, //!< CPU has RDRAND.
kX86FeatureRDSEED, //!< CPU has RDSEED.
kX86FeatureSMAP, //!< CPU has SMAP (supervisor-mode access prevention).
kX86FeatureSMEP, //!< CPU has SMEP (supervisor-mode execution prevention).
kX86FeatureSHA, //!< CPU has SHA-1 and SHA-256.
kX86FeatureXSAVE, //!< CPU has XSAVE support - XSAVE/XRSTOR, XSETBV/XGETBV, and XCR0.
kX86FeatureXSAVE_OS, //!< OS has enabled XSAVE, you can call XGETBV to get value of XCR0.
kX86FeatureAVX, //!< CPU has AVX.
kX86FeatureAVX2, //!< CPU has AVX2.
kX86FeatureF16C, //!< CPU has F16C.
kX86FeatureFMA3, //!< CPU has FMA3.
kX86FeatureFMA4, //!< CPU has FMA4.
kX86FeatureXOP, //!< CPU has XOP.
kX86FeatureBMI, //!< CPU has BMI (bit manipulation instructions #1).
kX86FeatureBMI2, //!< CPU has BMI2 (bit manipulation instructions #2).
kX86FeatureADX, //!< CPU has ADX (multi-precision add-carry instruction extensions).
kX86FeatureTBM, //!< CPU has TBM (trailing bit manipulation).
kX86FeatureMPX, //!< CPU has MPX (memory protection extensions).
kX86FeatureHLE, //!< CPU has HLE.
kX86FeatureRTM, //!< CPU has RTM.
kX86FeatureERMS, //!< CPU has ERMS (enhanced REP MOVSB/STOSB).
kX86FeatureFSGSBASE, //!< CPU has FSGSBASE.
kX86FeatureAVX512F, //!< CPU has AVX-512F (foundation).
kX86FeatureAVX512CD, //!< CPU has AVX-512CD (conflict detection).
kX86FeatureAVX512PF, //!< CPU has AVX-512PF (prefetch instructions).
kX86FeatureAVX512ER, //!< CPU has AVX-512ER (exponential and reciprocal instructions).
kX86FeatureAVX512DQ, //!< CPU has AVX-512DQ (DWORD/QWORD).
kX86FeatureAVX512BW, //!< CPU has AVX-512BW (BYTE/WORD).
kX86FeatureAVX512VL, //!< CPU has AVX VL (vector length extensions).
kX86FeatureAVX512IFMA, //!< CPU has AVX IFMA (integer fused multiply add using 52-bit precision).
kX86FeatureAVX512VBMI, //!< CPU has AVX VBMI (vector byte manipulation instructions).
kX86FeaturesCount //!< Count of X86/X64 CPU features.
};
// --------------------------------------------------------------------------
// [Other]
// --------------------------------------------------------------------------
//! \internal
enum {
kFeaturesPerUInt32 = static_cast<int>(sizeof(uint32_t)) * 8
};
// --------------------------------------------------------------------------
// [ArmInfo]
// --------------------------------------------------------------------------
struct ArmData {
};
// --------------------------------------------------------------------------
// [X86Info]
// --------------------------------------------------------------------------
struct X86Data {
uint32_t _processorType; //!< Processor type.
uint32_t _brandIndex; //!< Brand index.
uint32_t _flushCacheLineSize; //!< Flush cache line size (in bytes).
uint32_t _maxLogicalProcessors; //!< Maximum number of addressable IDs for logical processors.
};
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
ASMJIT_INLINE CpuInfo() noexcept { reset(); }
// --------------------------------------------------------------------------
// [Reset]
// --------------------------------------------------------------------------
ASMJIT_INLINE void reset() noexcept { ::memset(this, 0, sizeof(CpuInfo)); }
// --------------------------------------------------------------------------
// [Detect]
// --------------------------------------------------------------------------
ASMJIT_API void detect() noexcept;
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Get CPU architecture, see \Arch.
ASMJIT_INLINE uint32_t getArch() const noexcept { return _arch; }
//! Set CPU architecture, see \Arch.
ASMJIT_INLINE void setArch(uint32_t arch) noexcept { _arch = static_cast<uint8_t>(arch); }
//! Get CPU vendor string.
ASMJIT_INLINE const char* getVendorString() const noexcept { return _vendorString; }
//! Get CPU brand string.
ASMJIT_INLINE const char* getBrandString() const noexcept { return _brandString; }
//! Get CPU vendor ID.
ASMJIT_INLINE uint32_t getVendorId() const noexcept { return _vendorId; }
//! Get CPU family ID.
ASMJIT_INLINE uint32_t getFamily() const noexcept { return _family; }
//! Get CPU model ID.
ASMJIT_INLINE uint32_t getModel() const noexcept { return _model; }
//! Get CPU stepping.
ASMJIT_INLINE uint32_t getStepping() const noexcept { return _stepping; }
//! Get number of hardware threads available.
ASMJIT_INLINE uint32_t getHwThreadsCount() const noexcept {
return _hwThreadsCount;
}
//! Get whether CPU has a `feature`.
ASMJIT_INLINE bool hasFeature(uint32_t feature) const noexcept {
ASMJIT_ASSERT(feature < sizeof(_features) * 8);
uint32_t pos = feature / kFeaturesPerUInt32;
uint32_t bit = feature % kFeaturesPerUInt32;
return static_cast<bool>((_features[pos] >> bit) & 0x1);
}
//! Add a CPU `feature`.
ASMJIT_INLINE CpuInfo& addFeature(uint32_t feature) noexcept {
ASMJIT_ASSERT(feature < sizeof(_features) * 8);
uint32_t pos = feature / kFeaturesPerUInt32;
uint32_t bit = feature % kFeaturesPerUInt32;
_features[pos] |= static_cast<uint32_t>(1) << bit;
return *this;
}
// --------------------------------------------------------------------------
// [Accessors - ARM]
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
// [Accessors - X86]
// --------------------------------------------------------------------------
//! Get processor type.
ASMJIT_INLINE uint32_t getX86ProcessorType() const noexcept {
return _x86Data._processorType;
}
//! Get brand index.
ASMJIT_INLINE uint32_t getX86BrandIndex() const noexcept {
return _x86Data._brandIndex;
}
//! Get flush cache line size.
ASMJIT_INLINE uint32_t getX86FlushCacheLineSize() const noexcept {
return _x86Data._flushCacheLineSize;
}
//! Get maximum logical processors count.
ASMJIT_INLINE uint32_t getX86MaxLogicalProcessors() const noexcept {
return _x86Data._maxLogicalProcessors;
}
// --------------------------------------------------------------------------
// [Statics]
// --------------------------------------------------------------------------
//! Get the host CPU information.
static ASMJIT_API const CpuInfo& getHost() noexcept;
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! CPU vendor string.
char _vendorString[16];
//! CPU brand string.
char _brandString[64];
//! CPU architecture, see \ref Arch.
uint8_t _arch;
//! \internal
uint8_t _reserved[3];
//! CPU vendor id, see \ref CpuVendor.
uint32_t _vendorId;
//! CPU family ID.
uint32_t _family;
//! CPU model ID.
uint32_t _model;
//! CPU stepping.
uint32_t _stepping;
//! Number of hardware threads.
uint32_t _hwThreadsCount;
//! CPU features (bit-array).
uint32_t _features[8];
// Architecture specific data.
union {
ArmData _armData;
X86Data _x86Data;
};
};
//! \}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
// [Guard]
#endif // _ASMJIT_BASE_CPUINFO_H
+94
View File
@@ -0,0 +1,94 @@
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Export]
#define ASMJIT_EXPORTS
// [Dependencies]
#include "../base/globals.h"
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
// ============================================================================
// [asmjit::DebugUtils]
// ============================================================================
#if !defined(ASMJIT_DISABLE_TEXT)
static const char errorMessages[] = {
"Ok\0"
"No heap memory\0"
"No virtual memory\0"
"Invalid argument\0"
"Invalid state\0"
"Invalid architecture\0"
"Not initialized\0"
"No code generated\0"
"Code too large\0"
"Label already bound\0"
"Unknown instruction\0"
"Illegal instruction\0"
"Illegal addressing\0"
"Illegal displacement\0"
"Overlapped arguments\0"
"Unknown error\0"
};
static const char* findPackedString(const char* p, uint32_t id, uint32_t maxId) noexcept {
uint32_t i = 0;
if (id > maxId)
id = maxId;
while (i < id) {
while (p[0])
p++;
p++;
i++;
}
return p;
}
#endif // ASMJIT_DISABLE_TEXT
const char* DebugUtils::errorAsString(Error err) noexcept {
#if !defined(ASMJIT_DISABLE_TEXT)
return findPackedString(errorMessages, err, kErrorCount);
#else
static const char noMessage[] = "";
return noMessage;
#endif
}
void DebugUtils::debugOutput(const char* str) noexcept {
#if ASMJIT_OS_WINDOWS
::OutputDebugStringA(str);
#else
::fputs(str, stderr);
#endif
}
void DebugUtils::assertionFailed(const char* file, int line, const char* msg) noexcept {
char str[1024];
snprintf(str, 1024,
"[asmjit] Assertion failed at %s (line %d):\n"
"[asmjit] %s\n", file, line, msg);
// Support buggy `snprintf` implementations.
str[1023] = '\0';
debugOutput(str);
::abort();
}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
+666
View File
@@ -0,0 +1,666 @@
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Guard]
#ifndef _ASMJIT_BASE_GLOBALS_H
#define _ASMJIT_BASE_GLOBALS_H
// [Dependencies]
#include "../build.h"
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
//! \addtogroup asmjit_base
//! \{
// ============================================================================
// [asmjit::TypeDefs]
// ============================================================================
//! AsmJit error core (unsigned integer).
typedef uint32_t Error;
//! 64-bit unsigned pointer, compatible with JIT and non-JIT generators.
//!
//! This is the preferred pointer type to use with AsmJit library. It has a
//! capability to hold any pointer for any architecture making it an ideal
//! candidate for a cross-platform code generator.
typedef uint64_t Ptr;
//! like \ref Ptr, but signed.
typedef int64_t SignedPtr;
// ============================================================================
// [asmjit::GlobalDefs]
// ============================================================================
//! Invalid index
//!
//! Invalid index is the last possible index that is never used in practice. In
//! AsmJit it is used exclusively with strings to indicate the the length of the
//! string is not known and has to be determined.
static const size_t kInvalidIndex = ~static_cast<size_t>(0);
//! Invalid base address.
static const Ptr kNoBaseAddress = static_cast<Ptr>(static_cast<SignedPtr>(-1));
//! Global constants.
ASMJIT_ENUM(GlobalDefs) {
//! Invalid value or operand id.
kInvalidValue = 0xFFFFFFFF,
//! Invalid register index.
kInvalidReg = 0xFF,
//! Invalid variable type.
kInvalidVar = 0xFF,
//! Host memory allocator overhead.
//!
//! The overhead is decremented from all zone allocators so the operating
//! system doesn't have to allocate one extra virtual page to keep tract of
//! the requested memory block.
//!
//! The number is actually a guess.
kMemAllocOverhead = sizeof(intptr_t) * 4,
//! Memory grow threshold.
//!
//! After the grow threshold is reached the capacity won't be doubled
//! anymore.
kMemAllocGrowMax = 8192 * 1024
};
// ============================================================================
// [asmjit::ArchId]
// ============================================================================
//! CPU architecture identifier.
ASMJIT_ENUM(ArchId) {
//! No/Unknown architecture.
kArchNone = 0,
//! X86 architecture (32-bit).
kArchX86 = 1,
//! X64 architecture (64-bit), also called AMD64.
kArchX64 = 2,
//! X32 architecture (64-bit with 32-bit pointers) (NOT USED ATM).
kArchX32 = 3,
//! Arm architecture (32-bit).
kArchArm32 = 4,
//! Arm64 architecture (64-bit).
kArchArm64 = 5,
#if ASMJIT_ARCH_X86
kArchHost = kArchX86
#elif ASMJIT_ARCH_X64
kArchHost = kArchX64
#elif ASMJIT_ARCH_ARM32
kArchHost = kArchArm32
#elif ASMJIT_ARCH_ARM64
kArchHost = kArchArm64
#else
# error "[asmjit] Unsupported host architecture."
#endif
};
// ============================================================================
// [asmjit::CallConv]
// ============================================================================
//! Function calling convention.
//!
//! Calling convention is a scheme that defines how function arguments are
//! passed and how the return value handled. In assembler programming it's
//! always needed to comply with function calling conventions, because even
//! small inconsistency can cause undefined behavior or application's crash.
//!
//! Platform Independent Conventions
//! --------------------------------
//!
//! - `kCallConvHost` - Should match the current C++ compiler native calling
//! convention.
//!
//! X86/X64 Specific Conventions
//! ----------------------------
//!
//! List of calling conventions for 32-bit x86 mode:
//! - `kCallConvX86CDecl` - Calling convention for C runtime.
//! - `kCallConvX86StdCall` - Calling convention for WinAPI functions.
//! - `kCallConvX86MsThisCall` - Calling convention for C++ members under
//! Windows (produced by MSVC and all MSVC compatible compilers).
//! - `kCallConvX86MsFastCall` - Fastest calling convention that can be used
//! by MSVC compiler.
//! - `kCallConvX86BorlandFastCall` - Borland fastcall convention.
//! - `kCallConvX86GccFastCall` - GCC fastcall convention (2 register arguments).
//! - `kCallConvX86GccRegParm1` - GCC regparm(1) convention.
//! - `kCallConvX86GccRegParm2` - GCC regparm(2) convention.
//! - `kCallConvX86GccRegParm3` - GCC regparm(3) convention.
//!
//! List of calling conventions for 64-bit x86 mode (x64):
//! - `kCallConvX64Win` - Windows 64-bit calling convention (WIN64 ABI).
//! - `kCallConvX64Unix` - Unix 64-bit calling convention (AMD64 ABI).
//!
//! ARM Specific Conventions
//! ------------------------
//!
//! List of ARM calling conventions:
//! - `kCallConvArm32SoftFP` - Legacy calling convention, floating point
//! arguments are passed via GP registers.
//! - `kCallConvArm32HardFP` - Modern calling convention, uses VFP registers
//! to pass floating point arguments.
ASMJIT_ENUM(CallConv) {
//! Calling convention is invalid (can't be used).
kCallConvNone = 0,
// --------------------------------------------------------------------------
// [X86]
// --------------------------------------------------------------------------
//! X86 `__cdecl` calling convention (used by C runtime and libraries).
//!
//! Compatible across MSVC and GCC.
//!
//! Arguments direction:
//! - Right to left.
//!
//! Stack is cleaned by:
//! - Caller.
//!
//! Return value:
//! - Integer types - `eax:edx` registers.
//! - Floating point - `fp0` register.
kCallConvX86CDecl = 1,
//! X86 `__stdcall` calling convention (used mostly by WinAPI).
//!
//! Compatible across MSVC and GCC.
//!
//! Arguments direction:
//! - Right to left.
//!
//! Stack is cleaned by:
//! - Callee.
//!
//! Return value:
//! - Integer types - `eax:edx` registers.
//! - Floating point - `fp0` register.
kCallConvX86StdCall = 2,
//! X86 `__thiscall` calling convention (MSVC/Intel specific).
//!
//! This is MSVC (and Intel) specific calling convention used when targeting
//! Windows platform for C++ class methods. Implicit `this` pointer (defined
//! as the first argument) is stored in `ecx` register instead of storing it
//! on the stack.
//!
//! This calling convention is implicitly used by MSVC for class functions.
//!
//! C++ class functions that have variable number of arguments use `__cdecl`
//! calling convention instead.
//!
//! Arguments direction:
//! - Right to left (except for the first argument passed in `ecx`).
//!
//! Stack is cleaned by:
//! - Callee.
//!
//! Return value:
//! - Integer types - `eax:edx` registers.
//! - Floating point - `fp0` register.
kCallConvX86MsThisCall = 3,
//! X86 `__fastcall` convention (MSVC/Intel specific).
//!
//! The first two arguments (evaluated from the left to the right) are passed
//! in `ecx` and `edx` registers, all others on the stack from the right to
//! the left.
//!
//! Arguments direction:
//! - Right to left (except for the first two integers passed in `ecx` and `edx`).
//!
//! Stack is cleaned by:
//! - Callee.
//!
//! Return value:
//! - Integer types - `eax:edx` registers.
//! - Floating point - `fp0` register.
//!
//! NOTE: This calling convention differs from GCC's one.
kCallConvX86MsFastCall = 4,
//! X86 `__fastcall` convention (Borland specific).
//!
//! The first two arguments (evaluated from the left to the right) are passed
//! in `ecx` and `edx` registers, all others on the stack from the left to
//! the right.
//!
//! Arguments direction:
//! - Left to right (except for the first two integers passed in `ecx` and `edx`).
//!
//! Stack is cleaned by:
//! - Callee.
//!
//! Return value:
//! - Integer types - `eax:edx` registers.
//! - Floating point - `fp0` register.
//!
//! NOTE: Arguments on the stack are in passed in left to right order, which
//! is really Borland specific, all other `__fastcall` calling conventions
//! use right to left order.
kCallConvX86BorlandFastCall = 5,
//! X86 `__fastcall` convention (GCC specific).
//!
//! The first two arguments (evaluated from the left to the right) are passed
//! in `ecx` and `edx` registers, all others on the stack from the right to
//! the left.
//!
//! Arguments direction:
//! - Right to left (except for the first two integers passed in `ecx` and `edx`).
//!
//! Stack is cleaned by:
//! - Callee.
//!
//! Return value:
//! - Integer types - `eax:edx` registers.
//! - Floating point - `fp0` register.
//!
//! NOTE: This calling convention should be compatible with `kCallConvX86MsFastCall`.
kCallConvX86GccFastCall = 6,
//! X86 `regparm(1)` convention (GCC specific).
//!
//! The first argument (evaluated from the left to the right) is passed in
//! `eax` register, all others on the stack from the right to the left.
//!
//! Arguments direction:
//! - Right to left (except for the first integer passed in `eax`).
//!
//! Stack is cleaned by:
//! - Caller.
//!
//! Return value:
//! - Integer types - `eax:edx` registers.
//! - Floating point - `fp0` register.
kCallConvX86GccRegParm1 = 7,
//! X86 `regparm(2)` convention (GCC specific).
//!
//! The first two arguments (evaluated from the left to the right) are passed
//! in `ecx` and `edx` registers, all others on the stack from the right to
//! the left.
//!
//! Arguments direction:
//! - Right to left (except for the first two integers passed in `ecx` and `edx`).
//!
//! Stack is cleaned by:
//! - Caller.
//!
//! Return value:
//! - Integer types - `eax:edx` registers.
//! - Floating point - `fp0` register.
kCallConvX86GccRegParm2 = 8,
//! X86 `regparm(3)` convention (GCC specific).
//!
//! Three first parameters (evaluated from left-to-right) are in
//! EAX:EDX:ECX registers, all others on the stack in right-to-left direction.
//!
//! Arguments direction:
//! - Right to left (except for the first three integers passed in `ecx`,
//! `edx`, and `ecx`).
//!
//! Stack is cleaned by:
//! - Caller.
//!
//! Return value:
//! - Integer types - `eax:edx` registers.
//! - Floating point - `fp0` register.
kCallConvX86GccRegParm3 = 9,
// --------------------------------------------------------------------------
// [X64]
// --------------------------------------------------------------------------
//! X64 calling convention used by Windows platform (WIN64-ABI).
//!
//! The first 4 arguments are passed in the following registers:
//! - 1. 32/64-bit integer in `rcx` and floating point argument in `xmm0`
//! - 2. 32/64-bit integer in `rdx` and floating point argument in `xmm1`
//! - 3. 32/64-bit integer in `r8` and floating point argument in `xmm2`
//! - 4. 32/64-bit integer in `r9` and floating point argument in `xmm3`
//!
//! If one or more argument from the first four doesn't match the list above
//! it is simply skipped. WIN64-ABI is very specific about this.
//!
//! All other arguments are pushed on the stack from the right to the left.
//! Stack has to be aligned by 16 bytes, always. There is also a 32-byte
//! shadow space on the stack that can be used to save up to four 64-bit
//! registers.
//!
//! Arguments direction:
//! - Right to left (except for all parameters passed in registers).
//!
//! Stack cleaned by:
//! - Caller.
//!
//! Return value:
//! - Integer types - `rax`.
//! - Floating point - `xmm0`.
//!
//! Stack is always aligned to 16 bytes.
//!
//! More information about this calling convention can be found on MSDN
//! <http://msdn.microsoft.com/en-us/library/9b372w95.aspx>.
kCallConvX64Win = 10,
//! X64 calling convention used by Unix platforms (AMD64-ABI).
//!
//! First six 32 or 64-bit integer arguments are passed in `rdi`, `rsi`,
//! `rdx`, `rcx`, `r8`, and `r9` registers. First eight floating point or xmm
//! arguments are passed in `xmm0`, `xmm1`, `xmm2`, `xmm3`, `xmm4`, `xmm5`,
//! `xmm6`, and `xmm7` registers.
//!
//! There is also a red zene below the stack pointer that can be used by the
//! function. The red zone is typically from [rsp-128] to [rsp-8], however,
//! red zone can also be disabled.
//!
//! Arguments direction:
//! - Right to left (except for all arguments passed in registers).
//!
//! Stack cleaned by:
//! - Caller.
//!
//! Return value:
//! - Integer types - `rax`.
//! - Floating point - `xmm0`.
//!
//! Stack is always aligned to 16 bytes.
kCallConvX64Unix = 11,
// --------------------------------------------------------------------------
// [ARM]
// --------------------------------------------------------------------------
kCallConvArm32SoftFP = 16,
kCallConvArm32HardFP = 17,
// --------------------------------------------------------------------------
// [Internal]
// --------------------------------------------------------------------------
//! \internal
_kCallConvX86Start = 1,
//! \internal
_kCallConvX86End = 9,
//! \internal
_kCallConvX64Start = 10,
//! \internal
_kCallConvX64End = 11,
//! \internal
_kCallConvArmStart = 16,
//! \internal
_kCallConvArmEnd = 17,
// --------------------------------------------------------------------------
// [Host]
// --------------------------------------------------------------------------
#if defined(ASMJIT_DOCGEN)
//! Default calling convention based on the current compiler's settings.
//!
//! NOTE: This should be always the same as `kCallConvHostCDecl`, but some
//! compilers allow to override the default calling convention. Overriding
//! is not detected at the moment.
kCallConvHost = DETECTED_AT_COMPILE_TIME,
//! Default C calling convention based on the current compiler's settings.
kCallConvHostCDecl = DETECTED_AT_COMPILE_TIME,
//! Compatibility for `__stdcall` calling convention.
//!
//! NOTE: This enumeration is always set to a value which is compatible with
//! the current compiler's `__stdcall` calling convention. In 64-bit mode
//! there is no such convention and the value is mapped to `kCallConvX64Win`
//! or `kCallConvX64Unix`, depending on the host architecture.
kCallConvHostStdCall = DETECTED_AT_COMPILE_TIME,
//! Compatibility for `__fastcall` calling convention.
//!
//! NOTE: This enumeration is always set to a value which is compatible with
//! the current compiler's `__fastcall` calling convention. In 64-bit mode
//! there is no such convention and the value is mapped to `kCallConvX64Win`
//! or `kCallConvX64Unix`, depending on the host architecture.
kCallConvHostFastCall = DETECTED_AT_COMPILE_TIME
#elif ASMJIT_ARCH_X86
// X86 Host Support.
kCallConvHost = kCallConvX86CDecl,
kCallConvHostCDecl = kCallConvX86CDecl,
kCallConvHostStdCall = kCallConvX86StdCall,
kCallConvHostFastCall =
ASMJIT_CC_MSC ? kCallConvX86MsFastCall :
ASMJIT_CC_GCC ? kCallConvX86GccFastCall :
ASMJIT_CC_CLANG ? kCallConvX86GccFastCall :
ASMJIT_CC_CODEGEAR ? kCallConvX86BorlandFastCall : kCallConvNone
#elif ASMJIT_ARCH_X64
// X64 Host Support.
kCallConvHost = ASMJIT_OS_WINDOWS ? kCallConvX64Win : kCallConvX64Unix,
// These don't exist in 64-bit mode.
kCallConvHostCDecl = kCallConvHost,
kCallConvHostStdCall = kCallConvHost,
kCallConvHostFastCall = kCallConvHost
#elif ASMJIT_ARCH_ARM32
# if defined(__SOFTFP__)
kCallConvHost = kCallConvArm32SoftFP,
# else
kCallConvHost = kCallConvArm32HardFP,
# endif
// These don't exist on ARM.
kCallConvHostCDecl = kCallConvHost,
kCallConvHostStdCall = kCallConvHost,
kCallConvHostFastCall = kCallConvHost
#else
# error "[asmjit] Couldn't determine the target's calling convention."
#endif
};
// ============================================================================
// [asmjit::ErrorCode]
// ============================================================================
//! AsmJit error codes.
ASMJIT_ENUM(ErrorCode) {
//! No error (success).
//!
//! This is default state and state you want.
kErrorOk = 0,
//! Heap memory allocation failed.
kErrorNoHeapMemory,
//! Virtual memory allocation failed.
kErrorNoVirtualMemory,
//! Invalid argument.
kErrorInvalidArgument,
//! Invalid state.
kErrorInvalidState,
//! Invalid architecture.
kErrorInvalidArch,
//! The object is not initialized.
kErrorNotInitialized,
//! No code generated.
//!
//! Returned by runtime if the code-generator contains no code.
kErrorNoCodeGenerated,
//! Code generated is too large to fit in memory reserved.
//!
//! Returned by `StaticRuntime` in case that the code generated is too large
//! to fit in the memory already reserved for it.
kErrorCodeTooLarge,
//! Label is already bound.
kErrorLabelAlreadyBound,
//! Unknown instruction (an instruction ID is out of bounds or instruction
//! name is invalid).
kErrorUnknownInst,
//! Illegal instruction.
//!
//! This status code can also be returned in X64 mode if AH, BH, CH or DH
//! registers have been used together with a REX prefix. The instruction
//! is not encodable in such case.
//!
//! Example of raising `kErrorIllegalInst` error.
//!
//! ~~~
//! // Invalid address size.
//! a.mov(dword_ptr(eax), al);
//!
//! // Undecodable instruction - AH used with R10, however R10 can only be
//! // encoded by using REX prefix, which conflicts with AH.
//! a.mov(byte_ptr(r10), ah);
//! ~~~
//!
//! NOTE: In debug mode assertion is raised instead of returning an error.
kErrorIllegalInst,
//! Illegal (unencodable) addressing used.
kErrorIllegalAddresing,
//! Illegal (unencodable) displacement used.
//!
//! X86/X64 Specific
//! ----------------
//!
//! Short form of jump instruction has been used, but the displacement is out
//! of bounds.
kErrorIllegalDisplacement,
//! A variable has been assigned more than once to a function argument (Compiler).
kErrorOverlappedArgs,
//! Count of AsmJit error codes.
kErrorCount
};
//! \}
// ============================================================================
// [asmjit::Init / NoInit]
// ============================================================================
#if !defined(ASMJIT_DOCGEN)
struct _Init {};
static const _Init Init = {};
struct _NoInit {};
static const _NoInit NoInit = {};
#endif // !ASMJIT_DOCGEN
// ============================================================================
// [asmjit::DebugUtils]
// ============================================================================
namespace DebugUtils {
//! Get a printable version of `asmjit::Error` value.
ASMJIT_API const char* errorAsString(Error err) noexcept;
//! \addtogroup asmjit_base
//! \{
//! Called in debug build to output a debugging message caused by assertion
//! failure or tracing.
ASMJIT_API void debugOutput(const char* str) noexcept;
//! Called in debug build on assertion failure.
//!
//! \param file Source file name where it happened.
//! \param line Line in the source file.
//! \param msg Message to display.
//!
//! If you have problems with assertions put a breakpoint at assertionFailed()
//! function (asmjit/base/globals.cpp) and check the call stack to locate the
//! failing code.
ASMJIT_API void ASMJIT_NORETURN assertionFailed(const char* file, int line, const char* msg) noexcept;
//! \}
} // DebugUtils namespace
} // asmjit namespace
// ============================================================================
// [ASMJIT_ASSERT]
// ============================================================================
#if defined(ASMJIT_DEBUG)
# define ASMJIT_ASSERT(exp) \
do { \
if (!(exp)) { \
::asmjit::DebugUtils::assertionFailed( \
__FILE__ + ::asmjit::DebugUtils::kSourceRelativePathOffset, \
__LINE__, \
#exp); \
} \
} while (0)
# define ASMJIT_NOT_REACHED() \
::asmjit::DebugUtils::assertionFailed( \
__FILE__ + ::asmjit::DebugUtils::kSourceRelativePathOffset, \
__LINE__, \
"MUST NOT BE REACHED")
#else
# define ASMJIT_ASSERT(exp) ASMJIT_NOP
# define ASMJIT_NOT_REACHED() ASMJIT_ASSUME(0)
#endif // DEBUG
// ============================================================================
// [ASMJIT_PROPAGATE_ERROR]
// ============================================================================
//! \internal
//!
//! Used by AsmJit to return the `_Exp_` result if it's an error.
#define ASMJIT_PROPAGATE_ERROR(_Exp_) \
do { \
::asmjit::Error _errval = (_Exp_); \
if (_errval != ::asmjit::kErrorOk) \
return _errval; \
} while (0)
// ============================================================================
// [asmjit_cast<>]
// ============================================================================
//! \addtogroup asmjit_base
//! \{
//! Cast used to cast pointer to function. It's like reinterpret_cast<>,
//! but uses internally C style cast to work with MinGW.
//!
//! If you are using single compiler and `reinterpret_cast<>` works for you,
//! there is no reason to use `asmjit_cast<>`. If you are writing
//! cross-platform software with various compiler support, consider using
//! `asmjit_cast<>` instead of `reinterpret_cast<>`.
template<typename T, typename Z>
static ASMJIT_INLINE T asmjit_cast(Z* p) noexcept { return (T)p; }
//! \}
// [Api-End]
#include "../apiend.h"
// [Guard]
#endif // _ASMJIT_BASE_GLOBALS_H
+20
View File
@@ -0,0 +1,20 @@
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Export]
#define ASMJIT_EXPORTS
// [Dependencies]
#include "../base/hlstream.h"
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
File diff suppressed because it is too large Load Diff
+194
View File
@@ -0,0 +1,194 @@
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Export]
#define ASMJIT_EXPORTS
// [Guard]
#include "../build.h"
#if !defined(ASMJIT_DISABLE_LOGGER)
// [Dependencies]
#include "../base/containers.h"
#include "../base/logger.h"
#include "../base/utils.h"
#include <stdarg.h>
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
// ============================================================================
// [asmjit::LogUtil]
// ============================================================================
bool LogUtil::formatLine(StringBuilder& sb, const uint8_t* binData, size_t binLen, size_t dispLen, size_t imLen, const char* comment) noexcept {
size_t currentLen = sb.getLength();
size_t commentLen = comment ? Utils::strLen(comment, kMaxCommentLength) : 0;
ASMJIT_ASSERT(binLen >= dispLen);
if ((binLen != 0 && binLen != kInvalidIndex) || commentLen) {
size_t align = kMaxInstLength;
char sep = ';';
for (size_t i = (binLen == kInvalidIndex); i < 2; i++) {
size_t begin = sb.getLength();
// Append align.
if (currentLen < align) {
if (!sb.appendChars(' ', align - currentLen))
return false;
}
// Append separator.
if (sep) {
if (!(sb.appendChar(sep) & sb.appendChar(' ')))
return false;
}
// Append binary data or comment.
if (i == 0) {
if (!sb.appendHex(binData, binLen - dispLen - imLen))
return false;
if (!sb.appendChars('.', dispLen * 2))
return false;
if (!sb.appendHex(binData + binLen - imLen, imLen))
return false;
if (commentLen == 0)
break;
}
else {
if (!sb.appendString(comment, commentLen))
return false;
}
currentLen += sb.getLength() - begin;
align += kMaxBinaryLength;
sep = '|';
}
}
return sb.appendChar('\n');
}
// ============================================================================
// [asmjit::Logger - Construction / Destruction]
// ============================================================================
Logger::Logger() noexcept {
_options = 0;
::memset(_indentation, 0, ASMJIT_ARRAY_SIZE(_indentation));
}
Logger::~Logger() noexcept {}
// ============================================================================
// [asmjit::Logger - Logging]
// ============================================================================
void Logger::logFormat(uint32_t style, const char* fmt, ...) noexcept {
char buf[1024];
size_t len;
va_list ap;
va_start(ap, fmt);
len = vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
if (len >= sizeof(buf))
len = sizeof(buf) - 1;
logString(style, buf, len);
}
void Logger::logBinary(uint32_t style, const void* data, size_t size) noexcept {
static const char prefix[] = ".data ";
static const char hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
const uint8_t* s = static_cast<const uint8_t*>(data);
size_t i = size;
char buffer[128];
::memcpy(buffer, prefix, ASMJIT_ARRAY_SIZE(prefix) - 1);
while (i) {
uint32_t n = static_cast<uint32_t>(Utils::iMin<size_t>(i, 16));
char* p = buffer + ASMJIT_ARRAY_SIZE(prefix) - 1;
i -= n;
do {
uint32_t c = s[0];
p[0] = hex[c >> 4];
p[1] = hex[c & 15];
p += 2;
s += 1;
} while (--n);
*p++ = '\n';
logString(style, buffer, (size_t)(p - buffer));
}
}
// ============================================================================
// [asmjit::Logger - Indentation]
// ============================================================================
void Logger::setIndentation(const char* indentation) noexcept {
::memset(_indentation, 0, ASMJIT_ARRAY_SIZE(_indentation));
if (!indentation)
return;
size_t length = Utils::strLen(indentation, ASMJIT_ARRAY_SIZE(_indentation) - 1);
::memcpy(_indentation, indentation, length);
}
// ============================================================================
// [asmjit::FileLogger - Construction / Destruction]
// ============================================================================
FileLogger::FileLogger(FILE* stream) noexcept : _stream(nullptr) { setStream(stream); }
FileLogger::~FileLogger() noexcept {}
// ============================================================================
// [asmjit::FileLogger - Logging]
// ============================================================================
void FileLogger::logString(uint32_t style, const char* buf, size_t len) noexcept {
if (!_stream)
return;
if (len == kInvalidIndex)
len = strlen(buf);
fwrite(buf, 1, len, _stream);
}
// ============================================================================
// [asmjit::StringLogger - Construction / Destruction]
// ============================================================================
StringLogger::StringLogger() noexcept {}
StringLogger::~StringLogger() noexcept {}
// ============================================================================
// [asmjit::StringLogger - Logging]
// ============================================================================
void StringLogger::logString(uint32_t style, const char* buf, size_t len) noexcept {
_stringBuilder.appendString(buf, len);
}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
// [Guard]
#endif // !ASMJIT_DISABLE_LOGGER
+268
View File
@@ -0,0 +1,268 @@
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Guard]
#ifndef _ASMJIT_BASE_LOGGER_H
#define _ASMJIT_BASE_LOGGER_H
#include "../build.h"
// [Dependencies]
#include "../base/containers.h"
#include <stdarg.h>
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
//! \addtogroup asmjit_base
//! \{
#if !defined(ASMJIT_DISABLE_LOGGER)
// ============================================================================
// [asmjit::LogUtil]
// ============================================================================
// Only used by asmjit internals, not available to consumers.
#if defined(ASMJIT_EXPORTS)
struct LogUtil {
enum {
// Has to be big to be able to hold all metadata compiler can assign to a
// single instruction.
kMaxCommentLength = 512,
kMaxInstLength = 40,
kMaxBinaryLength = 26
};
static bool formatLine(
StringBuilder& sb,
const uint8_t* binData, size_t binLen, size_t dispLen, size_t imLen, const char* comment) noexcept;
};
#endif // ASMJIT_EXPORTS
// ============================================================================
// [asmjit::Logger]
// ============================================================================
//! Abstract logging class.
//!
//! This class can be inherited and reimplemented to fit into your logging
//! subsystem. When reimplementing use `Logger::log()` method to log into
//! a custom stream.
//!
//! This class also contain `_enabled` member that can be used to enable
//! or disable logging.
class ASMJIT_VIRTAPI Logger {
public:
ASMJIT_NO_COPY(Logger)
// --------------------------------------------------------------------------
// [Options]
// --------------------------------------------------------------------------
//! Logger options.
ASMJIT_ENUM(Options) {
kOptionBinaryForm = 0x00000001, //! Output instructions also in binary form.
kOptionHexImmediate = 0x00000002, //! Output immediates as hexadecimal numbers.
kOptionHexDisplacement = 0x00000004 //! Output displacements as hexadecimal numbers.
};
// --------------------------------------------------------------------------
// [Style]
// --------------------------------------------------------------------------
//! Logger style.
ASMJIT_ENUM(Style) {
kStyleDefault = 0,
kStyleDirective = 1,
kStyleLabel = 2,
kStyleData = 3,
kStyleComment = 4,
kStyleCount = 5
};
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create a `Logger` instance.
ASMJIT_API Logger() noexcept;
//! Destroy the `Logger` instance.
ASMJIT_API virtual ~Logger() noexcept;
// --------------------------------------------------------------------------
// [Logging]
// --------------------------------------------------------------------------
//! Log output.
virtual void logString(uint32_t style, const char* buf, size_t len = kInvalidIndex) noexcept = 0;
//! Log formatter message (like sprintf) sending output to `logString()` method.
ASMJIT_API void logFormat(uint32_t style, const char* fmt, ...) noexcept;
//! Log binary data.
ASMJIT_API void logBinary(uint32_t style, const void* data, size_t size) noexcept;
// --------------------------------------------------------------------------
// [Options]
// --------------------------------------------------------------------------
//! Get all logger options as a single integer.
ASMJIT_INLINE uint32_t getOptions() const noexcept { return _options; }
//! Get the given logger option.
ASMJIT_INLINE bool hasOption(uint32_t option) const noexcept {
return (_options & option) != 0;
}
ASMJIT_INLINE void addOptions(uint32_t options) noexcept { _options |= options; }
ASMJIT_INLINE void clearOptions(uint32_t options) noexcept { _options &= ~options; }
// --------------------------------------------------------------------------
// [Indentation]
// --------------------------------------------------------------------------
//! Get indentation.
ASMJIT_INLINE const char* getIndentation() const noexcept {
return _indentation;
}
//! Set indentation.
ASMJIT_API void setIndentation(const char* indentation) noexcept;
//! Reset indentation.
ASMJIT_INLINE void resetIndentation() noexcept {
setIndentation(nullptr);
}
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Options, see \ref LoggerOption.
uint32_t _options;
//! Indentation.
char _indentation[12];
};
// ============================================================================
// [asmjit::FileLogger]
// ============================================================================
//! Logger that can log to standard C `FILE*` stream.
class ASMJIT_VIRTAPI FileLogger : public Logger {
public:
ASMJIT_NO_COPY(FileLogger)
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create a new `FileLogger` that logs to a `FILE` stream.
ASMJIT_API FileLogger(FILE* stream = nullptr) noexcept;
//! Destroy the `FileLogger`.
ASMJIT_API virtual ~FileLogger() noexcept;
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Get `FILE*` stream.
//!
//! NOTE: Return value can be `nullptr`.
ASMJIT_INLINE FILE* getStream() const noexcept {
return _stream;
}
//! Set `FILE*` stream, can be set to `nullptr` to disable logging, although
//! the `ExternalTool` will still call `logString` even if there is no stream.
ASMJIT_INLINE void setStream(FILE* stream) noexcept {
_stream = stream;
}
// --------------------------------------------------------------------------
// [Logging]
// --------------------------------------------------------------------------
ASMJIT_API virtual void logString(uint32_t style, const char* buf, size_t len = kInvalidIndex) noexcept;
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! C file stream.
FILE* _stream;
};
// ============================================================================
// [asmjit::StringLogger]
// ============================================================================
//! String logger.
class ASMJIT_VIRTAPI StringLogger : public Logger {
public:
ASMJIT_NO_COPY(StringLogger)
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create new `StringLogger`.
ASMJIT_API StringLogger() noexcept;
//! Destroy the `StringLogger`.
ASMJIT_API virtual ~StringLogger() noexcept;
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Get `char*` pointer which represents the resulting string.
//!
//! The pointer is owned by `StringLogger`, it can't be modified or freed.
ASMJIT_INLINE const char* getString() const noexcept {
return _stringBuilder.getData();
}
//! Get the length of the string returned by `getString()`.
ASMJIT_INLINE size_t getLength() const noexcept {
return _stringBuilder.getLength();
}
//! Clear the resulting string.
ASMJIT_INLINE void clearString() noexcept {
_stringBuilder.clear();
}
// --------------------------------------------------------------------------
// [Logging]
// --------------------------------------------------------------------------
ASMJIT_API virtual void logString(uint32_t style, const char* buf, size_t len = kInvalidIndex) noexcept;
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Output.
StringBuilder _stringBuilder;
};
#else
struct Logger;
#endif // !ASMJIT_DISABLE_LOGGER
//! \}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
// [Guard]
#endif // _ASMJIT_BASE_LOGGER_H
+52
View File
@@ -0,0 +1,52 @@
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Export]
#define ASMJIT_EXPORTS
// [Dependencies]
#include "../base/globals.h"
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
// ============================================================================
// [asmjit::Operand]
// ============================================================================
// Prevent static initialization.
class Operand {
public:
struct BaseOp {
uint8_t op;
uint8_t size;
uint8_t reserved_2_1;
uint8_t reserved_3_1;
uint32_t id;
uint32_t reserved_8_4;
uint32_t reserved_12_4;
};
// Kept in union to prevent LTO warnings.
union {
BaseOp _base;
// Required to properly align this _fake_ `Operand`, not used.
uint64_t _data[2];
};
};
ASMJIT_VARAPI const Operand noOperand;
const Operand noOperand = {{ 0, 0, 0, 0, kInvalidValue, 0, 0 }};
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
File diff suppressed because it is too large Load Diff
+132
View File
@@ -0,0 +1,132 @@
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Export]
#define ASMJIT_EXPORTS
// [Dependencies]
#include "../base/podvector.h"
#include "../base/utils.h"
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
// ============================================================================
// [asmjit::PodVectorBase - NullData]
// ============================================================================
const PodVectorBase::Data PodVectorBase::_nullData = { 0, 0 };
static ASMJIT_INLINE bool isDataStatic(PodVectorBase* self, PodVectorBase::Data* d) noexcept {
return (void*)(self + 1) == (void*)d;
}
// ============================================================================
// [asmjit::PodVectorBase - Reset]
// ============================================================================
//! Clear vector data and free internal buffer.
void PodVectorBase::reset(bool releaseMemory) noexcept {
Data* d = _d;
if (d == &_nullData)
return;
if (releaseMemory && !isDataStatic(this, d)) {
ASMJIT_FREE(d);
_d = const_cast<Data*>(&_nullData);
return;
}
d->length = 0;
}
// ============================================================================
// [asmjit::PodVectorBase - Helpers]
// ============================================================================
Error PodVectorBase::_grow(size_t n, size_t sizeOfT) noexcept {
Data* d = _d;
size_t threshold = kMemAllocGrowMax / sizeOfT;
size_t capacity = d->capacity;
size_t after = d->length;
if (IntTraits<size_t>::maxValue() - n < after)
return kErrorNoHeapMemory;
after += n;
if (capacity >= after)
return kErrorOk;
// PodVector is used as a linear array for some data structures used by
// AsmJit code generation. The purpose of this agressive growing schema
// is to minimize memory reallocations, because AsmJit code generation
// classes live short life and will be freed or reused soon.
if (capacity < 32)
capacity = 32;
else if (capacity < 128)
capacity = 128;
else if (capacity < 512)
capacity = 512;
while (capacity < after) {
if (capacity < threshold)
capacity *= 2;
else
capacity += threshold;
}
return _reserve(capacity, sizeOfT);
}
Error PodVectorBase::_reserve(size_t n, size_t sizeOfT) noexcept {
Data* d = _d;
if (d->capacity >= n)
return kErrorOk;
size_t nBytes = sizeof(Data) + n * sizeOfT;
if (ASMJIT_UNLIKELY(nBytes < n))
return kErrorNoHeapMemory;
if (d == &_nullData) {
d = static_cast<Data*>(ASMJIT_ALLOC(nBytes));
if (ASMJIT_UNLIKELY(d == nullptr))
return kErrorNoHeapMemory;
d->length = 0;
}
else {
if (isDataStatic(this, d)) {
Data* oldD = d;
d = static_cast<Data*>(ASMJIT_ALLOC(nBytes));
if (ASMJIT_UNLIKELY(d == nullptr))
return kErrorNoHeapMemory;
size_t len = oldD->length;
d->length = len;
::memcpy(d->getData(), oldD->getData(), len * sizeOfT);
}
else {
d = static_cast<Data*>(ASMJIT_REALLOC(d, nBytes));
if (ASMJIT_UNLIKELY(d == nullptr))
return kErrorNoHeapMemory;
}
}
d->capacity = n;
_d = d;
return kErrorOk;
}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
+281
View File
@@ -0,0 +1,281 @@
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Guard]
#ifndef _ASMJIT_BASE_PODVECTOR_H
#define _ASMJIT_BASE_PODVECTOR_H
// [Dependencies]
#include "../base/globals.h"
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
//! \addtogroup asmjit_base
//! \{
// ============================================================================
// [asmjit::PodVectorBase]
// ============================================================================
//! \internal
class PodVectorBase {
public:
// --------------------------------------------------------------------------
// [Data]
// --------------------------------------------------------------------------
//! \internal
struct Data {
//! Get data.
ASMJIT_INLINE void* getData() const noexcept {
return static_cast<void*>(const_cast<Data*>(this + 1));
}
//! Capacity of the vector.
size_t capacity;
//! Length of the vector.
size_t length;
};
static ASMJIT_API const Data _nullData;
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create a new instance of `PodVectorBase`.
ASMJIT_INLINE PodVectorBase() noexcept : _d(const_cast<Data*>(&_nullData)) {}
//! Destroy the `PodVectorBase` and its data.
ASMJIT_INLINE ~PodVectorBase() noexcept { reset(true); }
protected:
explicit ASMJIT_INLINE PodVectorBase(Data* d) noexcept : _d(d) {}
// --------------------------------------------------------------------------
// [Reset]
// --------------------------------------------------------------------------
public:
//! Reset the vector data and set its `length` to zero.
//!
//! If `releaseMemory` is true the vector buffer will be released to the
//! system.
ASMJIT_API void reset(bool releaseMemory = false) noexcept;
// --------------------------------------------------------------------------
// [Grow / Reserve]
// --------------------------------------------------------------------------
protected:
ASMJIT_API Error _grow(size_t n, size_t sizeOfT) noexcept;
ASMJIT_API Error _reserve(size_t n, size_t sizeOfT) noexcept;
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
public:
Data* _d;
};
// ============================================================================
// [asmjit::PodVector<T>]
// ============================================================================
//! Template used to store and manage array of POD data.
//!
//! This template has these adventages over other vector<> templates:
//! - Non-copyable (designed to be non-copyable, we want it)
//! - No copy-on-write (some implementations of stl can use it)
//! - Optimized for working only with POD types
//! - Uses ASMJIT_... memory management macros
template <typename T>
class PodVector : public PodVectorBase {
public:
ASMJIT_NO_COPY(PodVector<T>)
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create a new instance of `PodVector<T>`.
ASMJIT_INLINE PodVector() noexcept {}
//! Destroy the `PodVector<T>` and its data.
ASMJIT_INLINE ~PodVector() noexcept {}
protected:
explicit ASMJIT_INLINE PodVector(Data* d) noexcept : PodVectorBase(d) {}
// --------------------------------------------------------------------------
// [Data]
// --------------------------------------------------------------------------
public:
//! Get whether the vector is empty.
ASMJIT_INLINE bool isEmpty() const noexcept { return _d->length == 0; }
//! Get length.
ASMJIT_INLINE size_t getLength() const noexcept { return _d->length; }
//! Get capacity.
ASMJIT_INLINE size_t getCapacity() const noexcept { return _d->capacity; }
//! Get data.
ASMJIT_INLINE T* getData() noexcept { return static_cast<T*>(_d->getData()); }
//! \overload
ASMJIT_INLINE const T* getData() const noexcept { return static_cast<const T*>(_d->getData()); }
// --------------------------------------------------------------------------
// [Grow / Reserve]
// --------------------------------------------------------------------------
//! Called to grow the buffer to fit at least `n` elements more.
ASMJIT_INLINE Error _grow(size_t n) noexcept { return PodVectorBase::_grow(n, sizeof(T)); }
//! Realloc internal array to fit at least `n` items.
ASMJIT_INLINE Error _reserve(size_t n) noexcept { return PodVectorBase::_reserve(n, sizeof(T)); }
// --------------------------------------------------------------------------
// [Ops]
// --------------------------------------------------------------------------
//! Prepend `item` to vector.
Error prepend(const T& item) noexcept {
Data* d = _d;
if (d->length == d->capacity) {
ASMJIT_PROPAGATE_ERROR(_grow(1));
_d = d;
}
::memmove(static_cast<T*>(d->getData()) + 1, d->getData(), d->length * sizeof(T));
::memcpy(d->getData(), &item, sizeof(T));
d->length++;
return kErrorOk;
}
//! Insert an `item` at the `index`.
Error insert(size_t index, const T& item) noexcept {
Data* d = _d;
ASMJIT_ASSERT(index <= d->length);
if (d->length == d->capacity) {
ASMJIT_PROPAGATE_ERROR(_grow(1));
d = _d;
}
T* dst = static_cast<T*>(d->getData()) + index;
::memmove(dst + 1, dst, d->length - index);
::memcpy(dst, &item, sizeof(T));
d->length++;
return kErrorOk;
}
//! Append `item` to vector.
Error append(const T& item) noexcept {
Data* d = _d;
if (d->length == d->capacity) {
ASMJIT_PROPAGATE_ERROR(_grow(1));
d = _d;
}
::memcpy(static_cast<T*>(d->getData()) + d->length, &item, sizeof(T));
d->length++;
return kErrorOk;
}
//! Get index of `val` or `kInvalidIndex` if not found.
size_t indexOf(const T& val) const noexcept {
Data* d = _d;
const T* data = static_cast<const T*>(d->getData());
size_t len = d->length;
for (size_t i = 0; i < len; i++)
if (data[i] == val)
return i;
return kInvalidIndex;
}
//! Remove item at index `i`.
void removeAt(size_t i) noexcept {
Data* d = _d;
ASMJIT_ASSERT(i < d->length);
T* data = static_cast<T*>(d->getData()) + i;
d->length--;
::memmove(data, data + 1, d->length - i);
}
//! Swap this pod-vector with `other`.
void swap(PodVector<T>& other) noexcept {
T* otherData = other._d;
other._d = _d;
_d = otherData;
}
//! Get item at index `i`.
ASMJIT_INLINE T& operator[](size_t i) noexcept {
ASMJIT_ASSERT(i < getLength());
return getData()[i];
}
//! Get item at index `i`.
ASMJIT_INLINE const T& operator[](size_t i) const noexcept {
ASMJIT_ASSERT(i < getLength());
return getData()[i];
}
};
// ============================================================================
// [asmjit::PodVectorTmp<T>]
// ============================================================================
template<typename T, size_t N>
class PodVectorTmp : public PodVector<T> {
public:
ASMJIT_NO_COPY(PodVectorTmp<T, N>)
// --------------------------------------------------------------------------
// [StaticData]
// --------------------------------------------------------------------------
struct StaticData : public PodVectorBase::Data {
char data[sizeof(T) * N];
};
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create a new instance of `PodVectorTmp<T>`.
ASMJIT_INLINE PodVectorTmp() noexcept : PodVector<T>(&_staticData) {
_staticData.capacity = N;
_staticData.length = 0;
}
//! Destroy the `PodVectorTmp<T>` and its data.
ASMJIT_INLINE ~PodVectorTmp() noexcept {}
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
StaticData _staticData;
};
//! \}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
// [Guard]
#endif // _ASMJIT_BASE_PODVECTOR_H
+214
View File
@@ -0,0 +1,214 @@
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Export]
#define ASMJIT_EXPORTS
// [Dependencies]
#include "../base/assembler.h"
#include "../base/runtime.h"
// TODO: Rename this, or make call conv independent of CompilerFunc.
#include "../base/compilerfunc.h"
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
// ============================================================================
// [asmjit::Runtime - Utilities]
// ============================================================================
static ASMJIT_INLINE uint32_t hostStackAlignment() noexcept {
// By default a pointer-size stack alignment is assumed.
uint32_t alignment = sizeof(intptr_t);
// ARM & ARM64
// -----------
//
// - 32-bit ARM requires stack to be aligned to 8 bytes.
// - 64-bit ARM requires stack to be aligned to 16 bytes.
#if ASMJIT_ARCH_ARM32 || ASMJIT_ARCH_ARM64
alignment = ASMJIT_ARCH_ARM32 ? 8 : 16;
#endif
// X86 & X64
// ---------
//
// - 32-bit X86 requires stack to be aligned to 4 bytes. Modern Linux, APPLE
// and UNIX guarantees 16-byte stack alignment even in 32-bit, but I'm
// not sure about all other UNIX operating systems, because 16-byte alignment
// is addition to an older specification.
// - 64-bit X86 requires stack to be aligned to 16 bytes.
#if ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64
int modernOS = ASMJIT_OS_LINUX || // Linux & ANDROID.
ASMJIT_OS_MAC || // OSX and iOS.
ASMJIT_OS_BSD; // BSD variants.
alignment = ASMJIT_ARCH_X64 || modernOS ? 16 : 4;
#endif
return alignment;
}
static ASMJIT_INLINE void hostFlushInstructionCache(void* p, size_t size) noexcept {
// Only useful on non-x86 architectures.
#if !ASMJIT_ARCH_X86 && !ASMJIT_ARCH_X64
# if ASMJIT_OS_WINDOWS
// Windows has a built-in support in kernel32.dll.
::FlushInstructionCache(_memMgr.getProcessHandle(), p, size);
# endif // ASMJIT_OS_WINDOWS
#else
ASMJIT_UNUSED(p);
ASMJIT_UNUSED(size);
#endif // !ASMJIT_ARCH_X86 && !ASMJIT_ARCH_X64
}
// ============================================================================
// [asmjit::Runtime - Construction / Destruction]
// ============================================================================
Runtime::Runtime() noexcept
: _runtimeType(kTypeNone),
_allocType(kVMemAllocFreeable),
_cpuInfo(),
_stackAlignment(0),
_cdeclConv(kCallConvNone),
_stdCallConv(kCallConvNone),
_baseAddress(kNoBaseAddress),
_sizeLimit(0) {
::memset(_reserved, 0, sizeof(_reserved));
}
Runtime::~Runtime() noexcept {}
// ============================================================================
// [asmjit::HostRuntime - Construction / Destruction]
// ============================================================================
HostRuntime::HostRuntime() noexcept {
_runtimeType = kTypeJit;
_cpuInfo = CpuInfo::getHost();
_stackAlignment = hostStackAlignment();
_cdeclConv = kCallConvHostCDecl;
_stdCallConv = kCallConvHostStdCall;
}
HostRuntime::~HostRuntime() noexcept {}
// ============================================================================
// [asmjit::HostRuntime - Interface]
// ============================================================================
void HostRuntime::flush(void* p, size_t size) noexcept {
hostFlushInstructionCache(p, size);
}
// ============================================================================
// [asmjit::StaticRuntime - Construction / Destruction]
// ============================================================================
StaticRuntime::StaticRuntime(void* baseAddress, size_t sizeLimit) noexcept {
_sizeLimit = sizeLimit;
_baseAddress = static_cast<Ptr>((uintptr_t)baseAddress);
}
StaticRuntime::~StaticRuntime() noexcept {}
// ============================================================================
// [asmjit::StaticRuntime - Interface]
// ============================================================================
Error StaticRuntime::add(void** dst, Assembler* assembler) noexcept {
size_t codeSize = assembler->getCodeSize();
size_t sizeLimit = _sizeLimit;
if (codeSize == 0) {
*dst = nullptr;
return kErrorNoCodeGenerated;
}
if (sizeLimit != 0 && sizeLimit < codeSize) {
*dst = nullptr;
return kErrorCodeTooLarge;
}
Ptr baseAddress = _baseAddress;
uint8_t* p = static_cast<uint8_t*>((void*)static_cast<uintptr_t>(baseAddress));
// Since the base address is known the `relocSize` returned should be equal
// to `codeSize`. It's better to fail if they don't match instead of passsing
// silently.
size_t relocSize = assembler->relocCode(p, baseAddress);
if (relocSize == 0 || codeSize != relocSize) {
*dst = nullptr;
return kErrorInvalidState;
}
_baseAddress += codeSize;
if (sizeLimit)
sizeLimit -= codeSize;
flush(p, codeSize);
*dst = p;
return kErrorOk;
}
Error StaticRuntime::release(void* p) noexcept {
// There is nothing to release as `StaticRuntime` doesn't manage any memory.
ASMJIT_UNUSED(p);
return kErrorOk;
}
// ============================================================================
// [asmjit::JitRuntime - Construction / Destruction]
// ============================================================================
JitRuntime::JitRuntime() noexcept {}
JitRuntime::~JitRuntime() noexcept {}
// ============================================================================
// [asmjit::JitRuntime - Interface]
// ============================================================================
Error JitRuntime::add(void** dst, Assembler* assembler) noexcept {
size_t codeSize = assembler->getCodeSize();
if (codeSize == 0) {
*dst = nullptr;
return kErrorNoCodeGenerated;
}
void* p = _memMgr.alloc(codeSize, getAllocType());
if (p == nullptr) {
*dst = nullptr;
return kErrorNoVirtualMemory;
}
// Relocate the code and release the unused memory back to `VMemMgr`.
size_t relocSize = assembler->relocCode(p);
if (relocSize == 0) {
*dst = nullptr;
_memMgr.release(p);
return kErrorInvalidState;
}
if (relocSize < codeSize)
_memMgr.shrink(p, relocSize);
flush(p, relocSize);
*dst = p;
return kErrorOk;
}
Error JitRuntime::release(void* p) noexcept {
return _memMgr.release(p);
}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
+266
View File
@@ -0,0 +1,266 @@
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Guard]
#ifndef _ASMJIT_BASE_RUNTIME_H
#define _ASMJIT_BASE_RUNTIME_H
// [Dependencies]
#include "../base/cpuinfo.h"
#include "../base/vmem.h"
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
// ============================================================================
// [Forward Declarations]
// ============================================================================
class Assembler;
class CpuInfo;
//! \addtogroup asmjit_base
//! \{
// ============================================================================
// [asmjit::Runtime]
// ============================================================================
//! Base runtime.
class ASMJIT_VIRTAPI Runtime {
public:
ASMJIT_NO_COPY(Runtime)
// --------------------------------------------------------------------------
// [asmjit::RuntimeType]
// --------------------------------------------------------------------------
ASMJIT_ENUM(Type) {
kTypeNone = 0,
kTypeJit = 1,
kTypeRemote = 2
};
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create a `Runtime` instance.
ASMJIT_API Runtime() noexcept;
//! Destroy the `Runtime` instance.
ASMJIT_API virtual ~Runtime() noexcept;
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Get the runtime type, see \ref Type.
ASMJIT_INLINE uint32_t getRuntimeType() const noexcept { return _runtimeType; }
//! Get stack alignment of the target.
ASMJIT_INLINE uint32_t getStackAlignment() const noexcept { return _stackAlignment; }
//! Get the CDECL calling convention conforming to the runtime's ABI.
//!
//! NOTE: This is a default calling convention used by the runtime's target.
ASMJIT_INLINE uint32_t getCdeclConv() const noexcept { return _cdeclConv; }
//! Get the STDCALL calling convention conforming to the runtime's ABI.
//!
//! NOTE: STDCALL calling convention is only used by 32-bit x86 target. On
//! all other targets it's mapped to CDECL and calling `getStdcallConv()` will
//! return the same as `getCdeclConv()`.
ASMJIT_INLINE uint32_t getStdCallConv() const noexcept { return _stdCallConv; }
//! Get CPU information.
ASMJIT_INLINE const CpuInfo& getCpuInfo() const noexcept { return _cpuInfo; }
//! Set CPU information.
ASMJIT_INLINE void setCpuInfo(const CpuInfo& ci) noexcept { _cpuInfo = ci; }
//! Get whether the runtime has a base address.
ASMJIT_INLINE bool hasBaseAddress() const noexcept { return _baseAddress != kNoBaseAddress; }
//! Get the base address.
ASMJIT_INLINE Ptr getBaseAddress() const noexcept { return _baseAddress; }
// --------------------------------------------------------------------------
// [Interface]
// --------------------------------------------------------------------------
//! Allocate a memory needed for a code generated by `assembler` and
//! relocate it to the target location.
//!
//! The beginning of the memory allocated for the function is returned in
//! `dst`. Returns Status code as \ref ErrorCode, on failure `dst` is set to
//! `nullptr`.
virtual Error add(void** dst, Assembler* assembler) noexcept = 0;
//! Release memory allocated by `add`.
virtual Error release(void* p) noexcept = 0;
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Type of the runtime.
uint8_t _runtimeType;
//! Type of the allocation.
uint8_t _allocType;
//! Runtime's stack alignment.
uint8_t _stackAlignment;
//! CDECL calling convention conforming to runtime ABI.
uint8_t _cdeclConv;
//! STDCALL calling convention conforming to runtime ABI.
uint8_t _stdCallConv;
//! \internal
uint8_t _reserved[3];
//! Runtime CPU information.
CpuInfo _cpuInfo;
//! Base address (-1 means no base address).
Ptr _baseAddress;
//! Maximum size of the code that can be added to the runtime (0=unlimited).
size_t _sizeLimit;
};
// ============================================================================
// [asmjit::HostRuntime]
// ============================================================================
//! Base runtime for JIT code generation.
class ASMJIT_VIRTAPI HostRuntime : public Runtime {
public:
ASMJIT_NO_COPY(HostRuntime)
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create a `HostRuntime` instance.
ASMJIT_API HostRuntime() noexcept;
//! Destroy the `HostRuntime` instance.
ASMJIT_API virtual ~HostRuntime() noexcept;
// --------------------------------------------------------------------------
// [Interface]
// --------------------------------------------------------------------------
//! Flush an instruction cache.
//!
//! This member function is called after the code has been copied to the
//! destination buffer. It is only useful for JIT code generation as it
//! causes a flush of the processor cache.
//!
//! Flushing is basically a NOP under X86/X64, but is needed by architectures
//! that do not have a transparent instruction cache.
//!
//! This function can also be overridden to improve compatibility with tools
//! such as Valgrind, however, it's not an official part of AsmJit.
ASMJIT_API virtual void flush(void* p, size_t size) noexcept;
};
// ============================================================================
// [asmjit::StaticRuntime]
// ============================================================================
//! JIT static runtime.
//!
//! JIT static runtime can be used to generate code to a memory location that
//! is known.
class ASMJIT_VIRTAPI StaticRuntime : public HostRuntime {
public:
ASMJIT_NO_COPY(StaticRuntime)
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create a `StaticRuntime` instance.
//!
//! The `address` specifies a fixed target address, which will be used as a
//! base address for relocation, and `sizeLimit` specifies the maximum size
//! of a code that can be copied to it. If there is no limit `sizeLimit`
//! should be zero.
ASMJIT_API StaticRuntime(void* baseAddress, size_t sizeLimit = 0) noexcept;
//! Destroy the `StaticRuntime` instance.
ASMJIT_API virtual ~StaticRuntime() noexcept;
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Get the base address.
ASMJIT_INLINE Ptr getBaseAddress() const noexcept { return _baseAddress; }
//! Get the maximum size of the code that can be relocated/stored in the target.
//!
//! Returns zero if unlimited.
ASMJIT_INLINE size_t getSizeLimit() const noexcept { return _sizeLimit; }
// --------------------------------------------------------------------------
// [Interface]
// --------------------------------------------------------------------------
ASMJIT_API virtual Error add(void** dst, Assembler* assembler) noexcept;
ASMJIT_API virtual Error release(void* p) noexcept;
};
// ============================================================================
// [asmjit::JitRuntime]
// ============================================================================
//! JIT runtime.
class ASMJIT_VIRTAPI JitRuntime : public HostRuntime {
public:
ASMJIT_NO_COPY(JitRuntime)
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create a `JitRuntime` instance.
ASMJIT_API JitRuntime() noexcept;
//! Destroy the `JitRuntime` instance.
ASMJIT_API virtual ~JitRuntime() noexcept;
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Get the type of allocation.
ASMJIT_INLINE uint32_t getAllocType() const noexcept { return _allocType; }
//! Set the type of allocation.
ASMJIT_INLINE void setAllocType(uint32_t allocType) noexcept { _allocType = allocType; }
//! Get the virtual memory manager.
ASMJIT_INLINE VMemMgr* getMemMgr() const noexcept { return const_cast<VMemMgr*>(&_memMgr); }
// --------------------------------------------------------------------------
// [Interface]
// --------------------------------------------------------------------------
ASMJIT_API virtual Error add(void** dst, Assembler* assembler) noexcept;
ASMJIT_API virtual Error release(void* p) noexcept;
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Virtual memory manager.
VMemMgr _memMgr;
};
//! \}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
// [Guard]
#endif // _ASMJIT_BASE_RUNTIME_H
+289
View File
@@ -0,0 +1,289 @@
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Export]
#define ASMJIT_EXPORTS
// [Dependencies]
#include "../base/utils.h"
#if ASMJIT_OS_POSIX
# include <time.h>
# include <unistd.h>
#endif // ASMJIT_OS_POSIX
#if ASMJIT_OS_MAC
# include <mach/mach_time.h>
#endif // ASMJIT_OS_MAC
#if ASMJIT_OS_WINDOWS
# if defined(_MSC_VER) && _MSC_VER >= 1400
# include <intrin.h>
# else
# define _InterlockedCompareExchange InterlockedCompareExchange
# endif // _MSC_VER
#endif // ASMJIT_OS_WINDOWS
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
// ============================================================================
// [asmjit::CpuTicks - Windows]
// ============================================================================
#if ASMJIT_OS_WINDOWS
static volatile uint32_t Utils_hiResTicks;
static volatile double Utils_hiResFreq;
uint32_t Utils::getTickCount() noexcept {
do {
uint32_t hiResOk = Utils_hiResTicks;
if (hiResOk == 1) {
LARGE_INTEGER now;
if (!::QueryPerformanceCounter(&now))
break;
return (int64_t)(double(now.QuadPart) / Utils_hiResFreq);
}
if (hiResOk == 0) {
LARGE_INTEGER qpf;
if (!::QueryPerformanceFrequency(&qpf)) {
_InterlockedCompareExchange((LONG*)&Utils_hiResTicks, 0xFFFFFFFF, 0);
break;
}
LARGE_INTEGER now;
if (!::QueryPerformanceCounter(&now)) {
_InterlockedCompareExchange((LONG*)&Utils_hiResTicks, 0xFFFFFFFF, 0);
break;
}
double freqDouble = double(qpf.QuadPart) / 1000.0;
Utils_hiResFreq = freqDouble;
_InterlockedCompareExchange((LONG*)&Utils_hiResTicks, 1, 0);
return static_cast<uint32_t>(
static_cast<int64_t>(double(now.QuadPart) / freqDouble) & 0xFFFFFFFF);
}
} while (0);
// Bail to a less precise GetTickCount().
return ::GetTickCount();
}
// ============================================================================
// [asmjit::CpuTicks - Mac]
// ============================================================================
#elif ASMJIT_OS_MAC
static mach_timebase_info_data_t CpuTicks_machTime;
uint32_t Utils::getTickCount() noexcept {
// Initialize the first time CpuTicks::now() is called (See Apple's QA1398).
if (CpuTicks_machTime.denom == 0) {
if (mach_timebase_info(&CpuTicks_machTime) != KERN_SUCCESS)
return 0;
}
// mach_absolute_time() returns nanoseconds, we need just milliseconds.
uint64_t t = mach_absolute_time() / 1000000;
t = t * CpuTicks_machTime.numer / CpuTicks_machTime.denom;
return static_cast<uint32_t>(t & 0xFFFFFFFFU);
}
// ============================================================================
// [asmjit::CpuTicks - Posix]
// ============================================================================
#else
uint32_t Utils::getTickCount() noexcept {
#if defined(_POSIX_MONOTONIC_CLOCK) && _POSIX_MONOTONIC_CLOCK >= 0
struct timespec ts;
if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
return 0;
uint64_t t = (uint64_t(ts.tv_sec ) * 1000) + (uint64_t(ts.tv_nsec) / 1000000);
return static_cast<uint32_t>(t & 0xFFFFFFFFU);
#else // _POSIX_MONOTONIC_CLOCK
#error "[asmjit] Utils::getTickCount() is not implemented for your target OS."
return 0;
#endif // _POSIX_MONOTONIC_CLOCK
}
#endif // ASMJIT_OS
// ============================================================================
// [asmjit::Utils - Unit]
// ============================================================================
#if defined(ASMJIT_TEST)
UNIT(base_utils) {
uint32_t i;
INFO("IntTraits<>.");
EXPECT(IntTraits<signed char>::kIsSigned,"IntTraits<signed char> should report signed.");
EXPECT(IntTraits<short>::kIsSigned, "IntTraits<signed short> should report signed.");
EXPECT(IntTraits<int>::kIsSigned, "IntTraits<int> should report signed.");
EXPECT(IntTraits<long>::kIsSigned, "IntTraits<long> should report signed.");
EXPECT(IntTraits<unsigned char>::kIsUnsigned, "IntTraits<unsigned char> should report unsigned.");
EXPECT(IntTraits<unsigned short>::kIsUnsigned, "IntTraits<unsigned short> should report unsigned.");
EXPECT(IntTraits<unsigned int>::kIsUnsigned, "IntTraits<unsigned int> should report unsigned.");
EXPECT(IntTraits<unsigned long>::kIsUnsigned, "IntTraits<unsigned long> should report unsigned.");
EXPECT(IntTraits<intptr_t>::kIsSigned, "IntTraits<intptr_t> should report signed.");
EXPECT(IntTraits<uintptr_t>::kIsUnsigned, "IntTraits<uintptr_t> should report unsigned.");
EXPECT(IntTraits<intptr_t>::kIsIntPtr, "IntTraits<intptr_t> should report intptr_t type.");
EXPECT(IntTraits<uintptr_t>::kIsIntPtr, "IntTraits<uintptr_t> should report intptr_t type.");
INFO("Utils::iMin()/iMax().");
EXPECT(Utils::iMin<int>( 0, -1) == -1, "Utils::iMin<int> should return a minimum value.");
EXPECT(Utils::iMin<int>(-1, -2) == -2, "Utils::iMin<int> should return a minimum value.");
EXPECT(Utils::iMin<int>( 1, 2) == 1, "Utils::iMin<int> should return a minimum value.");
EXPECT(Utils::iMax<int>( 0, -1) == 0, "Utils::iMax<int> should return a maximum value.");
EXPECT(Utils::iMax<int>(-1, -2) == -1, "Utils::iMax<int> should return a maximum value.");
EXPECT(Utils::iMax<int>( 1, 2) == 2, "Utils::iMax<int> should return a maximum value.");
INFO("Utils::inInterval().");
EXPECT(Utils::inInterval<int>(11 , 10, 20) == true , "Utils::inInterval<int> should return true if inside.");
EXPECT(Utils::inInterval<int>(101, 10, 20) == false, "Utils::inInterval<int> should return false if outside.");
INFO("Utils::isInt8().");
EXPECT(Utils::isInt8(-128) == true , "Utils::isInt8<> should return true if inside.");
EXPECT(Utils::isInt8( 127) == true , "Utils::isInt8<> should return true if inside.");
EXPECT(Utils::isInt8(-129) == false, "Utils::isInt8<> should return false if outside.");
EXPECT(Utils::isInt8( 128) == false, "Utils::isInt8<> should return false if outside.");
INFO("Utils::isInt16().");
EXPECT(Utils::isInt16(-32768) == true , "Utils::isInt16<> should return true if inside.");
EXPECT(Utils::isInt16( 32767) == true , "Utils::isInt16<> should return true if inside.");
EXPECT(Utils::isInt16(-32769) == false, "Utils::isInt16<> should return false if outside.");
EXPECT(Utils::isInt16( 32768) == false, "Utils::isInt16<> should return false if outside.");
INFO("Utils::isInt32().");
EXPECT(Utils::isInt32( 2147483647 ) == true, "Utils::isInt32<int> should return true if inside.");
EXPECT(Utils::isInt32(-2147483647 - 1) == true, "Utils::isInt32<int> should return true if inside.");
EXPECT(Utils::isInt32(ASMJIT_UINT64_C(2147483648)) == false, "Utils::isInt32<int> should return false if outside.");
EXPECT(Utils::isInt32(ASMJIT_UINT64_C(0xFFFFFFFF)) == false, "Utils::isInt32<int> should return false if outside.");
EXPECT(Utils::isInt32(ASMJIT_UINT64_C(0xFFFFFFFF) + 1) == false, "Utils::isInt32<int> should return false if outside.");
INFO("Utils::isUInt8().");
EXPECT(Utils::isUInt8(0) == true , "Utils::isUInt8<> should return true if inside.");
EXPECT(Utils::isUInt8(255) == true , "Utils::isUInt8<> should return true if inside.");
EXPECT(Utils::isUInt8(256) == false, "Utils::isUInt8<> should return false if outside.");
EXPECT(Utils::isUInt8(-1) == false, "Utils::isUInt8<> should return false if negative.");
INFO("Utils::isUInt12().");
EXPECT(Utils::isUInt12(0) == true , "Utils::isUInt12<> should return true if inside.");
EXPECT(Utils::isUInt12(4095) == true , "Utils::isUInt12<> should return true if inside.");
EXPECT(Utils::isUInt12(4096) == false, "Utils::isUInt12<> should return false if outside.");
EXPECT(Utils::isUInt12(-1) == false, "Utils::isUInt12<> should return false if negative.");
INFO("Utils::isUInt16().");
EXPECT(Utils::isUInt16(0) == true , "Utils::isUInt16<> should return true if inside.");
EXPECT(Utils::isUInt16(65535) == true , "Utils::isUInt16<> should return true if inside.");
EXPECT(Utils::isUInt16(65536) == false, "Utils::isUInt16<> should return false if outside.");
EXPECT(Utils::isUInt16(-1) == false, "Utils::isUInt16<> should return false if negative.");
INFO("Utils::isUInt32().");
EXPECT(Utils::isUInt32(ASMJIT_UINT64_C(0xFFFFFFFF)) == true, "Utils::isUInt32<uint64_t> should return true if inside.");
EXPECT(Utils::isUInt32(ASMJIT_UINT64_C(0xFFFFFFFF) + 1) == false, "Utils::isUInt32<uint64_t> should return false if outside.");
EXPECT(Utils::isUInt32(-1) == false, "Utils::isUInt32<int> should return false if negative.");
INFO("Utils::isPower2().");
for (i = 0; i < 64; i++) {
EXPECT(Utils::isPowerOf2(static_cast<uint64_t>(1) << i) == true,
"Utils::isPower2() didn't report power of 2.");
EXPECT(Utils::isPowerOf2((static_cast<uint64_t>(1) << i) ^ 0x001101) == false,
"Utils::isPower2() didn't report not power of 2.");
}
INFO("Utils::mask().");
for (i = 0; i < 32; i++) {
EXPECT(Utils::mask(i) == (1 << i),
"Utils::mask(%u) should return %X.", i, (1 << i));
}
INFO("Utils::bits().");
for (i = 0; i < 32; i++) {
uint32_t expectedBits = 0;
for (uint32_t b = 0; b < i; b++)
expectedBits |= static_cast<uint32_t>(1) << b;
EXPECT(Utils::bits(i) == expectedBits,
"Utils::bits(%u) should return %X.", i, expectedBits);
}
INFO("Utils::hasBit().");
for (i = 0; i < 32; i++) {
EXPECT(Utils::hasBit((1 << i), i) == true,
"Utils::hasBit(%X, %u) should return true.", (1 << i), i);
}
INFO("Utils::bitCount().");
for (i = 0; i < 32; i++) {
EXPECT(Utils::bitCount((1 << i)) == 1,
"Utils::bitCount(%X) should return true.", (1 << i));
}
EXPECT(Utils::bitCount(0x000000F0) == 4, "");
EXPECT(Utils::bitCount(0x10101010) == 4, "");
EXPECT(Utils::bitCount(0xFF000000) == 8, "");
EXPECT(Utils::bitCount(0xFFFFFFF7) == 31, "");
EXPECT(Utils::bitCount(0x7FFFFFFF) == 31, "");
INFO("Utils::findFirstBit().");
for (i = 0; i < 32; i++) {
EXPECT(Utils::findFirstBit((1 << i)) == i,
"Utils::findFirstBit(%X) should return %u.", (1 << i), i);
}
INFO("Utils::keepNOnesFromRight().");
EXPECT(Utils::keepNOnesFromRight(0xF, 1) == 0x1, "");
EXPECT(Utils::keepNOnesFromRight(0xF, 2) == 0x3, "");
EXPECT(Utils::keepNOnesFromRight(0xF, 3) == 0x7, "");
EXPECT(Utils::keepNOnesFromRight(0x5, 2) == 0x5, "");
EXPECT(Utils::keepNOnesFromRight(0xD, 2) == 0x5, "");
INFO("Utils::isAligned().");
EXPECT(Utils::isAligned<size_t>(0xFFFF, 4) == false, "");
EXPECT(Utils::isAligned<size_t>(0xFFF4, 4) == true , "");
EXPECT(Utils::isAligned<size_t>(0xFFF8, 8) == true , "");
EXPECT(Utils::isAligned<size_t>(0xFFF0, 16) == true , "");
INFO("Utils::alignTo().");
EXPECT(Utils::alignTo<size_t>(0xFFFF, 4) == 0x10000, "");
EXPECT(Utils::alignTo<size_t>(0xFFF4, 4) == 0x0FFF4, "");
EXPECT(Utils::alignTo<size_t>(0xFFF8, 8) == 0x0FFF8, "");
EXPECT(Utils::alignTo<size_t>(0xFFF0, 16) == 0x0FFF0, "");
EXPECT(Utils::alignTo<size_t>(0xFFF0, 32) == 0x10000, "");
INFO("Utils::alignToPowerOf2().");
EXPECT(Utils::alignToPowerOf2<size_t>(0xFFFF) == 0x10000, "");
EXPECT(Utils::alignToPowerOf2<size_t>(0xF123) == 0x10000, "");
EXPECT(Utils::alignToPowerOf2<size_t>(0x0F00) == 0x01000, "");
EXPECT(Utils::alignToPowerOf2<size_t>(0x0100) == 0x00100, "");
EXPECT(Utils::alignToPowerOf2<size_t>(0x1001) == 0x02000, "");
INFO("Utils::alignDiff().");
EXPECT(Utils::alignDiff<size_t>(0xFFFF, 4) == 1, "");
EXPECT(Utils::alignDiff<size_t>(0xFFF4, 4) == 0, "");
EXPECT(Utils::alignDiff<size_t>(0xFFF8, 8) == 0, "");
EXPECT(Utils::alignDiff<size_t>(0xFFF0, 16) == 0, "");
EXPECT(Utils::alignDiff<size_t>(0xFFF0, 32) == 16, "");
}
#endif // ASMJIT_TEST
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+233
View File
@@ -0,0 +1,233 @@
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Guard]
#ifndef _ASMJIT_BASE_VMEM_H
#define _ASMJIT_BASE_VMEM_H
// [Dependencies]
#include "../base/utils.h"
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
//! \addtogroup asmjit_base
//! \{
// ============================================================================
// [asmjit::VMemAllocType]
// ============================================================================
//! Type of virtual memory allocation, see `VMemMgr::alloc()`.
ASMJIT_ENUM(VMemAllocType) {
//! Normal memory allocation, has to be freed by `VMemMgr::release()`.
kVMemAllocFreeable = 0,
//! Allocate permanent memory, can't be freed.
kVMemAllocPermanent = 1
};
// ============================================================================
// [asmjit::VMemFlags]
// ============================================================================
//! Type of virtual memory allocation, see `VMemMgr::alloc()`.
ASMJIT_ENUM(VMemFlags) {
//! Memory is writable.
kVMemFlagWritable = 0x00000001,
//! Memory is executable.
kVMemFlagExecutable = 0x00000002
};
// ============================================================================
// [asmjit::VMemUtil]
// ============================================================================
//! Virtual memory utilities.
//!
//! Defines functions that provide facility to allocate and free memory that is
//! executable in a platform independent manner. If both the processor and host
//! operating system support data-execution-prevention then the only way how to
//! run machine code is to allocate it to a memory that has marked as executable.
//! VMemUtil is just unified interface to platform dependent APIs.
//!
//! `VirtualAlloc()` function is used on Windows operating system and `mmap()`
//! on POSIX. `VirtualAlloc()` and `mmap()` documentation provide a detailed
//! overview on how to use a platform specific APIs.
struct VMemUtil {
//! Get a size/alignment of a single virtual memory page.
static ASMJIT_API size_t getPageSize() noexcept;
//! Get a recommended granularity for a single `alloc` call.
static ASMJIT_API size_t getPageGranularity() noexcept;
//! Allocate virtual memory.
//!
//! Pages are readable/writeable, but they are not guaranteed to be
//! executable unless 'canExecute' is true. Returns the address of
//! allocated memory, or `nullptr` on failure.
static ASMJIT_API void* alloc(size_t length, size_t* allocated, uint32_t flags) noexcept;
//! Free memory allocated by `alloc()`.
static ASMJIT_API Error release(void* addr, size_t length) noexcept;
#if ASMJIT_OS_WINDOWS
//! Allocate virtual memory of `hProcess` (Windows only).
static ASMJIT_API void* allocProcessMemory(HANDLE hProcess, size_t length, size_t* allocated, uint32_t flags) noexcept;
//! Release virtual memory of `hProcess` (Windows only).
static ASMJIT_API Error releaseProcessMemory(HANDLE hProcess, void* addr, size_t length) noexcept;
#endif // ASMJIT_OS_WINDOWS
};
// ============================================================================
// [asmjit::VMemMgr]
// ============================================================================
//! Reference implementation of memory manager that uses `VMemUtil` to allocate
//! chunks of virtual memory and bit arrays to manage it.
class VMemMgr {
public:
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
#if !ASMJIT_OS_WINDOWS
//! Create a `VMemMgr` instance.
ASMJIT_API VMemMgr() noexcept;
#else
//! Create a `VMemMgr` instance.
//!
//! NOTE: When running on Windows it's possible to specify a `hProcess` to
//! be used for memory allocation. Using `hProcess` allows to allocate memory
//! of a remote process.
ASMJIT_API VMemMgr(HANDLE hProcess = static_cast<HANDLE>(0)) noexcept;
#endif // ASMJIT_OS_WINDOWS
//! Destroy the `VMemMgr` instance and free all blocks.
ASMJIT_API ~VMemMgr() noexcept;
// --------------------------------------------------------------------------
// [Reset]
// --------------------------------------------------------------------------
//! Free all allocated memory.
ASMJIT_API void reset() noexcept;
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
#if ASMJIT_OS_WINDOWS
//! Get the handle of the process memory manager is bound to.
ASMJIT_INLINE HANDLE getProcessHandle() const noexcept {
return _hProcess;
}
#endif // ASMJIT_OS_WINDOWS
//! Get how many bytes are currently allocated.
ASMJIT_INLINE size_t getAllocatedBytes() const noexcept {
return _allocatedBytes;
}
//! Get how many bytes are currently used.
ASMJIT_INLINE size_t getUsedBytes() const noexcept {
return _usedBytes;
}
//! Get whether to keep allocated memory after the `VMemMgr` is destroyed.
//!
//! \sa \ref setKeepVirtualMemory.
ASMJIT_INLINE bool getKeepVirtualMemory() const noexcept {
return _keepVirtualMemory;
}
//! Set whether to keep allocated memory after memory manager is
//! destroyed.
//!
//! This method is usable when patching code of remote process. You need to
//! allocate process memory, store generated assembler into it and patch the
//! method you want to redirect (into your code). This method affects only
//! VMemMgr destructor. After destruction all internal
//! structures are freed, only the process virtual memory remains.
//!
//! NOTE: Memory allocated with kVMemAllocPermanent is always kept.
//!
//! \sa \ref getKeepVirtualMemory.
ASMJIT_INLINE void setKeepVirtualMemory(bool keepVirtualMemory) noexcept {
_keepVirtualMemory = keepVirtualMemory;
}
// --------------------------------------------------------------------------
// [Alloc / Release]
// --------------------------------------------------------------------------
//! Allocate a `size` bytes of virtual memory.
//!
//! Note that if you are implementing your own virtual memory manager then you
//! can quitly ignore type of allocation. This is mainly for AsmJit to memory
//! manager that allocated memory will be never freed.
ASMJIT_API void* alloc(size_t size, uint32_t type = kVMemAllocFreeable) noexcept;
//! Free previously allocated memory at a given `address`.
ASMJIT_API Error release(void* p) noexcept;
//! Free extra memory allocated with `p`.
ASMJIT_API Error shrink(void* p, size_t used) noexcept;
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
#if ASMJIT_OS_WINDOWS
//! Process passed to `VirtualAllocEx` and `VirtualFree`.
HANDLE _hProcess;
#endif // ASMJIT_OS_WINDOWS
//! Lock to enable thread-safe functionality.
Lock _lock;
//! Default block size.
size_t _blockSize;
//! Default block density.
size_t _blockDensity;
// Whether to keep virtual memory after destroy.
bool _keepVirtualMemory;
//! How many bytes are currently allocated.
size_t _allocatedBytes;
//! How many bytes are currently used.
size_t _usedBytes;
//! \internal
//! \{
struct RbNode;
struct MemNode;
struct PermanentNode;
// Memory nodes root.
MemNode* _root;
// Memory nodes list.
MemNode* _first;
MemNode* _last;
MemNode* _optimal;
// Permanent memory.
PermanentNode* _permanent;
//! \}
};
//! \}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
// [Guard]
#endif // _ASMJIT_BASE_VMEM_H
+193
View File
@@ -0,0 +1,193 @@
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Export]
#define ASMJIT_EXPORTS
// [Dependencies]
#include "../base/utils.h"
#include "../base/zone.h"
#include <stdarg.h>
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
//! Zero size block used by `Zone` that doesn't have any memory allocated.
static const Zone::Block Zone_zeroBlock = {
nullptr, nullptr, nullptr, nullptr, { 0 }
};
// ============================================================================
// [asmjit::Zone - Construction / Destruction]
// ============================================================================
Zone::Zone(size_t blockSize) noexcept {
_block = const_cast<Zone::Block*>(&Zone_zeroBlock);
_blockSize = blockSize;
}
Zone::~Zone() noexcept {
reset(true);
}
// ============================================================================
// [asmjit::Zone - Reset]
// ============================================================================
void Zone::reset(bool releaseMemory) noexcept {
Block* cur = _block;
// Can't be altered.
if (cur == &Zone_zeroBlock)
return;
if (releaseMemory) {
// Since cur can be in the middle of the double-linked list, we have to
// traverse to both directions `prev` and `next` separately.
Block* next = cur->next;
do {
Block* prev = cur->prev;
ASMJIT_FREE(cur);
cur = prev;
} while (cur != nullptr);
cur = next;
while (cur != nullptr) {
next = cur->next;
ASMJIT_FREE(cur);
cur = next;
}
_block = const_cast<Zone::Block*>(&Zone_zeroBlock);
}
else {
while (cur->prev != nullptr)
cur = cur->prev;
cur->pos = cur->data;
_block = cur;
}
}
// ============================================================================
// [asmjit::Zone - Alloc]
// ============================================================================
void* Zone::_alloc(size_t size) noexcept {
Block* curBlock = _block;
size_t blockSize = Utils::iMax<size_t>(_blockSize, size);
// The `_alloc()` method can only be called if there is not enough space
// in the current block, see `alloc()` implementation for more details.
ASMJIT_ASSERT(curBlock == &Zone_zeroBlock || curBlock->getRemainingSize() < size);
// If the `Zone` has been reset the current block doesn't have to be the
// last one. Check if there is a block that can be used instead of allocating
// a new one. If there is a `next` block it's completely unused, we don't have
// to check for remaining bytes.
Block* next = curBlock->next;
if (next != nullptr && next->getBlockSize() >= size) {
next->pos = next->data + size;
_block = next;
return static_cast<void*>(next->data);
}
// Prevent arithmetic overflow.
if (blockSize > ~static_cast<size_t>(0) - sizeof(Block))
return nullptr;
Block* newBlock = static_cast<Block*>(ASMJIT_ALLOC(sizeof(Block) - sizeof(void*) + blockSize));
if (newBlock == nullptr)
return nullptr;
newBlock->pos = newBlock->data + size;
newBlock->end = newBlock->data + blockSize;
newBlock->prev = nullptr;
newBlock->next = nullptr;
if (curBlock != &Zone_zeroBlock) {
newBlock->prev = curBlock;
curBlock->next = newBlock;
// Does only happen if there is a next block, but the requested memory
// can't fit into it. In this case a new buffer is allocated and inserted
// between the current block and the next one.
if (next != nullptr) {
newBlock->next = next;
next->prev = newBlock;
}
}
_block = newBlock;
return static_cast<void*>(newBlock->data);
}
void* Zone::allocZeroed(size_t size) noexcept {
void* p = alloc(size);
if (p != nullptr)
::memset(p, 0, size);
return p;
}
void* Zone::dup(const void* data, size_t size) noexcept {
if (data == nullptr)
return nullptr;
if (size == 0)
return nullptr;
void* m = alloc(size);
if (m == nullptr)
return nullptr;
::memcpy(m, data, size);
return m;
}
char* Zone::sdup(const char* str) noexcept {
if (str == nullptr)
return nullptr;
size_t len = ::strlen(str);
if (len == 0)
return nullptr;
// Include NULL terminator and limit string length.
if (++len > 256)
len = 256;
char* m = static_cast<char*>(alloc(len));
if (m == nullptr)
return nullptr;
::memcpy(m, str, len);
m[len - 1] = '\0';
return m;
}
char* Zone::sformat(const char* fmt, ...) noexcept {
if (fmt == nullptr)
return nullptr;
char buf[512];
size_t len;
va_list ap;
va_start(ap, fmt);
len = vsnprintf(buf, ASMJIT_ARRAY_SIZE(buf) - 1, fmt, ap);
buf[len++] = 0;
va_end(ap);
return static_cast<char*>(dup(buf, len));
}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
+220
View File
@@ -0,0 +1,220 @@
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Guard]
#ifndef _ASMJIT_BASE_ZONE_H
#define _ASMJIT_BASE_ZONE_H
// [Dependencies]
#include "../base/globals.h"
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
//! \addtogroup asmjit_base
//! \{
// ============================================================================
// [asmjit::Zone]
// ============================================================================
//! Zone memory allocator.
//!
//! Zone is an incremental memory allocator that allocates memory by simply
//! incrementing a pointer. It allocates blocks of memory by using standard
//! C library `malloc/free`, but divides these blocks into smaller segments
//! requirested by calling `Zone::alloc()` and friends.
//!
//! Zone memory allocators are designed to allocate data of short lifetime. The
//! data used by `Assembler` and `Compiler` has a very short lifetime, thus, is
//! allocated by `Zone`. The advantage is that `Zone` can free all of the data
//! allocated at once by calling `reset()` or by `Zone` destructor.
class Zone {
public:
//! \internal
//!
//! A single block of memory.
struct Block {
// ------------------------------------------------------------------------
// [Accessors]
// ------------------------------------------------------------------------
//! Get the size of the block.
ASMJIT_INLINE size_t getBlockSize() const noexcept {
return (size_t)(end - data);
}
//! Get count of remaining bytes in the block.
ASMJIT_INLINE size_t getRemainingSize() const noexcept {
return (size_t)(end - pos);
}
// ------------------------------------------------------------------------
// [Members]
// ------------------------------------------------------------------------
//! Current data pointer (pointer to the first available byte).
uint8_t* pos;
//! End data pointer (pointer to the first invalid byte).
uint8_t* end;
//! Link to the previous block.
Block* prev;
//! Link to the next block.
Block* next;
//! Data.
uint8_t data[sizeof(void*)];
};
enum {
//! Zone allocator overhead.
kZoneOverhead =
kMemAllocOverhead
+ static_cast<int>(sizeof(Block) - sizeof(void*))
};
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create a new instance of `Zone` allocator.
//!
//! The `blockSize` parameter describes the default size of the block. If the
//! `size` parameter passed to `alloc()` is greater than the default size
//! `Zone` will allocate and use a larger block, but it will not change the
//! default `blockSize`.
//!
//! It's not required, but it's good practice to set `blockSize` to a
//! reasonable value that depends on the usage of `Zone`. Greater block sizes
//! are generally safer and performs better than unreasonably low values.
ASMJIT_API Zone(size_t blockSize) noexcept;
//! Destroy the `Zone` instance.
//!
//! This will destroy the `Zone` instance and release all blocks of memory
//! allocated by it. It performs implicit `reset(true)`.
ASMJIT_API ~Zone() noexcept;
// --------------------------------------------------------------------------
// [Reset]
// --------------------------------------------------------------------------
//! Reset the `Zone` invalidating all blocks allocated.
//!
//! If `releaseMemory` is true all buffers will be released to the system.
ASMJIT_API void reset(bool releaseMemory = false) noexcept;
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Get the default block size.
ASMJIT_INLINE size_t getBlockSize() const noexcept {
return _blockSize;
}
// --------------------------------------------------------------------------
// [Alloc]
// --------------------------------------------------------------------------
//! Allocate `size` bytes of memory.
//!
//! Pointer returned is valid until the `Zone` instance is destroyed or reset
//! by calling `reset()`. If you plan to make an instance of C++ from the
//! given pointer use placement `new` and `delete` operators:
//!
//! ~~~
//! using namespace asmjit;
//!
//! class Object { ... };
//!
//! // Create Zone with default block size of approximately 65536 bytes.
//! Zone zone(65536 - Zone::kZoneOverhead);
//!
//! // Create your objects using zone object allocating, for example:
//! Object* obj = static_cast<Object*>( zone.alloc(sizeof(Object)) );
//
//! if (obj == nullptr) {
//! // Handle out of memory error.
//! }
//!
//! // Placement `new` and `delete` operators can be used to instantiate it.
//! new(obj) Object();
//!
//! // ... lifetime of your objects ...
//!
//! // To destroy the instance (if required).
//! obj->~Object();
//!
//! // Reset or destroy `Zone`.
//! zone.reset();
//! ~~~
ASMJIT_INLINE void* alloc(size_t size) noexcept {
Block* cur = _block;
uint8_t* ptr = cur->pos;
size_t remainingBytes = (size_t)(cur->end - ptr);
if (remainingBytes < size)
return _alloc(size);
cur->pos += size;
ASMJIT_ASSERT(cur->pos <= cur->end);
return (void*)ptr;
}
//! Allocate `size` bytes of zeroed memory.
//!
//! See \ref alloc() for more details.
ASMJIT_API void* allocZeroed(size_t size) noexcept;
//! Like `alloc()`, but the return pointer is casted to `T*`.
template<typename T>
ASMJIT_INLINE T* allocT(size_t size = sizeof(T)) noexcept {
return static_cast<T*>(alloc(size));
}
//! Like `allocZeroed()`, but the return pointer is casted to `T*`.
template<typename T>
ASMJIT_INLINE T* allocZeroedT(size_t size = sizeof(T)) noexcept {
return static_cast<T*>(allocZeroed(size));
}
//! \internal
ASMJIT_API void* _alloc(size_t size) noexcept;
//! Helper to duplicate data.
ASMJIT_API void* dup(const void* data, size_t size) noexcept;
//! Helper to duplicate string.
ASMJIT_API char* sdup(const char* str) noexcept;
//! Helper to duplicate formatted string, maximum length is 256 bytes.
ASMJIT_API char* sformat(const char* str, ...) noexcept;
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! The current block.
Block* _block;
//! Default block size.
size_t _blockSize;
};
//! \}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
// [Guard]
#endif // _ASMJIT_BASE_ZONE_H