IndexSupport

Support [¶]

Provides utility functions, arena allocator, and arena-backed containers.

Overview

This functionality is primarily intended for AsmJit's internal use, but is exposed to users since it may be used in public headers as well. Arena and arena-backed containers are used by many AsmJit classes, which are public, and AsmJit doesn't try to hide the use.

The arena allocator is used for most allocations within AsmJit. It is optimized for fast allocation of small objects, avoiding the overhead of malloc(). Memory is managed in large blocks that are split into smaller chunks, reducing fragmentation and improving performance.

Releasing an arena allocator invalidates memory it holds, allowing efficient cleanup without per-object destruction. Long-lived objects typically reset their data in the destructor or via reset() for allocation reuse. All AsmJit containers use Arena allocator.

Arena Allocators [¶]

  • Arena - Arena memory allocator that quickly allocates the requested memory from larger chunks and then frees everything at once. AsmJit uses Arena allocators almost everywhere as almost everything is short-lived.
  • ArenaTmp - A temporary Arena with some initial static storage. If the allocation requests fit the static storage allocated then there will be no dynamic memory allocation during the lifetime of ArenaTmp, otherwise it would act as Arena with one preallocated block at the beginning.

Arena-Allocated Containers [¶]

Using Arena-Allocated Containers [¶]

The most common data structure exposed by AsmJit is ArenaVector. It's very similar to std::vector, but the implementation doesn't use exceptions and uses the mentioned Arena allocator for increased performance and decreased memory footprint. You don't have to worry about allocations as you should not need to add items to AsmJit's data structures directly as there should be API for all required operations.

Most of the time, AsmJit returns a non-owning Span instead of a reference to the allocator when it returns an array of something. For example, the following APIs in CodeHolder return a non-owning Span instance:

using namespace asmjit;
void example(CodeHolder& code) {
// Contains all section entries managed by CodeHolder.
Span<Section*> sections = code.sections();
// Contains all label entries managed by CodeHolder.
Span<LabelEntry> label_entries = code.label_entries();
// Contains all relocation entries managed by CodeHolder.
Span<RelocEntry*> reloc_entries = code.reloc_entries();
}

Span has overloaded array access operator to make it possible to access its elements through operator[]. Some standard functions like ArenaVector::is_empty(), ArenaVector::size(), and ArenaVector::data() are provided as well. Vectors are also iterable through a range-based for loop:

using namespace asmjit;
void example(CodeHolder& code) {
Span<LabelEntry> label_entries = code.label_entries();
for (size_t label_id = 0; label_id < label_entries.size(); label_id++) {
const LabelEntry& le = label_entries[label_id];
if (le.is_bound()) {
printf("Bound Label #%u at offset=%llu\n", uint32_t(label_id), (unsigned long long)le.offset());
}
}
}

Design Considerations [¶]

Arena-allocated containers do not store the allocator within the container. This decision was made to reduce the footprint of such containers as AsmJit tooling, especially Compiler's register allocation, may use many instances of such containers to perform code analysis and register allocation.

For example to append an item into an ArenaVector it's required to pass the allocator as the first argument, so it can be used in case that the vector needs to grow. Such function also returns an error, which must be propagated to the caller.

using namespace asmjit;
Error example(Arena& arena) {
// Unfortunately, arena must be provided to all functions that mutate
// the vector. However, AsmJit users should never need to do this as all
// manipulation should be done through public API, which takes care of
// this.
for (int i = 0; i < 100; i++) {
ASMJIT_PROPAGATE(vector.append(arena, i));
}
// By default vector's destructor doesn't release anything as it knows
// that its content is allocated by Arena. However, \ref ArenaVector::release
// can be used to explicitly release the vector data back to the allocator if
// necessary
vector.release(arena);
}

Containers like ArenaVector also provide a functionality to reserve a certain number of items before any items are added to it. This approach is used internally in most places as it allows to prepare space for data that will be added to some container before the data itself was created.

using namespace asmjit;
Error example(Arena& arena) {
ASMJIT_PROPAGATE(vector.reserve_additional(arena, 100));
for (int i = 0; i < 100; i++) {
// Cannot fail.
vector.append_unchecked(arena, i);
}
vector.release(arena);
}

Typedefs