mirror of
https://github.com/asmjit/asmjit
synced 2026-06-08 13:13:30 +00:00
b56f4176cb
* Denested src folder to root, renamed testing to asmjit-testing
* Refactored how headers are included into <asmjit/...> form. This
is necessary as compilers would never simplify a path once a ..
appears in include directory - then paths such as ../core/../core
appeared in asserts, which was ugly
* Moved support utilities into asmjit/support/... (still included
by asmjit/core.h for convenience and compatibility)
* Added CMakePresets.json for making it easy to develop AsmJit
* Reworked CMakeLists to be shorter and use CMake option(),
etc... This simplifies it and makes it using more standard
features
* ASMJIT_EMBED now creates asmjit_embed INTERFACE library,
which is accessible via asmjit::asmjit target - this simplifies
embedding and makes it the same as library targets from a CMake
perspective
* Removed ASMJIT_DEPS - this is now provided by cmake target
aliases - 'asmjit::asmjit' so users should not need this variable
* Changed meaning of ASMJIT_LIBS - this now contains only AsmJit
dependencies without asmjit::asmjit target alias. Don't rely on
ASMJIT_LIBS anymore as it's only used internally
* Removed ASMJIT_NO_DEPRECATED option - AsmJit is not going
to provide controllable deprecations in the future
* Removed ASMJIT_NO_VALIDATION in favor of ASMJIT_NO_INTROSPECTION,
which now controls query, features, and validation API presence
* Removed ASMJIT_DIR option - it was never really needed
* Removed AMX_TRANSPOSE feature from instruction database (X86).
Intel has removed it as well, so it's a feature that won't
be siliconized
444 lines
12 KiB
C++
444 lines
12 KiB
C++
// This file is part of AsmJit project <https://asmjit.com>
|
|
//
|
|
// See <asmjit/core.h> or LICENSE.md for license and copyright information
|
|
// SPDX-License-Identifier: Zlib
|
|
|
|
#include <asmjit/core/api-build_p.h>
|
|
#include <asmjit/core/emitterutils_p.h>
|
|
#include <asmjit/core/errorhandler.h>
|
|
#include <asmjit/core/logger.h>
|
|
#include <asmjit/support/support.h>
|
|
|
|
ASMJIT_BEGIN_NAMESPACE
|
|
|
|
// BaseEmitter - Construction & Destruction
|
|
// ========================================
|
|
|
|
BaseEmitter::BaseEmitter(EmitterType emitter_type) noexcept
|
|
: _emitter_type(emitter_type) {}
|
|
|
|
BaseEmitter::~BaseEmitter() noexcept {
|
|
if (_code) {
|
|
_add_emitter_flags(EmitterFlags::kDestroyed);
|
|
_code->detach(this);
|
|
}
|
|
}
|
|
|
|
// BaseEmitter - Finalize
|
|
// ======================
|
|
|
|
Error BaseEmitter::finalize() {
|
|
// Does nothing by default, overridden by `BaseBuilder` and `BaseCompiler`.
|
|
return Error::kOk;
|
|
}
|
|
|
|
// BaseEmitter - Internals
|
|
// =======================
|
|
|
|
static constexpr EmitterFlags kEmitterPreservedFlags = EmitterFlags::kOwnLogger | EmitterFlags::kOwnErrorHandler;
|
|
|
|
static ASMJIT_NOINLINE void BaseEmitter_updateForcedOptions(BaseEmitter* self) noexcept {
|
|
bool emit_comments = false;
|
|
bool has_diagnostic_options = false;
|
|
|
|
if (self->emitter_type() == EmitterType::kAssembler) {
|
|
// Assembler: Don't emit comments if logger is not attached.
|
|
emit_comments = self->_code != nullptr && self->_logger != nullptr;
|
|
has_diagnostic_options = self->has_diagnostic_option(DiagnosticOptions::kValidateAssembler);
|
|
}
|
|
else {
|
|
// Builder/Compiler: Always emit comments, we cannot assume they won't be used.
|
|
emit_comments = self->_code != nullptr;
|
|
has_diagnostic_options = self->has_diagnostic_option(DiagnosticOptions::kValidateIntermediate);
|
|
}
|
|
|
|
if (emit_comments) {
|
|
self->_add_emitter_flags(EmitterFlags::kLogComments);
|
|
}
|
|
else {
|
|
self->_clear_emitter_flags(EmitterFlags::kLogComments);
|
|
}
|
|
|
|
// The reserved option tells emitter (Assembler/Builder/Compiler) that there may be either a border
|
|
// case (CodeHolder not attached, for example) or that logging or validation is required.
|
|
if (self->_code == nullptr || self->_logger || has_diagnostic_options) {
|
|
self->_forced_inst_options |= InstOptions::kReserved;
|
|
}
|
|
else {
|
|
self->_forced_inst_options &= ~InstOptions::kReserved;
|
|
}
|
|
}
|
|
|
|
// BaseEmitter - Diagnostic Options
|
|
// ================================
|
|
|
|
void BaseEmitter::add_diagnostic_options(DiagnosticOptions options) noexcept {
|
|
_diagnostic_options |= options;
|
|
BaseEmitter_updateForcedOptions(this);
|
|
}
|
|
|
|
void BaseEmitter::clear_diagnostic_options(DiagnosticOptions options) noexcept {
|
|
_diagnostic_options &= ~options;
|
|
BaseEmitter_updateForcedOptions(this);
|
|
}
|
|
|
|
// BaseEmitter - Logging
|
|
// =====================
|
|
|
|
void BaseEmitter::set_logger(Logger* logger) noexcept {
|
|
#ifndef ASMJIT_NO_LOGGING
|
|
if (logger) {
|
|
_logger = logger;
|
|
_add_emitter_flags(EmitterFlags::kOwnLogger);
|
|
}
|
|
else {
|
|
_logger = nullptr;
|
|
_clear_emitter_flags(EmitterFlags::kOwnLogger);
|
|
if (_code) {
|
|
_logger = _code->logger();
|
|
}
|
|
}
|
|
BaseEmitter_updateForcedOptions(this);
|
|
#else
|
|
Support::maybe_unused(logger);
|
|
#endif
|
|
}
|
|
|
|
// BaseEmitter - Error Handling
|
|
// ============================
|
|
|
|
void BaseEmitter::set_error_handler(ErrorHandler* error_handler) noexcept {
|
|
if (error_handler) {
|
|
_error_handler = error_handler;
|
|
_add_emitter_flags(EmitterFlags::kOwnErrorHandler);
|
|
}
|
|
else {
|
|
_error_handler = nullptr;
|
|
_clear_emitter_flags(EmitterFlags::kOwnErrorHandler);
|
|
if (_code) {
|
|
_error_handler = _code->error_handler();
|
|
}
|
|
}
|
|
}
|
|
|
|
Error BaseEmitter::_report_error(Error err, const char* message) {
|
|
ASMJIT_ASSERT(err != Error::kOk);
|
|
|
|
ErrorHandler* eh = _error_handler;
|
|
if (eh) {
|
|
if (!message) {
|
|
message = DebugUtils::error_as_string(err);
|
|
}
|
|
eh->handle_error(err, message, this);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
// BaseEmitter - Sections
|
|
// ======================
|
|
|
|
// [[pure virtual]]
|
|
Error BaseEmitter::section(Section* section) {
|
|
Support::maybe_unused(section);
|
|
return make_error(Error::kInvalidState);
|
|
}
|
|
|
|
// BaseEmitter - Labels
|
|
// ====================
|
|
|
|
// [[pure virtual]]
|
|
Label BaseEmitter::new_label() {
|
|
return Label(Globals::kInvalidId);
|
|
}
|
|
|
|
// [[pure virtual]]
|
|
Label BaseEmitter::new_named_label(const char* name, size_t name_size, LabelType type, uint32_t parent_id) {
|
|
Support::maybe_unused(name, name_size, type, parent_id);
|
|
return Label(Globals::kInvalidId);
|
|
}
|
|
|
|
Label BaseEmitter::label_by_name(const char* name, size_t name_size, uint32_t parent_id) noexcept {
|
|
return Label(_code ? _code->label_id_by_name(name, name_size, parent_id) : Globals::kInvalidId);
|
|
}
|
|
|
|
// [[pure virtual]]
|
|
Error BaseEmitter::bind(const Label& label) {
|
|
Support::maybe_unused(label);
|
|
return make_error(Error::kInvalidState);
|
|
}
|
|
|
|
bool BaseEmitter::is_label_valid(uint32_t label_id) const noexcept {
|
|
return _code && label_id < _code->label_count();
|
|
}
|
|
|
|
// BaseEmitter - Emit (Low-Level)
|
|
// ==============================
|
|
|
|
using EmitterUtils::no_ext;
|
|
|
|
Error BaseEmitter::_emitI(InstId inst_id) {
|
|
return _emit(inst_id, no_ext[0], no_ext[1], no_ext[2], no_ext);
|
|
}
|
|
|
|
Error BaseEmitter::_emitI(InstId inst_id, const Operand_& o0) {
|
|
return _emit(inst_id, o0, no_ext[1], no_ext[2], no_ext);
|
|
}
|
|
|
|
Error BaseEmitter::_emitI(InstId inst_id, const Operand_& o0, const Operand_& o1) {
|
|
return _emit(inst_id, o0, o1, no_ext[2], no_ext);
|
|
}
|
|
|
|
Error BaseEmitter::_emitI(InstId inst_id, const Operand_& o0, const Operand_& o1, const Operand_& o2) {
|
|
return _emit(inst_id, o0, o1, o2, no_ext);
|
|
}
|
|
|
|
Error BaseEmitter::_emitI(InstId inst_id, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3) {
|
|
Operand_ op_ext[3] = { o3 };
|
|
return _emit(inst_id, o0, o1, o2, op_ext);
|
|
}
|
|
|
|
Error BaseEmitter::_emitI(InstId inst_id, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4) {
|
|
Operand_ op_ext[3] = { o3, o4 };
|
|
return _emit(inst_id, o0, o1, o2, op_ext);
|
|
}
|
|
|
|
Error BaseEmitter::_emitI(InstId inst_id, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4, const Operand_& o5) {
|
|
Operand_ op_ext[3] = { o3, o4, o5 };
|
|
return _emit(inst_id, o0, o1, o2, op_ext);
|
|
}
|
|
|
|
// [[pure virtual]]
|
|
Error BaseEmitter::_emit(InstId inst_id, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_* op_ext) {
|
|
Support::maybe_unused(inst_id, o0, o1, o2, op_ext);
|
|
return make_error(Error::kInvalidState);
|
|
}
|
|
|
|
Error BaseEmitter::_emit_op_array(InstId inst_id, const Operand_* operands, size_t op_count) {
|
|
const Operand_* op = operands;
|
|
Operand_ op_ext[3];
|
|
|
|
switch (op_count) {
|
|
case 0:
|
|
return _emit(inst_id, no_ext[0], no_ext[1], no_ext[2], no_ext);
|
|
|
|
case 1:
|
|
return _emit(inst_id, op[0], no_ext[1], no_ext[2], no_ext);
|
|
|
|
case 2:
|
|
return _emit(inst_id, op[0], op[1], no_ext[2], no_ext);
|
|
|
|
case 3:
|
|
return _emit(inst_id, op[0], op[1], op[2], no_ext);
|
|
|
|
case 4:
|
|
op_ext[0] = op[3];
|
|
op_ext[1].reset();
|
|
op_ext[2].reset();
|
|
return _emit(inst_id, op[0], op[1], op[2], op_ext);
|
|
|
|
case 5:
|
|
op_ext[0] = op[3];
|
|
op_ext[1] = op[4];
|
|
op_ext[2].reset();
|
|
return _emit(inst_id, op[0], op[1], op[2], op_ext);
|
|
|
|
case 6:
|
|
return _emit(inst_id, op[0], op[1], op[2], op + 3);
|
|
|
|
default:
|
|
return make_error(Error::kInvalidArgument);
|
|
}
|
|
}
|
|
|
|
// BaseEmitter - Emit Utilities
|
|
// ============================
|
|
|
|
Error BaseEmitter::emit_prolog(const FuncFrame& frame) {
|
|
if (ASMJIT_UNLIKELY(!_code))
|
|
return make_error(Error::kNotInitialized);
|
|
|
|
return _funcs.emit_prolog(this, frame);
|
|
}
|
|
|
|
Error BaseEmitter::emit_epilog(const FuncFrame& frame) {
|
|
if (ASMJIT_UNLIKELY(!_code))
|
|
return make_error(Error::kNotInitialized);
|
|
|
|
return _funcs.emit_epilog(this, frame);
|
|
}
|
|
|
|
Error BaseEmitter::emit_args_assignment(const FuncFrame& frame, const FuncArgsAssignment& args) {
|
|
if (ASMJIT_UNLIKELY(!_code))
|
|
return make_error(Error::kNotInitialized);
|
|
|
|
return _funcs.emit_args_assignment(this, frame, args);
|
|
}
|
|
|
|
// BaseEmitter - Align
|
|
// ===================
|
|
|
|
// [[pure virtual]]
|
|
Error BaseEmitter::align(AlignMode align_mode, uint32_t alignment) {
|
|
Support::maybe_unused(align_mode, alignment);
|
|
return make_error(Error::kInvalidState);
|
|
}
|
|
|
|
// BaseEmitter - Embed
|
|
// ===================
|
|
|
|
// [[pure virtual]]
|
|
Error BaseEmitter::embed(const void* data, size_t data_size) {
|
|
Support::maybe_unused(data, data_size);
|
|
return make_error(Error::kInvalidState);
|
|
}
|
|
|
|
// [[pure virtual]]
|
|
Error BaseEmitter::embed_data_array(TypeId type_id, const void* data, size_t item_count, size_t repeat_count) {
|
|
Support::maybe_unused(type_id, data, item_count, repeat_count);
|
|
return make_error(Error::kInvalidState);
|
|
}
|
|
|
|
// [[pure virtual]]
|
|
Error BaseEmitter::embed_const_pool(const Label& label, const ConstPool& pool) {
|
|
Support::maybe_unused(label, pool);
|
|
return make_error(Error::kInvalidState);
|
|
}
|
|
|
|
// [[pure virtual]]
|
|
Error BaseEmitter::embed_label(const Label& label, size_t data_size) {
|
|
Support::maybe_unused(label, data_size);
|
|
return make_error(Error::kInvalidState);
|
|
}
|
|
|
|
// [[pure virtual]]
|
|
Error BaseEmitter::embed_label_delta(const Label& label, const Label& base, size_t data_size) {
|
|
Support::maybe_unused(label, base, data_size);
|
|
return make_error(Error::kInvalidState);
|
|
}
|
|
|
|
// BaseEmitter - Comment
|
|
// =====================
|
|
|
|
// [[pure virtual]]
|
|
Error BaseEmitter::comment(const char* data, size_t size) {
|
|
Support::maybe_unused(data, size);
|
|
return make_error(Error::kInvalidState);
|
|
}
|
|
|
|
Error BaseEmitter::commentf(const char* fmt, ...) {
|
|
if (!has_emitter_flag(EmitterFlags::kLogComments)) {
|
|
if (!has_emitter_flag(EmitterFlags::kAttached)) {
|
|
return report_error(make_error(Error::kNotInitialized));
|
|
}
|
|
return Error::kOk;
|
|
}
|
|
|
|
#ifndef ASMJIT_NO_LOGGING
|
|
StringTmp<1024> sb;
|
|
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
Error err = sb.append_vformat(fmt, ap);
|
|
va_end(ap);
|
|
|
|
ASMJIT_PROPAGATE(err);
|
|
return comment(sb.data(), sb.size());
|
|
#else
|
|
Support::maybe_unused(fmt);
|
|
return Error::kOk;
|
|
#endif
|
|
}
|
|
|
|
Error BaseEmitter::commentv(const char* fmt, va_list ap) {
|
|
if (!has_emitter_flag(EmitterFlags::kLogComments)) {
|
|
if (!has_emitter_flag(EmitterFlags::kAttached)) {
|
|
return report_error(make_error(Error::kNotInitialized));
|
|
}
|
|
return Error::kOk;
|
|
}
|
|
|
|
#ifndef ASMJIT_NO_LOGGING
|
|
StringTmp<1024> sb;
|
|
Error err = sb.append_vformat(fmt, ap);
|
|
|
|
ASMJIT_PROPAGATE(err);
|
|
return comment(sb.data(), sb.size());
|
|
#else
|
|
Support::maybe_unused(fmt, ap);
|
|
return Error::kOk;
|
|
#endif
|
|
}
|
|
|
|
// BaseEmitter - Events
|
|
// ====================
|
|
|
|
Error BaseEmitter::on_attach(CodeHolder& code) noexcept {
|
|
_code = &code;
|
|
_environment = code.environment();
|
|
_add_emitter_flags(EmitterFlags::kAttached);
|
|
|
|
_gp_signature.set_bits(
|
|
Environment::is_32bit(code.arch())
|
|
? RegTraits<RegType::kGp32>::kSignature
|
|
: RegTraits<RegType::kGp64>::kSignature
|
|
);
|
|
|
|
on_settings_updated();
|
|
return Error::kOk;
|
|
}
|
|
|
|
Error BaseEmitter::on_detach(CodeHolder& code) noexcept {
|
|
Support::maybe_unused(code);
|
|
|
|
if (!has_own_logger()) {
|
|
_logger = nullptr;
|
|
}
|
|
|
|
if (!has_own_error_handler()) {
|
|
_error_handler = nullptr;
|
|
}
|
|
|
|
_clear_emitter_flags(~kEmitterPreservedFlags);
|
|
_instruction_alignment = uint8_t(0);
|
|
_forced_inst_options = InstOptions::kReserved;
|
|
_private_data = 0;
|
|
|
|
_environment.reset();
|
|
_gp_signature.reset();
|
|
|
|
_inst_options = InstOptions::kNone;
|
|
_extra_reg.reset();
|
|
_inline_comment = nullptr;
|
|
|
|
return Error::kOk;
|
|
}
|
|
|
|
Error BaseEmitter::on_reinit(CodeHolder& code) noexcept {
|
|
ASMJIT_ASSERT(_code == &code);
|
|
Support::maybe_unused(code);
|
|
|
|
_inst_options = InstOptions::kNone;
|
|
_extra_reg.reset();
|
|
_inline_comment = nullptr;
|
|
|
|
return Error::kOk;
|
|
}
|
|
|
|
void BaseEmitter::on_settings_updated() noexcept {
|
|
// Only called when attached to CodeHolder by CodeHolder.
|
|
ASMJIT_ASSERT(_code != nullptr);
|
|
|
|
if (!has_own_logger()) {
|
|
_logger = _code->logger();
|
|
}
|
|
|
|
if (!has_own_error_handler()) {
|
|
_error_handler = _code->error_handler();
|
|
}
|
|
|
|
BaseEmitter_updateForcedOptions(this);
|
|
}
|
|
|
|
ASMJIT_END_NAMESPACE
|