* tests/gpgscm/scheme-private.h (struct scheme): New field
'frame_freelist'.
* tests/gpgscm/scheme.c (enum scheme_types): New type 'T_FRAME'.
(type_to_string): Handle new type.
(settype): New macro.
(gc_disable): Make sure there is at least one frame in the free list.
(mark): Handle frame objects.
(finalize_cell): Likewise.
(dump_stack_initialize): Initialize free list.
(dump_stack_free): Simplify.
(frame_length): New variable.
(dump_stack_make_frame): New function.
(frame_slots): Likewise.
(frame_payload): New macro.
(dump_stack_allocate_frame): New function.
(dump_stack_deallocate_frame): Likewise.
(dump_stack_preallocate_frame): Likewise.
(_s_return): Unpack frame object and deallocate it.
(s_save): Wrap state in an frame object.
(dump_stack_mark): Mark the free list.
--
TinySCHEME being a SECD-machine needs to push frames onto the dump
stack. Previously, the dump stack was a list. This required four
cells for the spine, as well as up to one additional cell to encode
the current opcode. This was quite inefficient despite the fact that
we recovered the spine as well as the integer cell.
We introduce frame objects, which are a special variant of vectors of
length four. Since the length is fixed, this frees up the length
field of the vector object to store the unboxed opcode. A frame
object now fits in two cells.
Saving two or three cells is a mere byproduct, the performance gain
comes from increased locality, unboxed opcode representation, and the
ability to easily put the objects in a free list, keeping the garbage
collector out of the continuous motion of the virtual machine.
Signed-off-by: Justus Winter <justus@g10code.com>
* tests/gpgscm/scheme-private.h (struct scheme): Remove field 'op'.
* tests/gpgscm/scheme.c (opexe_0): Inline into 'Eval_Cycle'.
(_Error_1): Return the opcode to evaluate next.
(Error_1): Do not return, but set the opcode and goto dispatch.
(Error_0): Likewise.
(s_goto): Likewise.
(s_return): Likewise.
(s_return_enable_gc): Likewise.
(s_thread_to): Remove superfluous cast.
(_s_return): Return the opcode to evaluate next.
(scheme_init_custom_alloc): Adapt to removal of field 'op'.
Signed-off-by: Justus Winter <justus@g10code.com>
* tests/gpgscm/scheme-private.h (CELL_MINRECOVER): New macro.
* tests/gpgscm/scheme.c (_get_cell): Move the heuristic to get more
cells...
(gc): ... here where every caller benefits from the optimization.
Signed-off-by: Justus Winter <justus@g10code.com>
* tests/gpgscm/Makefile.am (gpgscm_SOURCES): Add new file.
* tests/gpgscm/scheme-private.h (struct cell): Move number to the top
of the union so that we can initialize it.
(struct scheme): Remove 'integer_segment'.
* tests/gpgscm/scheme.c (initialize_small_integers): Remove function.
(small_integers): New variable.
(MAX_SMALL_INTEGER): Compute.
(mk_small_integer): Adapt.
(mark): Avoid marking objects already marked. This allows us to run
the algorithm over objects in the rodata section if they are already
marked.
(scheme_init_custom_alloc): Remove initialization.
(scheme_deinit): Remove deallocation.
* tests/gpgscm/small-integers.h: New file.
--
Allocate small integers from a fixed pool in the rodata section. This
spares us the initialization, and deduplicates integers across
different processes. It also makes the integers immutable, increasing
memory safety.
Signed-off-by: Justus Winter <justus@g10code.com>
* tests/gpgscm/scheme-private.h (struct scheme): Make 'vptr' const.
* tests/gpgscm/scheme.c (num_zero): Statically initialize and turn
into constant.
(num_one): Likewise.
(charnames): Change type so that it can be stored in rodata.
(is_ascii_name): Adapt slightly.
(assign_proc): Make argument const char *.
(op_code_info): Make some fields const char *.
(tests): Make const.
(dispatch_table): Make const. At least it can be made read-only after
relocation.
(Eval_Cycle): Adapt slightly.
(vtbl): Make const.
Signed-off-by: Justus Winter <justus@g10code.com>
* tests/gpgscm/scheme-private.h (struct scheme): Remove fixed-size
arrays for cell segments, replace them with a pointer to the new
'struct cell_segment' instead.
* tests/gpgscm/scheme.c (struct cell_segment): New definition.
(_alloc_cellseg): Allocate the header within the segment, return a
pointer to the header.
(_dealloc_cellseg): New function.
(alloc_cellseg): Insert the segments into a list.
(_get_cell): Allocate a new segment if less than a quarter of
CELL_SIGSIZE is recovered during garbage collection.
(initialize_small_integers): Adapt callsite.
(gc): Walk the list of segments.
(scheme_init_custom_alloc): Remove initialization of removed field.
(scheme_deinit): Adapt deallocation.
--
Previously the number of cells that could be allocated was a
compile-time limit. Remove this limit.
Signed-off-by: Justus Winter <justus@g10code.com>
* tests/gpgscm/scheme-private.h (struct cell): Use uintptr_t for
'_flags'. This way, '_flags' has the size of a machine word.
--
The compact vector representation introduced in 49e2ae65 requires that
we can tell apart pointers and type flags. This did not work on
64-bit big-endian architectures.
Fixes a crash on 64-bit big-endian architectures.
Hat-tip-to: gniibe
Fixes-commit: 49e2ae65e892f93be7f87cfaae3392b50a99e4b1
Signed-off-by: Justus Winter <justus@g10code.com>
* tests/gpgscm/scheme-private.h (struct port): Move location
information out of the union.
* tests/gpgscm/scheme.c (mark): All ports need marking now.
(gc): Likewise all ports on the load stack.
(port_clear_location): Adapt accordingly. Also, add an empty function
for !SHOW_ERROR_LINE.
(port_increment_current_line): Likewise.
(port_reset_current_line): Drop function in favor of...
(port_init_location): ... this new function.
(file_push): Simplify.
(file_pop): Likewise.
(port_rep_from_filename): Likewise.
(port_rep_from_file): Likewise.
(port_rep_from_string): Also initialize the location.
(port_rep_from_scratch): Likewise.
(port_close): Simplify and generalize.
(skipspace): Likewise.
(token): Likewise.
(_Error_1): Generalize.
(opexe_5): Likewise.
(scheme_deinit): Simplify and generalize.
(scheme_load_named_file): Likewise.
(scheme_load_string): Also initialize the location.
--
This change tracks the location of source code loaded from non-file
ports that is used in error messages. It also simplifies the code
quite a bit.
Signed-off-by: Justus Winter <justus@g10code.com>
* tests/gpgscm/scheme-private.h (struct cell): Add a compact vector
representation.
* tests/gpgscm/scheme.c (vector_length): Use new representation.
(vector_size): New macro.
(get_vector_object): Use the new representation.
(fill_vector): Likewise.
(vector_elem): Likewise.
(set_vector_elem): Likewise.
(mark): Likewise.
(gc): Likewise. Be careful not to confuse immediate values for type
flags.
(finalize_cell): Vectors now require finalization.
--
Previously, vectors were represented using consecutive cons cells,
wasting one word per cell for the type information. Fix that by using
a flat array.
Previously, a vector of size N required 1 + (n + 1) / 2 cells. Now it
uses 1 + (n - 1 + 2) / 3 cells.
Signed-off-by: Justus Winter <justus@g10code.com>
* tests/gpgscm/scheme-private.h (struct port): Use boxed values for
filename and current line. This allows us to use the same Scheme
object for labeling all expressions in a file.
* tests/gpgscm/scheme.c (file_push): Use boxed type for filename.
(mark): Mark location objects of port objects.
(gc): Mark location objects in the load stack.
(port_clear_location): New function.
(port_reset_current_line): Likewise.
(port_increment_current_line): Likewise.
(file_pop): Adapt accordingly.
(port_rep_from_filename): Likewise.
(port_rep_from_file): Likewise.
(port_close): Likewise.
(skipspace): Likewise.
(token): Likewise.
(_Error_1): Likewise.
(opexe_0): Likewise.
(opexe_5): Likewise.
(scheme_deinit): Likewise.
(scheme_load_file): Likewise.
(scheme_load_named_file): Likewise.
Signed-off-by: Justus Winter <justus@g10code.com>
* tests/gpgscm/init.scm (vm-history-print): New function.
* tests/gpgscm/opdefines.h: New opcodes 'CALLSTACK_POP', 'APPLY_CODE',
and 'VM_HISTORY'.
* tests/gpgscm/scheme-private.h (struct history): New definition.
(struct scheme): New field 'history'.
* tests/gpgscm/scheme.c (gc): Mark objects in the history.
(history_free): New function.
(history_init): Likewise.
(history_mark): Likewise.
(add_mod): New macro.
(sub_mod): Likewise.
(tailstack_clear): New function.
(callstack_pop): Likewise.
(callstack_push): Likewise.
(tailstack_push): Likewise.
(tailstack_flatten): Likewise.
(callstack_flatten): Likewise.
(history_flatten): Likewise.
(opexe_0): New variable 'callsite', keep track of the expression if it
is a call, implement the new opcodes, record function applications in
the history.
(opexe_6): Implement new opcode.
(scheme_init_custom_alloc): Initialize history.
(scheme_deinit): Free history.
* tests/gpgscm/scheme.h (USE_HISTORY): New macro.
--
This patch makes TinySCHEME keep a history of function calls. This
history can be used to produce helpful error messages. The history
data structure is inspired by MIT/GNU Scheme.
Signed-off-by: Justus Winter <justus@g10code.com>
fu history
* tests/gpgscm/scheme-private.h (struct scheme): Add field 'flags'.
* tests/gpgscm/scheme.c (S_OP_MASK): New macro.
(S_FLAG_MASK, s_set_flag, s_clear_flag, s_get_flag): Likewise.
(_s_return): Unpack the encoded opcode and flags.
(s_save): Encode the flags along with the opcode. Use normal
integers to encode the result.
(scheme_init_custom_alloc): Initialize 'op' and 'flags'.
Signed-off-by: Justus Winter <justus@g10code.com>
* tests/gpgscm/scheme-private.h (struct scheme): New fields for the
static integer cells.
* tests/gpgscm/scheme.c (_alloc_cellseg): New function.
(alloc_cellseg): Use the new function.
(MAX_SMALL_INTEGER): New macro.
(initialize_small_integers): New function.
(mk_small_integer): Likewise.
(mk_integer): Return a small integer if possible.
(_s_return): Do not free 'op' if it is a small integer.
(s_save): Use a small integer to box the opcode.
(scheme_init_custom_alloc): Initialize small integers.
(scheme_deinit): Free chunk of small integers.
* tests/gpgscm/scheme.h (USE_SMALL_INTEGERS): New macro.
Signed-off-by: Justus Winter <justus@g10code.com>
* tests/gpgscm/scheme-private.h (struct scheme): New fields
'inhibit_gc', 'reserved_cells', and 'reserved_lineno'.
* tests/gpgscm/scheme.c (GC_ENABLED): New macro.
(USE_GC_LOCKING): Likewise.
(gc_reservations): Likewise.
(gc_reservation_failure): New function.
(_gc_disable): Likewise.
(gc_disable): New macro.
(gc_enable): Likewise.
(gc_enabled): Likewise.
(gc_consume): Likewise.
(get_cell_x): Consume reserved cell if garbage collection is disabled.
(_get_cell): Assert that gc is enabled.
(get_cell): Only record cell in the list of recently allocated cells
if gc is enabled.
(get_vector_object): Likewise.
(gc): Assert that gc is enabled.
(s_return): Add comment, adjust call to '_s_return'.
(s_return_enable_gc): New macro.
(_s_return): Add flag 'enable_gc' and re-enable gc if set.
(oblist_add_by_name): Use the new facilities to protect the
allocations.
(new_frame_in_env): Likewise.
(new_slot_spec_in_env): Likewise.
(s_save): Likewise.
(opexe_0): Likewise.
(opexe_1): Likewise.
(opexe_2): Likewise.
(opexe_5): Likewise.
(opexe_6): Likewise.
(scheme_init_custom_alloc): Initialize the new fields.
--
Every time a cell is allocated, the interpreter may run out of free
cells and do a garbage collection. This is problematic because it
might garbage collect objects that have been allocated, but are not
yet made available to the interpreter.
Previously, we would plug such newly allocated cells into the list of
newly allocated objects rooted at car(sc->sink), but that requires
allocating yet another cell increasing pressure on the memory
management system.
A faster alternative is to preallocate the cells needed for an
operation and make sure the garbage collection is not run until all
allocated objects are plugged in. This can be done with gc_disable
and gc_enable.
This optimization can be applied incrementally. This commit picks all
low-hanging fruits.
Signed-off-by: Justus Winter <justus@g10code.com>
* tests/gpgscm/scheme-private.h (struct scheme): Use (void *) for
alloc_seg.
* tests/gpgscm/scheme.c (alloc_cellseg): Use (void *) for cp. Use
(void *) for coercion of address calculation.
--
In old C language, (char *) means an address. In modern C, it's
specifically an address with alignment=1. It's good to use (void *) for
an address, because newer compiler emits warnings. Note: in this
particular case, it is just a warning and the code is safe against
invalid alignment, though.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>