Compilation#

Platform#

type UNIT_Platform#

An integer encoding the target architecture and ABI. Constructed by bitwise OR of a UNIT_Architecture and a UNIT_ABI.

UNIT_Platform platform = UNIT_ARCH_AMD64 | UNIT_ABI_SYSTEMV;
enum UNIT_Architecture#
enumerator UNIT_ARCH_AMD64#

x86-64 (AMD64). This is the only architecture currently implemented.

enumerator UNIT_ARCH_AARCH64#

ARM64 (AArch64). Not yet implemented.

enum UNIT_ABI#
enumerator UNIT_ABI_SYSTEMV#

System V AMD64 ABI. Used on Linux, FreeBSD, and other Unix-like systems.

enumerator UNIT_ABI_WIN64#

Windows x64 calling convention. Not yet implemented.

enumerator UNIT_ABI_APPLE#

Apple ARM64 ABI. Not yet implemented.

UNIT_HOST_PLATFORM#

The platform of the current machine, auto-detected at compile time. This is the most common value to pass to UNIT_Compile().

UNIT_CompiledProcedure *compiled = UNIT_Compile(&proc, UNIT_HOST_PLATFORM);
UNIT_ABI UNIT_Platform_GET_ABI(UNIT_Platform platform)#

Extract the ABI from a platform value.

Parameters:
  • platform – The platform.

Returns:

The ABI component.

UNIT_Architecture UNIT_Platform_GET_ARCH(UNIT_Platform platform)#

Extract the architecture from a platform value.

Parameters:
  • platform – The platform.

Returns:

The architecture component.

Compiling#

UNIT_CompiledProcedure *UNIT_Compile(const UNIT_Procedure *procedure, UNIT_Platform platform)#

Compile a procedure to machine code. This translates the stack IR to register IR, performs register allocation, runs register IR optimization (unless UNIT_FLAG_NO_OPTIMIZE_TRANSLATION is set), and encodes the result as machine code.

Parameters:
  • procedure – The procedure to compile. Must have been optimized with UNIT_Procedure_Optimize() first (optional but recommended).

  • platform – The target platform.

Returns:

A heap-allocated compiled procedure, or NULL on failure. Must be freed with UNIT_CompiledProcedure_Free().

Example#
UNIT_Procedure_Optimize(&proc);
UNIT_CompiledProcedure *compiled = UNIT_Compile(&proc, UNIT_HOST_PLATFORM);
if (compiled == NULL) {
    UNIT_PrintError(proc.context, stderr);
    return -1;
}

// Use compiled procedure ...

UNIT_CompiledProcedure_Free(compiled);
struct UNIT_CompiledProcedure#

A compiled procedure containing machine code, ready for JIT execution or object file output.

UNIT_Context *context#

The context.

UNIT_Platform platform#

The target platform.

const char *name#

The procedure name.

void UNIT_CompiledProcedure_Free(UNIT_CompiledProcedure *compiled)#

Free a compiled procedure. Safe to call with NULL.

Parameters:
  • compiled – The compiled procedure to free, or NULL.

Object files#

enum UNIT_ExecutableFormat#
enumerator UNIT_FORMAT_ELF#

ELF format (Linux, FreeBSD, etc). This is the only format currently implemented.

enumerator UNIT_FORMAT_MACHO#

Mach-O format (macOS). Not yet implemented.

enumerator UNIT_FORMAT_PE#

PE/COFF format (Windows). Not yet implemented.

UNIT_Status UNIT_CompiledProcedure_WriteObjectFile(const UNIT_CompiledProcedure *compiled, const char *path, UNIT_ExecutableFormat format)#

Write the compiled procedure to an object file. The resulting file can be linked with gcc or clang.

Parameters:
  • compiled – The compiled procedure.

  • path – The output file path.

  • format – The object file format.

Example#
1UNIT_CompiledProcedure_WriteObjectFile(compiled, "output.o", UNIT_FORMAT_ELF);
Linking#
gcc output.o -o output

JIT Compilation#

UNIT_ExecutableBuffer *UNIT_CompiledProcedure_JIT(const UNIT_CompiledProcedure *compiled, const UNIT_SymbolMap *symbol_map)#

JIT compile a procedure into executable memory. Returns an opaque buffer containing the compiled function.

Symbols referenced by UNIT_OP_CALL_NAME are resolved first from symbol_map (if provided), then via dlsym.

Parameters:
  • compiled – The compiled procedure.

  • symbol_map – Custom symbol resolution map, or NULL to use dlsym only.

Returns:

A heap-allocated executable buffer, or NULL on failure. Must be freed with UNIT_ExecutableBuffer_Free().

Example#
UNIT_ExecutableBuffer *buf = UNIT_CompiledProcedure_JIT(compiled, NULL);
if (buf == NULL) {
    UNIT_PrintError(compiled->context, stderr);
    return -1;
}

int64_t (*fn)(int64_t) = (int64_t (*)(int64_t))UNIT_ExecutableBuffer_GetPointer(buf);
printf("%ld\n", fn(42));

UNIT_ExecutableBuffer_Free(buf);
type UNIT_ExecutableBuffer#

An opaque type representing JIT-compiled executable memory. The internal structure is not publicly visible.

void *UNIT_ExecutableBuffer_GetPointer(const UNIT_ExecutableBuffer *buffer)#

Get the function pointer from an executable buffer. Cast this to the appropriate function pointer type.

Parameters:
  • buffer – The executable buffer.

Returns:

A pointer to the compiled function.

void UNIT_ExecutableBuffer_Free(UNIT_ExecutableBuffer *buffer)#

Free an executable buffer and its mapped memory. Safe to call with NULL.

Parameters:
  • buffer – The buffer to free, or NULL.

Symbol maps#

struct UNIT_SymbolMap#

A map from symbol names to addresses, used for custom symbol resolution during JIT compilation. Useful for providing trampolines, callbacks, or calling between JIT-compiled functions.

UNIT_Context *context#

The context.

UNIT_Status UNIT_SymbolMap_Init(UNIT_SymbolMap *symbol_map, UNIT_Context *context)#

Initialize a symbol map. On success, UNIT_SymbolMap_Clear() must be called later.

Parameters:
  • symbol_map – A pointer to a symbol map.

  • context – The context.

UNIT_SymbolMap *UNIT_SymbolMap_New(UNIT_Context *context)#

Create a new heap-allocated symbol map. On success, UNIT_SymbolMap_Free() must be called later.

Parameters:
  • context – The context.

Returns:

A heap-allocated symbol map, or NULL on failure.

void UNIT_SymbolMap_Clear(UNIT_SymbolMap *symbol_map)#

Free memory allocated by UNIT_SymbolMap_Init().

void UNIT_SymbolMap_Free(UNIT_SymbolMap *symbol_map)#

Free memory allocated by UNIT_SymbolMap_New(). Safe to call with NULL.

UNIT_Status UNIT_SymbolMap_RegisterSymbol(UNIT_SymbolMap *symbol_map, const char *name, void *address)#

Register a symbol name with its address. During JIT compilation, this address is used instead of dlsym for the given name.

Parameters:
  • symbol_map – The symbol map.

  • name – The symbol name.

  • address – The function pointer or data address.

Example#
int64_t my_callback(int64_t x) { return x * 2; }

UNIT_SymbolMap symbols;
UNIT_SymbolMap_Init(&symbols, &ctx);
UNIT_SymbolMap_RegisterSymbol(&symbols, "my_callback",
                               (void *)my_callback);

UNIT_ExecutableBuffer *buf = UNIT_CompiledProcedure_JIT(compiled, &symbols);

// The compiled code can now call "my_callback" and it will
// resolve to the function above.

UNIT_SymbolMap_Clear(&symbols);

Debugging#

UNIT_Status UNIT_CompiledProcedure_PrintTranslatedIR(const UNIT_CompiledProcedure *compiled, FILE *stream)#

Print the register IR to a file stream. Shows the translation output after register allocation and optimization.

Parameters:
  • compiled – The compiled procedure.

  • stream – The output stream (e.g. stdout).

Example#
UNIT_CompiledProcedure_PrintTranslatedIR(compiled, stdout);