334 lines
9.0 KiB
C++
334 lines
9.0 KiB
C++
// This file is part of AsmJit project <https://asmjit.com>
|
|
//
|
|
// See asmjit.h or LICENSE.md for license and copyright information
|
|
// SPDX-License-Identifier: Zlib
|
|
|
|
#include "../core/api-build_p.h"
|
|
#include "../core/emitterutils_p.h"
|
|
#include "../core/errorhandler.h"
|
|
#include "../core/logger.h"
|
|
#include "../core/support.h"
|
|
|
|
ASMJIT_BEGIN_NAMESPACE
|
|
|
|
// BaseEmitter - Construction & Destruction
|
|
// ========================================
|
|
|
|
BaseEmitter::BaseEmitter(EmitterType emitterType) noexcept
|
|
: _emitterType(emitterType) {}
|
|
|
|
BaseEmitter::~BaseEmitter() noexcept {
|
|
if (_code) {
|
|
_addEmitterFlags(EmitterFlags::kDestroyed);
|
|
_code->detach(this);
|
|
}
|
|
}
|
|
|
|
// BaseEmitter - Finalize
|
|
// ======================
|
|
|
|
Error BaseEmitter::finalize() {
|
|
// Does nothing by default, overridden by `BaseBuilder` and `BaseCompiler`.
|
|
return kErrorOk;
|
|
}
|
|
|
|
// BaseEmitter - Internals
|
|
// =======================
|
|
|
|
static constexpr EmitterFlags kEmitterPreservedFlags = EmitterFlags::kOwnLogger | EmitterFlags::kOwnErrorHandler;
|
|
|
|
static ASMJIT_NOINLINE void BaseEmitter_updateForcedOptions(BaseEmitter* self) noexcept {
|
|
bool emitComments = false;
|
|
bool hasDiagnosticOptions = false;
|
|
|
|
if (self->emitterType() == EmitterType::kAssembler) {
|
|
// Assembler: Don't emit comments if logger is not attached.
|
|
emitComments = self->_code != nullptr && self->_logger != nullptr;
|
|
hasDiagnosticOptions = self->hasDiagnosticOption(DiagnosticOptions::kValidateAssembler);
|
|
}
|
|
else {
|
|
// Builder/Compiler: Always emit comments, we cannot assume they won't be used.
|
|
emitComments = self->_code != nullptr;
|
|
hasDiagnosticOptions = self->hasDiagnosticOption(DiagnosticOptions::kValidateIntermediate);
|
|
}
|
|
|
|
if (emitComments)
|
|
self->_addEmitterFlags(EmitterFlags::kLogComments);
|
|
else
|
|
self->_clearEmitterFlags(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 || hasDiagnosticOptions)
|
|
self->_forcedInstOptions |= InstOptions::kReserved;
|
|
else
|
|
self->_forcedInstOptions &= ~InstOptions::kReserved;
|
|
}
|
|
|
|
// BaseEmitter - Diagnostic Options
|
|
// ================================
|
|
|
|
void BaseEmitter::addDiagnosticOptions(DiagnosticOptions options) noexcept {
|
|
_diagnosticOptions |= options;
|
|
BaseEmitter_updateForcedOptions(this);
|
|
}
|
|
|
|
void BaseEmitter::clearDiagnosticOptions(DiagnosticOptions options) noexcept {
|
|
_diagnosticOptions &= ~options;
|
|
BaseEmitter_updateForcedOptions(this);
|
|
}
|
|
|
|
// BaseEmitter - Logging
|
|
// =====================
|
|
|
|
void BaseEmitter::setLogger(Logger* logger) noexcept {
|
|
#ifndef ASMJIT_NO_LOGGING
|
|
if (logger) {
|
|
_logger = logger;
|
|
_addEmitterFlags(EmitterFlags::kOwnLogger);
|
|
}
|
|
else {
|
|
_logger = nullptr;
|
|
_clearEmitterFlags(EmitterFlags::kOwnLogger);
|
|
if (_code)
|
|
_logger = _code->logger();
|
|
}
|
|
BaseEmitter_updateForcedOptions(this);
|
|
#else
|
|
DebugUtils::unused(logger);
|
|
#endif
|
|
}
|
|
|
|
// BaseEmitter - Error Handling
|
|
// ============================
|
|
|
|
void BaseEmitter::setErrorHandler(ErrorHandler* errorHandler) noexcept {
|
|
if (errorHandler) {
|
|
_errorHandler = errorHandler;
|
|
_addEmitterFlags(EmitterFlags::kOwnErrorHandler);
|
|
}
|
|
else {
|
|
_errorHandler = nullptr;
|
|
_clearEmitterFlags(EmitterFlags::kOwnErrorHandler);
|
|
if (_code)
|
|
_errorHandler = _code->errorHandler();
|
|
}
|
|
}
|
|
|
|
Error BaseEmitter::reportError(Error err, const char* message) {
|
|
ErrorHandler* eh = _errorHandler;
|
|
if (eh) {
|
|
if (!message)
|
|
message = DebugUtils::errorAsString(err);
|
|
eh->handleError(err, message, this);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
// BaseEmitter - Labels
|
|
// ====================
|
|
|
|
Label BaseEmitter::labelByName(const char* name, size_t nameSize, uint32_t parentId) noexcept {
|
|
return Label(_code ? _code->labelIdByName(name, nameSize, parentId) : Globals::kInvalidId);
|
|
}
|
|
|
|
bool BaseEmitter::isLabelValid(uint32_t labelId) const noexcept {
|
|
return _code && labelId < _code->labelCount();
|
|
}
|
|
|
|
// BaseEmitter - Emit (Low-Level)
|
|
// ==============================
|
|
|
|
using EmitterUtils::noExt;
|
|
|
|
Error BaseEmitter::_emitI(InstId instId) {
|
|
return _emit(instId, noExt[0], noExt[1], noExt[2], noExt);
|
|
}
|
|
|
|
Error BaseEmitter::_emitI(InstId instId, const Operand_& o0) {
|
|
return _emit(instId, o0, noExt[1], noExt[2], noExt);
|
|
}
|
|
|
|
Error BaseEmitter::_emitI(InstId instId, const Operand_& o0, const Operand_& o1) {
|
|
return _emit(instId, o0, o1, noExt[2], noExt);
|
|
}
|
|
|
|
Error BaseEmitter::_emitI(InstId instId, const Operand_& o0, const Operand_& o1, const Operand_& o2) {
|
|
return _emit(instId, o0, o1, o2, noExt);
|
|
}
|
|
|
|
Error BaseEmitter::_emitI(InstId instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3) {
|
|
Operand_ opExt[3] = { o3 };
|
|
return _emit(instId, o0, o1, o2, opExt);
|
|
}
|
|
|
|
Error BaseEmitter::_emitI(InstId instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4) {
|
|
Operand_ opExt[3] = { o3, o4 };
|
|
return _emit(instId, o0, o1, o2, opExt);
|
|
}
|
|
|
|
Error BaseEmitter::_emitI(InstId instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4, const Operand_& o5) {
|
|
Operand_ opExt[3] = { o3, o4, o5 };
|
|
return _emit(instId, o0, o1, o2, opExt);
|
|
}
|
|
|
|
Error BaseEmitter::_emitOpArray(InstId instId, const Operand_* operands, size_t opCount) {
|
|
const Operand_* op = operands;
|
|
Operand_ opExt[3];
|
|
|
|
switch (opCount) {
|
|
case 0:
|
|
return _emit(instId, noExt[0], noExt[1], noExt[2], noExt);
|
|
|
|
case 1:
|
|
return _emit(instId, op[0], noExt[1], noExt[2], noExt);
|
|
|
|
case 2:
|
|
return _emit(instId, op[0], op[1], noExt[2], noExt);
|
|
|
|
case 3:
|
|
return _emit(instId, op[0], op[1], op[2], noExt);
|
|
|
|
case 4:
|
|
opExt[0] = op[3];
|
|
opExt[1].reset();
|
|
opExt[2].reset();
|
|
return _emit(instId, op[0], op[1], op[2], opExt);
|
|
|
|
case 5:
|
|
opExt[0] = op[3];
|
|
opExt[1] = op[4];
|
|
opExt[2].reset();
|
|
return _emit(instId, op[0], op[1], op[2], opExt);
|
|
|
|
case 6:
|
|
return _emit(instId, op[0], op[1], op[2], op + 3);
|
|
|
|
default:
|
|
return DebugUtils::errored(kErrorInvalidArgument);
|
|
}
|
|
}
|
|
|
|
// BaseEmitter - Emit Utilities
|
|
// ============================
|
|
|
|
Error BaseEmitter::emitProlog(const FuncFrame& frame) {
|
|
if (ASMJIT_UNLIKELY(!_code))
|
|
return DebugUtils::errored(kErrorNotInitialized);
|
|
|
|
return _funcs.emitProlog(this, frame);
|
|
}
|
|
|
|
Error BaseEmitter::emitEpilog(const FuncFrame& frame) {
|
|
if (ASMJIT_UNLIKELY(!_code))
|
|
return DebugUtils::errored(kErrorNotInitialized);
|
|
|
|
return _funcs.emitEpilog(this, frame);
|
|
}
|
|
|
|
Error BaseEmitter::emitArgsAssignment(const FuncFrame& frame, const FuncArgsAssignment& args) {
|
|
if (ASMJIT_UNLIKELY(!_code))
|
|
return DebugUtils::errored(kErrorNotInitialized);
|
|
|
|
return _funcs.emitArgsAssignment(this, frame, args);
|
|
}
|
|
|
|
// BaseEmitter - Comment
|
|
// =====================
|
|
|
|
Error BaseEmitter::commentf(const char* fmt, ...) {
|
|
if (!hasEmitterFlag(EmitterFlags::kLogComments)) {
|
|
if (!hasEmitterFlag(EmitterFlags::kAttached))
|
|
return reportError(DebugUtils::errored(kErrorNotInitialized));
|
|
return kErrorOk;
|
|
}
|
|
|
|
#ifndef ASMJIT_NO_LOGGING
|
|
StringTmp<1024> sb;
|
|
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
Error err = sb.appendVFormat(fmt, ap);
|
|
va_end(ap);
|
|
|
|
ASMJIT_PROPAGATE(err);
|
|
return comment(sb.data(), sb.size());
|
|
#else
|
|
DebugUtils::unused(fmt);
|
|
return kErrorOk;
|
|
#endif
|
|
}
|
|
|
|
Error BaseEmitter::commentv(const char* fmt, va_list ap) {
|
|
if (!hasEmitterFlag(EmitterFlags::kLogComments)) {
|
|
if (!hasEmitterFlag(EmitterFlags::kAttached))
|
|
return reportError(DebugUtils::errored(kErrorNotInitialized));
|
|
return kErrorOk;
|
|
}
|
|
|
|
#ifndef ASMJIT_NO_LOGGING
|
|
StringTmp<1024> sb;
|
|
Error err = sb.appendVFormat(fmt, ap);
|
|
|
|
ASMJIT_PROPAGATE(err);
|
|
return comment(sb.data(), sb.size());
|
|
#else
|
|
DebugUtils::unused(fmt, ap);
|
|
return kErrorOk;
|
|
#endif
|
|
}
|
|
|
|
// BaseEmitter - Events
|
|
// ====================
|
|
|
|
Error BaseEmitter::onAttach(CodeHolder* code) noexcept {
|
|
_code = code;
|
|
_environment = code->environment();
|
|
_addEmitterFlags(EmitterFlags::kAttached);
|
|
|
|
const ArchTraits& archTraits = ArchTraits::byArch(code->arch());
|
|
RegType nativeRegType = Environment::is32Bit(code->arch()) ? RegType::kGp32 : RegType::kGp64;
|
|
_gpSignature = archTraits.regTypeToSignature(nativeRegType);
|
|
|
|
onSettingsUpdated();
|
|
return kErrorOk;
|
|
}
|
|
|
|
Error BaseEmitter::onDetach(CodeHolder* code) noexcept {
|
|
DebugUtils::unused(code);
|
|
|
|
if (!hasOwnLogger())
|
|
_logger = nullptr;
|
|
|
|
if (!hasOwnErrorHandler())
|
|
_errorHandler = nullptr;
|
|
|
|
_clearEmitterFlags(~kEmitterPreservedFlags);
|
|
_forcedInstOptions = InstOptions::kReserved;
|
|
_privateData = 0;
|
|
|
|
_environment.reset();
|
|
_gpSignature.reset();
|
|
|
|
_instOptions = InstOptions::kNone;
|
|
_extraReg.reset();
|
|
_inlineComment = nullptr;
|
|
|
|
return kErrorOk;
|
|
}
|
|
|
|
void BaseEmitter::onSettingsUpdated() noexcept {
|
|
// Only called when attached to CodeHolder by CodeHolder.
|
|
ASMJIT_ASSERT(_code != nullptr);
|
|
|
|
if (!hasOwnLogger())
|
|
_logger = _code->logger();
|
|
|
|
if (!hasOwnErrorHandler())
|
|
_errorHandler = _code->errorHandler();
|
|
|
|
BaseEmitter_updateForcedOptions(this);
|
|
}
|
|
|
|
ASMJIT_END_NAMESPACE
|