Skip to content

Commit

Permalink
Move x registers from contexts to schedulers (fix atomvm#698)
Browse files Browse the repository at this point in the history
X registers are saved in context on context switching but are otherwise
allocated only once per scheduler thread, thus allowing up to 1024 registers
as the compiler allows

Signed-off-by: Paul Guyot <[email protected]>
  • Loading branch information
pguyot committed Nov 13, 2023
1 parent 389676e commit 86930bd
Show file tree
Hide file tree
Showing 29 changed files with 701 additions and 565 deletions.
308 changes: 171 additions & 137 deletions src/libAtomVM/bif.c

Large diffs are not rendered by default.

136 changes: 68 additions & 68 deletions src/libAtomVM/bif.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,74 +41,74 @@ extern "C" {

const struct ExportedFunction *bif_registry_get_handler(AtomString module, AtomString function, int arity);

term bif_erlang_self_0(Context *ctx);
term bif_erlang_byte_size_1(Context *ctx, int live, term arg1);
term bif_erlang_bit_size_1(Context *ctx, int live, term arg1);
term bif_erlang_length_1(Context *ctx, int live, term arg1);

term bif_erlang_is_atom_1(Context *ctx, term arg1);
term bif_erlang_is_binary_1(Context *ctx, term arg1);
term bif_erlang_is_boolean_1(Context *ctx, term arg1);
term bif_erlang_is_float_1(Context *ctx, term arg1);
term bif_erlang_is_function_1(Context *ctx, term arg1);
term bif_erlang_is_integer_1(Context *ctx, term arg1);
term bif_erlang_is_list_1(Context *ctx, term arg1);
term bif_erlang_is_number_1(Context *ctx, term arg1);
term bif_erlang_is_pid_1(Context *ctx, term arg1);
term bif_erlang_is_reference_1(Context *ctx, term arg1);
term bif_erlang_is_tuple_1(Context *ctx, term arg1);
term bif_erlang_is_map_1(Context *ctx, term arg1);
term bif_erlang_is_map_key_2(Context *ctx, term arg1, term arg2);

term bif_erlang_hd_1(Context *ctx, term arg1);
term bif_erlang_tl_1(Context *ctx, term arg1);

term bif_erlang_element_2(Context *ctx, term arg1, term arg2);
term bif_erlang_tuple_size_1(Context *ctx, term arg1);

term bif_erlang_map_size_1(Context *ctx, int live, term arg1);
term bif_erlang_map_get_2(Context *ctx, term arg1, term arg2);

term bif_erlang_add_2(Context *ctx, int live, term arg1, term arg2);
term bif_erlang_sub_2(Context *ctx, int live, term arg1, term arg2);
term bif_erlang_mul_2(Context *ctx, int live, term arg1, term arg2);
term bif_erlang_div_2(Context *ctx, int live, term arg1, term arg2);
term bif_erlang_rem_2(Context *ctx, int live, term arg1, term arg2);
term bif_erlang_neg_1(Context *ctx, int live, term arg1);
term bif_erlang_abs_1(Context *ctx, int live, term arg1);

term bif_erlang_ceil_1(Context *ctx, int live, term arg1);
term bif_erlang_floor_1(Context *ctx, int live, term arg1);
term bif_erlang_round_1(Context *ctx, int live, term arg1);
term bif_erlang_trunc_1(Context *ctx, int live, term arg1);

term bif_erlang_bor_2(Context *ctx, int live, term arg1, term arg2);
term bif_erlang_band_2(Context *ctx, int live, term arg1, term arg2);
term bif_erlang_bxor_2(Context *ctx, int live, term arg1, term arg2);
term bif_erlang_bsl_2(Context *ctx, int live, term arg1, term arg2);
term bif_erlang_bsr_2(Context *ctx, int live, term arg1, term arg2);
term bif_erlang_bnot_1(Context *ctx, int live, term arg1);

term bif_erlang_not_1(Context *ctx, term arg1);
term bif_erlang_and_2(Context *ctx, term arg1, term arg2);
term bif_erlang_or_2(Context *ctx, term arg1, term arg2);
term bif_erlang_xor_2(Context *ctx, term arg1, term arg2);

term bif_erlang_equal_to_2(Context *ctx, term arg1, term arg2);
term bif_erlang_not_equal_to_2(Context *ctx, term arg1, term arg2);

term bif_erlang_exactly_equal_to_2(Context *ctx, term arg1, term arg2);
term bif_erlang_exactly_not_equal_to_2(Context *ctx, term arg1, term arg2);

term bif_erlang_greater_than_2(Context *ctx, term arg1, term arg2);
term bif_erlang_less_than_2(Context *ctx, term arg1, term arg2);
term bif_erlang_less_than_or_equal_2(Context *ctx, term arg1, term arg2);
term bif_erlang_greater_than_or_equal_2(Context *ctx, term arg1, term arg2);

term bif_erlang_get_1(Context *ctx, term arg1);

term bif_erlang_min_2(Context *ctx, term arg1, term arg2);
term bif_erlang_max_2(Context *ctx, term arg1, term arg2);
term bif_erlang_self_0(Context *ctx, term *x_regs);
term bif_erlang_byte_size_1(Context *ctx, term *x_regs, int live, term arg1);
term bif_erlang_bit_size_1(Context *ctx, term *x_regs, int live, term arg1);
term bif_erlang_length_1(Context *ctx, term *x_regs, int live, term arg1);

term bif_erlang_is_atom_1(Context *ctx, term *x_regs, term arg1);
term bif_erlang_is_binary_1(Context *ctx, term *x_regs, term arg1);
term bif_erlang_is_boolean_1(Context *ctx, term *x_regs, term arg1);
term bif_erlang_is_float_1(Context *ctx, term *x_regs, term arg1);
term bif_erlang_is_function_1(Context *ctx, term *x_regs, term arg1);
term bif_erlang_is_integer_1(Context *ctx, term *x_regs, term arg1);
term bif_erlang_is_list_1(Context *ctx, term *x_regs, term arg1);
term bif_erlang_is_number_1(Context *ctx, term *x_regs, term arg1);
term bif_erlang_is_pid_1(Context *ctx, term *x_regs, term arg1);
term bif_erlang_is_reference_1(Context *ctx, term *x_regs, term arg1);
term bif_erlang_is_tuple_1(Context *ctx, term *x_regs, term arg1);
term bif_erlang_is_map_1(Context *ctx, term *x_regs, term arg1);
term bif_erlang_is_map_key_2(Context *ctx, term *x_regs, term arg1, term arg2);

term bif_erlang_hd_1(Context *ctx, term *x_regs, term arg1);
term bif_erlang_tl_1(Context *ctx, term *x_regs, term arg1);

term bif_erlang_element_2(Context *ctx, term *x_regs, term arg1, term arg2);
term bif_erlang_tuple_size_1(Context *ctx, term *x_regs, term arg1);

term bif_erlang_map_size_1(Context *ctx, term *x_regs, int live, term arg1);
term bif_erlang_map_get_2(Context *ctx, term *x_regs, term arg1, term arg2);

term bif_erlang_add_2(Context *ctx, term *x_regs, int live, term arg1, term arg2);
term bif_erlang_sub_2(Context *ctx, term *x_regs, int live, term arg1, term arg2);
term bif_erlang_mul_2(Context *ctx, term *x_regs, int live, term arg1, term arg2);
term bif_erlang_div_2(Context *ctx, term *x_regs, int live, term arg1, term arg2);
term bif_erlang_rem_2(Context *ctx, term *x_regs, int live, term arg1, term arg2);
term bif_erlang_neg_1(Context *ctx, term *x_regs, int live, term arg1);
term bif_erlang_abs_1(Context *ctx, term *x_regs, int live, term arg1);

term bif_erlang_ceil_1(Context *ctx, term *x_regs, int live, term arg1);
term bif_erlang_floor_1(Context *ctx, term *x_regs, int live, term arg1);
term bif_erlang_round_1(Context *ctx, term *x_regs, int live, term arg1);
term bif_erlang_trunc_1(Context *ctx, term *x_regs, int live, term arg1);

term bif_erlang_bor_2(Context *ctx, term *x_regs, int live, term arg1, term arg2);
term bif_erlang_band_2(Context *ctx, term *x_regs, int live, term arg1, term arg2);
term bif_erlang_bxor_2(Context *ctx, term *x_regs, int live, term arg1, term arg2);
term bif_erlang_bsl_2(Context *ctx, term *x_regs, int live, term arg1, term arg2);
term bif_erlang_bsr_2(Context *ctx, term *x_regs, int live, term arg1, term arg2);
term bif_erlang_bnot_1(Context *ctx, term *x_regs, int live, term arg1);

term bif_erlang_not_1(Context *ctx, term *x_regs, term arg1);
term bif_erlang_and_2(Context *ctx, term *x_regs, term arg1, term arg2);
term bif_erlang_or_2(Context *ctx, term *x_regs, term arg1, term arg2);
term bif_erlang_xor_2(Context *ctx, term *x_regs, term arg1, term arg2);

term bif_erlang_equal_to_2(Context *ctx, term *x_regs, term arg1, term arg2);
term bif_erlang_not_equal_to_2(Context *ctx, term *x_regs, term arg1, term arg2);

term bif_erlang_exactly_equal_to_2(Context *ctx, term *x_regs, term arg1, term arg2);
term bif_erlang_exactly_not_equal_to_2(Context *ctx, term *x_regs, term arg1, term arg2);

term bif_erlang_greater_than_2(Context *ctx, term *x_regs, term arg1, term arg2);
term bif_erlang_less_than_2(Context *ctx, term *x_regs, term arg1, term arg2);
term bif_erlang_less_than_or_equal_2(Context *ctx, term *x_regs, term arg1, term arg2);
term bif_erlang_greater_than_or_equal_2(Context *ctx, term *x_regs, term arg1, term arg2);

term bif_erlang_get_1(Context *ctx, term *x_regs, term arg1);

term bif_erlang_min_2(Context *ctx, term *x_regs, term arg1, term arg2);
term bif_erlang_max_2(Context *ctx, term *x_regs, term arg1, term arg2);

#ifdef __cplusplus
}
Expand Down
13 changes: 7 additions & 6 deletions src/libAtomVM/context.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ Context *context_new(GlobalContext *glb)
}
ctx->e = ctx->heap.heap_end;

context_clean_registers(ctx, 0);

ctx->xregs_count = 0;
ctx->saved_x = NULL;
ctx->fr = NULL;

ctx->min_heap_size = 0;
Expand Down Expand Up @@ -151,6 +151,7 @@ void context_destroy(Context *ctx)
// Any other process released our mailbox, so we can clear it.
mailbox_destroy(&ctx->mailbox, &ctx->heap);

free(ctx->saved_x);
free(ctx->fr);

memory_destroy_heap(&ctx->heap, ctx->global);
Expand Down Expand Up @@ -195,14 +196,14 @@ void context_process_process_info_request_signal(Context *ctx, struct BuiltInAto
} // else: sender died
}

bool context_process_signal_trap_answer(Context *ctx, struct TermSignal *signal)
bool context_process_signal_trap_answer(Context *ctx, term *x_regs, struct TermSignal *signal)
{
context_update_flags(ctx, ~Trap, NoFlags);
ctx->x[0] = signal->signal_term;
x_regs[0] = signal->signal_term;
return true;
}

void context_process_flush_monitor_signal(Context *ctx, uint64_t ref_ticks, bool info)
void context_process_flush_monitor_signal(Context *ctx, term *x_regs, uint64_t ref_ticks, bool info)
{
context_update_flags(ctx, ~Trap, NoFlags);
bool result = true;
Expand All @@ -222,7 +223,7 @@ void context_process_flush_monitor_signal(Context *ctx, uint64_t ref_ticks, bool
}
}
mailbox_reset(&ctx->mailbox);
ctx->x[0] = result ? TRUE_ATOM : FALSE_ATOM;
x_regs[0] = result ? TRUE_ATOM : FALSE_ATOM;
}

void context_update_flags(Context *ctx, int mask, int value) CLANG_THREAD_SANITIZE_SAFE
Expand Down
98 changes: 68 additions & 30 deletions src/libAtomVM/context.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,17 +74,18 @@ enum HeapGrowthStrategy
FibonacciHeapGrowth
};

// Max number of x(N) & fr(N) registers
// BEAM sets this to 1024.
#define MAX_REG 16
#define MAX_REG 1024

struct Context
{
// First fields matches ErlNifEnv structure.
GlobalContext *global;
Heap heap;
term *e;
term x[MAX_REG];
term *saved_x;
avm_float_t *fr;
uint16_t xregs_count;
uint16_t fpregs_count;

struct ListHead processes_list_head;

Expand All @@ -95,8 +96,6 @@ struct Context

struct ListHead monitors_head;

avm_float_t *fr;

size_t min_heap_size;
size_t max_heap_size;
enum HeapGrowthStrategy heap_growth_strategy;
Expand Down Expand Up @@ -189,18 +188,69 @@ Context *context_new(GlobalContext *glb);
void context_destroy(Context *c);

/**
* @brief Ensure we have FP registers, allocating them if necessary.
* @param c context fo allocate FP registers for
* @brief Ensure at least a given number of FP registers are available,
* allocating them if necessary.
* @param c context to allocate FP registers for
* @param n_regs number of registers to allocate
*/
static inline void context_ensure_fpregs(Context *c, uint16_t n_regs)
{
if (n_regs > c->fpregs_count) {
size_t new_size = sizeof(avm_float_t) * n_regs;
if (IS_NULL_PTR(c->fr)) {
c->fr = (avm_float_t *) malloc(new_size);
if (IS_NULL_PTR(c->fr)) {
fprintf(stderr, "Could not allocate FP registers\n");
AVM_ABORT();
}
} else {
c->fr = realloc(c->fr, new_size);
if (IS_NULL_PTR(c->fr)) {
fprintf(stderr, "Could not allocate FP registers\n");
AVM_ABORT();
}
}
c->fpregs_count = n_regs;
}
}

/**
* @brief Saved registers before context switching.
* @param c context to save registers to
* @param x_regs registers to save
* @param n_regs number of registers to save
*/
static inline void context_ensure_fpregs(Context *c)
static inline void context_save_xregs(Context *c, term *x_regs, uint16_t n_regs)
{
if (UNLIKELY(c->fr == NULL)) {
c->fr = (avm_float_t *) malloc(sizeof(avm_float_t) * MAX_REG);
if (UNLIKELY(c->fr == NULL)) {
fprintf(stderr, "Could not allocate FP registers\n");
AVM_ABORT();
size_t new_size = sizeof(term) * n_regs;
if (n_regs > c->xregs_count) {
if (UNLIKELY(c->saved_x == NULL)) {
c->saved_x = (term *) malloc(new_size);
if (IS_NULL_PTR(c->saved_x)) {
fprintf(stderr, "Could not allocate X registers\n");
AVM_ABORT();
}
} else {
c->saved_x = realloc(c->saved_x, new_size);
if (IS_NULL_PTR(c->saved_x)) {
fprintf(stderr, "Could not allocate X registers\n");
AVM_ABORT();
}
}
}
memcpy(c->saved_x, x_regs, new_size);
c->xregs_count = n_regs;
}

/**
* @brief Restore registers after context switching.
* @param c context to restore registers from
* @param x_regs registers to restore
*/
static inline void context_restore_xregs(Context *c, term *x_regs)
{
size_t size = sizeof(term) * c->xregs_count;
memcpy(x_regs, c->saved_x, size);
}

/**
Expand All @@ -227,20 +277,6 @@ static inline int context_is_port_driver(const Context *ctx)
return ctx->native_handler != NULL;
}

/**
* @brief Cleans up unused registers
*
* @details Sets to NIL unused registers, x[0] - x[live - 1] will not be overwritten.
* @param ctx a valid context
* @param live number of used registers
*/
static inline void context_clean_registers(Context *ctx, int live)
{
for (int i = live; i < MAX_REG; i++) {
ctx->x[i] = term_nil();
}
}

/**
* @brief Returns a context's stack base
*
Expand Down Expand Up @@ -368,19 +404,21 @@ void context_process_process_info_request_signal(Context *ctx, struct BuiltInAto
* @brief Process a trap answer signal.
*
* @param ctx the context being executed
* @param x_regs the current x registers
* @param signal the answer message
* @return \c true if successful, \c false in case of memory error
*/
bool context_process_signal_trap_answer(Context *ctx, struct TermSignal *signal);
bool context_process_signal_trap_answer(Context *ctx, term *x_regs, struct TermSignal *signal);

/**
* @brief Process a flush monitor signal.
*
* @param ctx the context being executed
* @param x_regs the current x registers
* @param ref_ticks the monitor reference
* @param info whether to return FALSE_ATOM if no message was flushed.
*/
void context_process_flush_monitor_signal(Context *ctx, uint64_t ref_ticks, bool info);
void context_process_flush_monitor_signal(Context *ctx, term *x_regs, uint64_t ref_ticks, bool info);

/**
* @brief Get process information.
Expand Down
15 changes: 3 additions & 12 deletions src/libAtomVM/debug.c
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,6 @@ COLD_FUNC void debug_dump_memory(Context *ctx, term *start, term *end, const cha
fprintf(stderr, "DEBUG:\n");
}

COLD_FUNC void debug_dump_context(Context *ctx)
{
debug_dump_heap(ctx);
debug_dump_stack(ctx);
debug_dump_registers(ctx);
}

COLD_FUNC void debug_dump_heap(Context *ctx)
{
debug_dump_memory(ctx, ctx->heap.heap_start, ctx->heap.heap_ptr, "heap");
Expand All @@ -94,11 +87,6 @@ COLD_FUNC void debug_dump_stack(Context *ctx)
debug_dump_memory(ctx, ctx->e, stack_base, "stack");
}

COLD_FUNC void debug_dump_registers(Context *ctx)
{
debug_dump_memory(ctx, ctx->x, ctx->x + 16, "register");
}

COLD_FUNC void debug_print_processes_list(struct ListHead *processes)
{
Context *contexts = GET_LIST_ENTRY(processes, Context, processes_list_head);
Expand Down Expand Up @@ -128,6 +116,9 @@ COLD_FUNC char reg_type_c(int reg_type)
case 4:
return 'y';

case 11:
return 'x';

case 12:
return 'y';

Expand Down
Loading

0 comments on commit 86930bd

Please sign in to comment.