Procedures#

class unit::Procedure#

A procedure represents a single function to be compiled. It holds the stack-based IR and provides methods to emit instructions.

Procedures cannot be copied or moved.

explicit Procedure(Context &ctx, const std::string &name)#

Create a new procedure.

Parameters:
  • ctx – The context that owns this procedure’s memory.

  • name – The symbol name for the compiled function.

Throws:

unit::error – If initialization fails.

UNIT_Procedure *raw()#

Return the underlying C procedure pointer.

void optimize()#

Run stack IR optimization passes: inlining, constant folding, dead store elimination, and local variable optimization. Call this before compile().

Throws:

unit::error – If optimization fails.

CompiledProcedure compile(Platform platform)#

Compile the procedure to machine code for the given platform.

Parameters:

platform – Target platform (e.g. Platform::host()).

Returns:

A compiled procedure ready for JIT or object file output.

Throws:

unit::error – If compilation fails.

void print_instructions(FILE *stream = stdout)#

Print the stack IR to a file stream. Useful for debugging.

void set_flags(Flag flags)#

Set procedure flags.

Parameters:

flags – Bitwise OR of Flag values.

Flag get_flags() const#

Return the current procedure flags.

Stack Operations

void load_integer(int64_t value)#

Push an integer constant onto the stack. See UNIT_OP_LOAD_INTEGER.

Stack effect: -- value

Example#
proc.load_integer(42); // stack: [42]
void load_string(const std::string &value)#

Push a string constant onto the stack. The string is copied internally. See UNIT_Procedure_AddStringLoad().

Stack effect: -- pointer

void load_argument(uint8_t index)#

Push a function argument onto the stack. Argument 0 is the first parameter. See UNIT_OP_LOAD_ARGUMENT.

Stack effect: -- value

void load_local(int64_t index)#

Push a local variable onto the stack by index. See UNIT_OP_LOAD_LOCAL.

Stack effect: -- value

void store_local(int64_t index)#

Pop the top of the stack into a local variable by index. See UNIT_OP_STORE_LOCAL.

Stack effect: value --

void load_name(Local local)#

Push a local variable onto the stack by handle. See UNIT_Procedure_AddLoadName().

Stack effect: -- value

void store_name(Local local)#

Pop the top of the stack into a local variable by handle. See UNIT_Procedure_AddStoreName().

Stack effect: value --

void pop()#

Discard the top of the stack. See UNIT_OP_POP.

Stack effect: value --

void copy(int64_t offset)#

Duplicate the stack item at the given depth. copy(0) duplicates the top. See UNIT_OP_COPY.

Stack effect: -- value

proc.load_integer(10);   // stack: [10]
proc.load_integer(20);   // stack: [10, 20]
proc.copy(1);            // stack: [10, 20, 10]
void swap(int64_t offset)#

Swap the top of the stack with the item at the given depth. See UNIT_OP_SWAP.

Stack effect: unchanged (items rearranged)

Arithmetic

void add()#

Pop two values, push their sum. See UNIT_OP_ADD.

Stack effect: a b -- a+b

void subtract()#

Pop two values, push their difference. See UNIT_OP_SUBTRACT.

Stack effect: a b -- a-b

void multiply()#

Pop two values, push their product. See UNIT_OP_MULTIPLY.

Stack effect: a b -- a*b

void divide()#

Pop two values, push their quotient (integer division, truncated toward zero). See UNIT_OP_DIVIDE.

Stack effect: a b -- a/b

void modulo()#

Pop two values, push the remainder. See UNIT_OP_MODULO.

Stack effect: a b -- a%b

Comparisons

All comparisons pop two values, push a comparison result consumed by jump_if_true() or jump_if_false().

void compare_equal()#

See UNIT_OP_COMPARE_EQUAL.

Stack effect: a b -- result

void compare_not_equal()#

See UNIT_OP_COMPARE_NOT_EQUAL.

Stack effect: a b -- result

void compare_less()#

See UNIT_OP_COMPARE_LESS.

Stack effect: a b -- result

void compare_less_equal()#

See UNIT_OP_COMPARE_LESS_EQUAL.

Stack effect: a b -- result

void compare_greater()#

See UNIT_OP_COMPARE_GREATER.

Stack effect: a b -- result

void compare_greater_equal()#

See UNIT_OP_COMPARE_GREATER_EQUAL.

Stack effect: a b -- result

Control Flow

JumpLabel create_jump_label(const std::string &name)#

Create a jump target. The label must later be placed with use_label().

Returns:

A label handle for use with jump instructions.

Throws:

unit::error – If allocation fails.

void use_label(JumpLabel label)#

Place a label at the current position in the instruction stream. All jumps to this label will target the next instruction emitted.

void jump_to(JumpLabel label)#

Unconditional jump. See UNIT_OP_JUMP_TO.

void jump_if_true(JumpLabel label)#

Pop a comparison result. Jump if true. See UNIT_OP_JUMP_IF_TRUE.

Stack effect: comparison --

void jump_if_false(JumpLabel label)#

Pop a comparison result. Jump if false. See UNIT_OP_JUMP_IF_FALSE.

Stack effect: comparison --

Example#
auto end = proc.create_jump_label("end");
proc.load_integer(5);
proc.load_integer(5);
proc.compare_equal();
proc.jump_if_true(end);
// ... not-equal path ...
proc.use_label(end);
// ... equal path continues here ...

Calls

void call_name(const std::string &name, uint8_t nargs)#

Call an external function by name. The top nargs stack items are passed as arguments (first argument pushed first). The return value is pushed onto the stack. See UNIT_Procedure_AddCallName().

Stack effect: arg1 arg2 ... argN -- result

Example#
proc.load_string("%d\n");
proc.load_integer(42);
proc.call_name("printf", 2);
proc.pop();  // discard printf return value
void call_procedure(Procedure &target, uint8_t nargs)#

Call another UNIT procedure. This enables inlining during optimization. See UNIT_Procedure_AddCallProcedure().

Stack effect: arg1 arg2 ... argN -- result

void return_value()#

Pop the top of the stack and return it to the caller. See UNIT_OP_RETURN_VALUE.

Stack effect: value --

void exit()#

Terminate the process immediately. See UNIT_OP_EXIT.

Memory Access

void read_bytes(uint8_t num_bytes)#

Pop an address, read num_bytes from it (1, 2, 4, or 8), push the value. See UNIT_OP_READ_BYTES.

Stack effect: address -- value

void write_bytes(uint8_t num_bytes)#

Pop an address and a value, write num_bytes of the value to the address. See UNIT_OP_WRITE_BYTES.

Stack effect: address value --

void address_of(Local local)#

Push the memory address of a local variable. See UNIT_OP_ADDRESS_OF.

Stack effect: -- address

Type Conversion

void convert(IntegerType type)#

Convert the top of the stack to a different integer width. See UNIT_OP_CONVERT.

Stack effect: value -- converted_value

Locals

Local create_local(const std::string &name)#

Create a named local variable. Returns a handle for use with store_name() and load_name().

Throws:

unit::error – If allocation fails.

Local variables#

class unit::Local#

A handle to a local variable, returned by Procedure::create_local().

constexpr bool operator==(Local other) const#
constexpr bool operator!=(Local other) const#
UNIT_Local raw() const#

Return the underlying C local handle.

Jump labels#

class unit::JumpLabel#

A handle to a jump target, returned by Procedure::create_jump_label().

bool operator==(JumpLabel other) const#
bool operator!=(JumpLabel other) const#
UNIT_JumpLabel *raw() const#

Return the underlying C label pointer.

Flags#

enum class unit::Flag : uint32_t#

Procedure flags, combined with bitwise OR.

enumerator NONE = 0#

No flags.

enumerator NO_OPTIMIZE_TRANSLATION = UNIT_FLAG_NO_OPTIMIZE_TRANSLATION#

Skip register IR optimization (move coalescing, dead move elimination, forward copy propagation) during compilation.

enumerator FORCE_NO_INLINE = UNIT_FLAG_FORCE_NO_INLINE#

Prevent this procedure from being inlined into callers during optimization, regardless of size.

enumerator FORCE_INLINE = UNIT_FLAG_FORCE_INLINE#

Always inline this procedure into callers, regardless of size.

enumerator PRINT_TRANSLATION_PREOP = UNIT_FLAG_PRINT_TRANSLATION_PREOP#

Print the register IR to stderr before optimization runs. Useful for debugging.

enumerator PRINT_TRANSLATION_POSTOP = UNIT_FLAG_PRINT_TRANSLATION_POSTOP#

Print the register IR to stderr after optimization runs. Useful for debugging.

Example#
proc.set_flags(unit::Flag::FORCE_NO_INLINE);

// Combine flags with bitwise OR
proc.set_flags(unit::Flag::FORCE_NO_INLINE | unit::Flag::NO_OPTIMIZE_TRANSLATION);

Compiled procedures#

class unit::CompiledProcedure#

A compiled procedure, ready for JIT execution or object file output. Returned by Procedure::compile(). Cannot be copied. Move-only.

template<typename Func>
ExecutableBuffer<Func> jit()#

JIT compile and return an executable buffer. Symbols are resolved via dlsym.

Throws:

unit::error – If JIT compilation fails.

template<typename Func>
ExecutableBuffer<Func> jit(SymbolMap &symbols)#

JIT compile with custom symbol resolution. Symbols in the map are checked before falling back to dlsym.

Throws:

unit::error – If JIT compilation fails.

void write_object_file(const std::string &path, ExecutableFormat format)#

Write the compiled procedure to an object file.

Parameters:
  • path – Output file path.

  • format – Object file format (e.g., ExecutableFormat::ELF).

Throws:

unit::error – If writing fails.

void print_translated(FILE *stream = stdout)#

Print the register IR to a file stream. Useful for debugging.

UNIT_CompiledProcedure *raw() const#

Return the underlying C compiled procedure pointer.

Executable buffers#

template<typename Func>
class unit::ExecutableBuffer#

A JIT-compiled function, callable directly. Returned by CompiledProcedure::jit(). Cannot be copied. Move-only. The executable memory is freed on destruction.

Func is the function pointer type, e.g. int64_t(*)(int64_t).

template<typename ...Args>
auto operator()(Args... args) const#

Call the JIT-compiled function.

auto add = compiled.jit<int64_t(*)(int64_t, int64_t)>();
int64_t result = add(3, 4);  // 7

Symbol maps#

class unit::SymbolMap#

A map from symbol names to addresses, used for custom symbol resolution during JIT compilation. Cannot be copied or moved.

explicit SymbolMap(Context &ctx)#

Create an empty symbol map.

Throws:

unit::error – If initialization fails.

void register_symbol(const std::string &name, void *address)#

Register a symbol name with its address.

Parameters:
  • name – The symbol name (e.g. "my_function").

  • address – The function pointer or data address.

Example#
unit::SymbolMap symbols(ctx);
symbols.register_symbol("my_callback", (void *)my_callback);
auto fn = compiled.jit<int64_t(*)()>(symbols);