AsmJit
Low-Latency Machine Code Generation
Documentation of breaking changes.
AsmJit is a live project that is being actively developed. Deprecating the existing API in favor of a new one is preferred, but it's not always possible if the changes are significant. AsmJit authors prefer to do accumulated breaking changes at once instead of breaking the API often. This page documents deprecated and removed APIs and should serve as a how-to guide for people that want to port existing code to work with the newest AsmJit.
Useful tips before you start:
ASMJIT_NO_DEPRECATED
macro defined to make sure that you are not using deprecated functionality at all. Deprecated functions are decorated with ASMJIT_DEPRECATED()
macro, but sometimes it's not possible to decorate everything like classes, which are used by deprecated functions as well, because some compilers would warn about that. If your project compiles fine with ASMJIT_NO_DEPRECATED
it's not using anything, which was deprecated.Core changes:
eq()
to equals()
- Only related to String
, ZoneVector
, and CpuFeatures
. Old function names were deprecated.CallConvId::kNone
in favor of CallConvId::kCDecl
, which is now the default calling convention.CallConvId::kHost
in favor of CallConvId::kCDecl
- host calling convention is now not part of CallConvId, it can be calculated from CallConvId and Environment instead.Core changes:
a64::Vec::ElementType
to a64::VecElementType
and made it a typed enum. This enum was used mostly internally, but there is a public API using it, so it's a breaking change.FuncSignature
, FuncSignatureT
, and FuncSignatureBuilder
. There is only FuncSignature
now, which acts as a function signature holder and builder. Replace FuncSignatureBuilder
with FuncSignature
and use FuncSignature::build<args>
instead of FuncSignatureT<args>
. The old API has been deprecated.Core changes:
arm::Gp
to a64::Gp
and arm::Vec
to a64::Vec
.Core changes:
JitAllocator::Span
now represents a memory that the user can access. It abstracts both regular and dual mappings.Span
is mostly designed to make it possible to write into it, so in general the read+execute pointer is what user is intended to keep. Use span.rx()
to access RX pointer. Span
is not needed after the memory it references has been modified, only remember span.rx()
pointer, which is then used to deallocate or change the memory the span references.JitAllocator::alloc()
to allocate a Span
, then pass the populated Span to JitAllocator
write API such as JitAllocator::write()
- note that JitAllocator can also establish a scope, so you can use a lambda function that would perform the write, but since it's going through JitAllocator it's able to ensure that the memory is actually writable.Span
from rx pointer, use JitAllocator::query(<span-out>, rx)
to get it.AVX512_CDI
to AVX512_CD
.AVX512_ERI
to AVX512_ER
.AVX512_PFI
to AVX512_PF
.Core changes:
OperandSignature
.Compiler::setArg()
was deprecated, use FuncNode::setArg() instead.{arch}Features::k
to CpuFeatures::{arch}k
.BaseEmitter::kEncodingOption
to EncodingOptions::k
.BaseEmitter::kFlag
to EmitterFlags::k
.BaseEmitter::kType
to EmitterType::k
.BaseEmitter::kValidationOption
to DiagnosticOptions::kValidate
.BaseFeatures
to CpuFeatures
.BaseInst::kControl
to InstControlFlow::k
.BaseInst::kOption
and x86::Inst::kOption
to InstOptions::k
.BaseNode::kNode
to NodeType::k
.BaseReg::kGroup
and x86::Reg::kGroup
to RegGroup::k
.BaseReg::kType
and x86::Reg::kType
to RegType::k
.CallConv::kFlag
to CallConvFlags::k
.CallConv::kId
to CallConvId::k
.CallConv::kStrategy
to CallConvStrategy::k
.CodeBuffer::kFlag
to CodeBufferFlags
.ConstPool::kScope
to ConstPoolScope::k
.Environment::kArch
to Arch::k
.Environment::kSubArch
to SubArch::k
.Environment::kFormat
to OjectFormat::k
.Environment::kPlatform
to Platform::k
.Environment::kAbi
to PlatformABI::k
.Environment::kVendor
to Vendor::k
.FormatOptions::kFlag
to FormatFlags::k
and DiagnosticOptions::k
(Compiler diagnostics flags).FormatOptions::kIndentation
to FormatIndentationGroup::k
.FuncFrame::kAttr
to FuncAttributes::k
.Globals::kReset
to ResetPolicy::k
.InstDB::kAvx512Flag
to InstDB::Avx512Flags::k
.InstDB::kFlag
to InstDB::InstFlags::k
.InstDB::kMemFlag
to InstDB::OpFlags::kMem
.InstDB::kMode
to InstDB::Mode::k
.InstDB::kOpFlag
to InstDB::OpFlags::k{OpType}...
.JitAllocator::kOption
to JitAllocatorOptions::k
.Label::kType
to LabelType::k
.Operand::kOpType
to OperandType::k
.OpRWInfo::kFlag
to OpRWFlags::k
.Type::kId
to TypeId::k
.VirtMem::k
to VirtMem::MemoryFlags::k
.AsmJit has been cleaned up significantly, many todo items have been fixed and many functions and classes have been redesigned, some in an incompatible way.
Core changes:
Imm
operand has now only Imm::value()
and Imm::valueAs()
functions that return its value content, and Imm::setValue()
function that sets the content. Functions like setI8()
, setU8()
were deprecated.
Old functions were deprecated, but code using them should still compile.
ArchInfo
has been replaced with Environment
. Environment provides more details about the architecture, but drops some properties that were used by arch info - gpSize(
) and gpCount()
. gpSize()
can be replaced with registerSize()
getter, which returns a native register size of the architecture the environment uses. However, gpCount()
was removed - at the moment ArchTraits
can be used to access such properties.
Some other functions were renamed, like ArchInfo::isX86Family()
is now Environment::isFamilyX86()
, etc. The reason for changing the order was support for more properties and all the accessors now start with the type of the property, like Environment::isPlatformWindows()
.
This function causes many other classes to provide environment()
getter instead of archInfo()
getter. In addition, AsmJit now uses arch()
to get an architecture instead of archId()
. ArchInfo::kIdXXX
was renamed to Environment::kArchXXX
.
Some functions were deprecated, some removed...
CodeInfo
has been removed in favor of Environment
. If you used CodeInfo
to set architecture and base address, this is now possible with Environment
and setting base address explicitly by CodeHolder::init()
Environment
, and the second argument is base address, which defaults to Globals::kNoBaseAddress
.CodeInfo class was deprecated, but the code using it should still compile with warnings.
CallConv
has been updated to offer a more unified way of representing calling conventions - many calling conventions were abstracted to follow standard naming like CallConvId::kCDecl
or CallConvId::kStdCall
.
This change means that other APIs like FuncDetail::init()
now require both, calling convention and target Environment
.
Logging
namespace has been renamed to Formatter
, which now provides general functionality for formatting in AsmJit.
Logging namespace should still work, but its use is deprecated. Unfortunately this will be without deprecation warnings, so make sure you don't use it.
Data64
, Data128
, and Data256
structs were deprecated and should no longer be used. There is no replacement, AsmJit users should simply create their own structures if they need them or use the new repeated embed API in emitters, see BaseEmitter::embedDataArray()
.Emitter changes:
BaseEmitter::emit()
function signature has been changed to accept 3 operands by reference and the rest 3 operands as a continuous array. This change is purely cosmetic and shouldn't affect users as emit() has many overloads that dispatch to the right function.x86::Emitter
(Assembler, Builder, Compiler) deprecates embed utilities like dint8()
, duint8()
, duint16()
, dxmm()
, etc... in favor of a new and more powerful BaseEmitter::embedDataArray()
. This function also allows emitting repeated values and/or patterns, which is used by helpers BaseEmitter::embedUInt8()
, and others...BaseEmitter::DiagnosticOptions
, which can be enabled/disabled through BaseEmitter::addDiagnosticOptions()
and BaseEmitter::clearDiagnosticOptions()
, respectively. Validation options now separate between encoding and Builder/Compiler so it's possible to choose the granularity required.Builder changes:
Compiler changes:
FuncCallNode
has been renamed to InvokeNode
. Additionally, function calls should now use x86::Compiler::invoke()
instead of call()
. The reason behind this is to remove the confusion between a call
instruction and AsmJit's call()
intrinsic, which is now invoke()
.Creating new nodes also changed. Now the preferred way of invoking a function is to call x86::Compiler::invoke()
where the first argument is InvokeNode**
. The function now returns an error and would call ErrorHandler
in case of a failure. Error handling was unspecified in the past - the function was marked noexcept, but called error handler, which could throw.
The reason behind this change is to make the API consistent with other changes and to also make it possible to inspect the possible error. In the previous API it returned a new node or nullptr
in case of error, which the user couldn't inspect unless there was an attached ErrorHandler
.
Samples: