]> gitweb.factorcode.org Git - factor.git/commitdiff
vm: code heap compaction at runtime using compact-gc primitive
authorSlava Pestov <slava@slava-pestovs-macbook-pro.local>
Fri, 16 Oct 2009 16:39:22 +0000 (11:39 -0500)
committerSlava Pestov <slava@slava-pestovs-macbook-pro.local>
Fri, 16 Oct 2009 16:39:35 +0000 (11:39 -0500)
37 files changed:
Makefile
basis/bootstrap/image/image.factor
basis/compiler/tests/alien.factor
basis/cpu/ppc/bootstrap.factor
basis/cpu/ppc/ppc.factor
basis/cpu/x86/32/32.factor
basis/cpu/x86/32/bootstrap.factor
basis/cpu/x86/64/64.factor
basis/cpu/x86/64/bootstrap.factor
basis/cpu/x86/bootstrap.factor
basis/stack-checker/alien/alien.factor
basis/stack-checker/known-words/known-words.factor
core/alien/alien-docs.factor
core/alien/alien.factor
core/bootstrap/primitives.factor
vm/callbacks.cpp [new file with mode: 0644]
vm/callbacks.hpp [new file with mode: 0644]
vm/callstack.cpp
vm/code_block.cpp
vm/code_block.hpp
vm/code_heap.cpp
vm/collector.hpp
vm/contexts.cpp
vm/contexts.hpp
vm/debug.cpp
vm/errors.cpp
vm/factor.cpp
vm/full_collector.cpp
vm/full_collector.hpp
vm/gc.cpp
vm/image.cpp
vm/image.hpp
vm/master.hpp
vm/primitives.cpp
vm/quotations.cpp
vm/run.hpp
vm/vm.hpp

index f9eb353a34841041699c6f165f818f2e3b643a48..35cf7a05c4293a94fc18c6e340eff43b8dc9dbcc 100755 (executable)
--- a/Makefile
+++ b/Makefile
@@ -37,6 +37,7 @@ DLL_OBJS = $(PLAF_DLL_OBJS) \
        vm/bignum.o \
        vm/booleans.o \
        vm/byte_arrays.o \
+       vm/callbacks.o \
        vm/callstack.o \
        vm/code_block.o \
        vm/code_heap.o \
index d90cd71bc4f4671dbae3d2d96cce6d7986cee8d4..e086215e910b9cb4141b7f2a67c98b38a4041e75 100644 (file)
@@ -172,6 +172,8 @@ USERENV: jit-execute-jump 42
 USERENV: jit-execute-call 43
 USERENV: jit-declare-word 44
 
+USERENV: callback-stub 45
+
 ! PIC stubs
 USERENV: pic-load 47
 USERENV: pic-tag 48
index eaa8be72f0ed8a3f3bbb0c2978550f7bd6b6d4ea..1bf7a00c752b56fcdd9805a3330e2c8b263483b8 100755 (executable)
@@ -355,16 +355,12 @@ FUNCTION: ulonglong ffi_test_38 ( ulonglong x, ulonglong y ) ;
     "testing" callback-5 callback_test_1
 ] unit-test
 
-: callback-5a ( -- callback )
-    "void" { } "cdecl" [ 8000000 f <array> drop ] alien-callback ;
+: callback-5b ( -- callback )
+    "void" { } "cdecl" [ compact-gc ] alien-callback ;
 
-! Hack; if we're on ARM, we probably don't have much RAM, so
-! skip this test.
-! cpu "arm" = [
-!     [ "testing" ] [
-!         "testing" callback-5a callback_test_1
-!     ] unit-test
-! ] unless
+[ "testing" ] [
+    "testing" callback-5b callback_test_1
+] unit-test
 
 : callback-6 ( -- callback )
     "void" { } "cdecl" [ [ continue ] callcc0 ] alien-callback ;
@@ -593,3 +589,4 @@ FUNCTION: short ffi_test_48 ( bool-field-test x ) ;
 FUNCTION: void this_does_not_exist ( ) ;
 
 [ this_does_not_exist ] [ { "kernel-error" 10 f f } = ] must-fail-with
+
index 80c0124039e747224583b18dcc75c889808821ec..e1ad4441a3bd29d93e64b1b6c11a523ffbdae310 100644 (file)
@@ -249,6 +249,12 @@ CONSTANT: rs-reg 14
     ! fall-through on miss\r
 ] mega-lookup jit-define\r
 \r
+[\r
+    0 2 LOAD32 rc-absolute-cell rt-xt jit-rel\r
+    2 MTCTR\r
+    BCTR\r
+] callback-stub jit-define\r
+\r
 ! ! ! Sub-primitives\r
 \r
 ! Quotations and words\r
index bf239a696d3ecc8911dbb55bb63f7ac79b10b628..f9a44b5ffb03caa02c4b73850f5b3cee7369c902 100644 (file)
@@ -715,7 +715,9 @@ M: ppc %box-small-struct ( c-type -- )
     3 3 0 LWZ ;
 
 M: ppc %nest-stacks ( -- )
-    3 %load-vm-addr
+    ! Save current frame. See comment in vm/contexts.hpp
+    3 1 stack-frame get total-size>> 2 cells - ADDI
+    4 %load-vm-addr
     "nest_stacks" f %alien-invoke ;
 
 M: ppc %unnest-stacks ( -- )
index 60d2a42c01c4fe5cb917ea3adee1757be1351acb..ce73b4961ee865311cbb92084ce531f6db23059c 100755 (executable)
@@ -120,11 +120,9 @@ M: x86.32 %save-param-reg 3drop ;
     #! parameter being passed to a callback from C.
     over [ load-return-reg ] [ 2drop ] if ;
 
-CONSTANT: vm-ptr-size 4
-
 M:: x86.32 %box ( n rep func -- )
     n rep (%box)
-    rep rep-size vm-ptr-size + [
+    rep rep-size cell + [
         push-vm-ptr
         rep push-return-reg
         func f %alien-invoke
@@ -138,7 +136,7 @@ M:: x86.32 %box ( n rep func -- )
 
 M: x86.32 %box-long-long ( n func -- )
     [ (%box-long-long) ] dip
-    8 vm-ptr-size + [
+    12 [
         push-vm-ptr
         EDX PUSH
         EAX PUSH
@@ -148,7 +146,7 @@ M: x86.32 %box-long-long ( n func -- )
 M:: x86.32 %box-large-struct ( n c-type -- )
     ! Compute destination address
     EDX n struct-return@ LEA
-    8 vm-ptr-size + [
+    12 [
         push-vm-ptr
         ! Push struct size
         c-type heap-size PUSH
@@ -166,7 +164,7 @@ M: x86.32 %prepare-box-struct ( -- )
 
 M: x86.32 %box-small-struct ( c-type -- )
     #! Box a <= 8-byte struct returned in EAX:EDX. OS X only.
-    12 vm-ptr-size + [
+    16 [
         push-vm-ptr
         heap-size PUSH
         EDX PUSH
@@ -208,7 +206,7 @@ M: x86.32 %unbox-long-long ( n func -- )
 
 : %unbox-struct-1 ( -- )
     #! Alien must be in EAX.
-    4 vm-ptr-size + [
+    8 [
         push-vm-ptr
         EAX PUSH
         "alien_offset" f %alien-invoke
@@ -218,7 +216,7 @@ M: x86.32 %unbox-long-long ( n func -- )
 
 : %unbox-struct-2 ( -- )
     #! Alien must be in EAX.
-    4 vm-ptr-size + [
+    8 [
         push-vm-ptr
         EAX PUSH
         "alien_offset" f %alien-invoke
@@ -239,7 +237,7 @@ M:: x86.32 %unbox-large-struct ( n c-type -- )
     ! Alien must be in EAX.
     ! Compute destination address
     EDX n stack@ LEA
-    12 vm-ptr-size + [
+    16 [
         push-vm-ptr
         ! Push struct size
         c-type heap-size PUSH
@@ -252,8 +250,11 @@ M:: x86.32 %unbox-large-struct ( n c-type -- )
     ] with-aligned-stack ;
 
 M: x86.32 %nest-stacks ( -- )
-    4 [
+    8 [
         push-vm-ptr
+        ! Save current frame. See comment in vm/contexts.hpp
+        EAX stack-reg stack-frame get total-size>> [+] LEA
+        EAX PUSH
         "nest_stacks" f %alien-invoke
     ] with-aligned-stack ;
 
index 0540ccd6d6bcf52667a18e9eede2ea9891cef2f1..c5f6975d33b7a731ff655bdc8ed0d6a3f655e5fe 100644 (file)
@@ -17,6 +17,7 @@ IN: bootstrap.x86
 : temp1 ( -- reg ) EDX ;
 : temp2 ( -- reg ) ECX ;
 : temp3 ( -- reg ) EBX ;
+: safe-reg ( -- reg ) EAX ;
 : stack-reg ( -- reg ) ESP ;
 : ds-reg ( -- reg ) ESI ;
 : rs-reg ( -- reg ) EDI ;
index e2cd5c19728aefd9443649e0c82dcf000699e0db..c34530c307947e57b2d3aaccfda1d289ecd2494f 100644 (file)
@@ -194,7 +194,9 @@ M: x86.64 %alien-invoke
     R11 CALL ;
 
 M: x86.64 %nest-stacks ( -- )
-    param-reg-1 %mov-vm-ptr
+    ! Save current frame. See comment in vm/contexts.hpp
+    param-reg-1 stack-reg stack-frame get total-size>> 3 cells - [+] LEA
+    param-reg-2 %mov-vm-ptr
     "nest_stacks" f %alien-invoke ;
 
 M: x86.64 %unnest-stacks ( -- )
index e26e8042328e74e16f01265e581600c9fbc81207..b42a38b2d2d6feb513b96537361654726758c61c 100644 (file)
@@ -14,6 +14,7 @@ IN: bootstrap.x86
 : temp1 ( -- reg ) RSI ;
 : temp2 ( -- reg ) RDX ;
 : temp3 ( -- reg ) RBX ;
+: safe-reg ( -- reg ) RAX ;
 : stack-reg ( -- reg ) RSP ;
 : ds-reg ( -- reg ) R14 ;
 : rs-reg ( -- reg ) R15 ;
index eb4da296dc9d85601a76d3a0bf44c178262b80b3..fb94445f780bac9944012a3dc906111cbe67eb7c 100644 (file)
@@ -243,6 +243,11 @@ big-endian off
     ! fall-through on miss
 ] mega-lookup jit-define
 
+[
+    safe-reg 0 MOV rc-absolute-cell rt-xt jit-rel
+    safe-reg JMP
+] callback-stub jit-define
+
 ! ! ! Sub-primitives
 
 ! Quotations and words
index 3d150adf9117774057ca51f84080c9a971de5dd0..2a20ba74cd79b0ced07da1cb47eee363cd2bc556 100644 (file)
@@ -1,8 +1,7 @@
-! Copyright (C) 2008 Slava Pestov.
+! Copyright (C) 2008, 2009 Slava Pestov.
 ! See http://factorcode.org/license.txt for BSD license.
 USING: kernel sequences accessors combinators math namespaces
-init sets words alien.libraries
-alien alien.c-types
+init sets words assocs alien.libraries alien alien.c-types
 stack-checker.backend stack-checker.errors stack-checker.visitor ;
 IN: stack-checker.alien
 
@@ -58,11 +57,11 @@ TUPLE: alien-callback-params < alien-node-params quot xt ;
     ! Quotation which coerces return value to required type
     return-prep-quot infer-quot-here ;
 
-: register-callback ( word -- ) callbacks get conjoin ;
+: callback-xt ( word -- alien )
+    callbacks get [ <callback> ] cache ;
 
 : callback-bottom ( params -- )
-    xt>> [ [ register-callback ] [ word-xt drop <alien> ] bi ] curry
-    infer-quot-here ;
+    xt>> [ callback-xt ] curry infer-quot-here ;
 
 : infer-alien-callback ( -- )
     alien-callback-params new
@@ -70,6 +69,6 @@ TUPLE: alien-callback-params < alien-node-params quot xt ;
     pop-literal nip >>abi
     pop-literal nip >>parameters
     pop-literal nip >>return
-    gensym >>xt
+    "( callback )" f <word> >>xt
     dup callback-bottom
     #alien-callback, ;
index e5d8f6231cec82d4f935a30bfcf7924f6ac32975..8cddac5a752e52e8871da9048d071a166811d325 100644 (file)
@@ -495,8 +495,12 @@ M: bad-executable summary
 
 \ (exists?) { string } { object } define-primitive
 
+\ minor-gc { } { } define-primitive
+
 \ gc { } { } define-primitive
 
+\ compact-gc { } { } define-primitive
+
 \ gc-stats { } { array } define-primitive
 
 \ (save-image) { byte-array } { } define-primitive
@@ -711,3 +715,7 @@ M: bad-executable summary
 \ inline-cache-stats { } { array } define-primitive
 
 \ optimized? { word } { object } define-primitive
+
+\ strip-stack-traces { } { } define-primitive
+
+\ <callback> { word } { alien } define-primitive
index 3f91b7102fffe23bc7d0861dc4d2bfce43bf233f..9fb9c042eea605d96f7b73ffd4de267d7076f159 100644 (file)
@@ -175,13 +175,6 @@ HELP: alien-invoke-error
     }
 } ;
 
-ARTICLE: "alien-callback-gc" "Callbacks and code GC"
-"A callback consits of two parts; the callback word, which pushes the address of the callback on the stack when executed, and the callback body itself. If the callback word is redefined, removed from the dictionary using " { $link forget } ", or recompiled, the callback body will not be reclaimed by the garbage collector, since potentially C code may be holding a reference to the callback body."
-$nl
-"This is the safest approach, however it can lead to code heap leaks when repeatedly reloading code which defines callbacks. If you are " { $emphasis "completely sure" } " that no running C code is holding a reference to any callbacks, you can blow them all away:"
-{ $code "USE: alien callbacks get clear-hash gc" }
-"This will reclaim all callback bodies which are otherwise unreachable from the dictionary (that is, their associated callback words have since been redefined, recompiled or forgotten)." ;
-
 ARTICLE: "alien-callback" "Calling Factor from C"
 "Callbacks can be defined and passed to C code as function pointers; the C code can then invoke the callback and run Factor code:"
 { $subsections
@@ -189,7 +182,6 @@ ARTICLE: "alien-callback" "Calling Factor from C"
     POSTPONE: CALLBACK:
 }
 "There are some caveats concerning the conversion of Factor objects to C values, and vice versa. See " { $link "c-data" } "."
-{ $subsections "alien-callback-gc" }
 { $see-also "byte-arrays-gc" } ;
 
 ARTICLE: "alien-globals" "Accessing C global variables"
index d98ea3d1032a019d7367aba509fa88e9c07e99c0..3f2b5f95bf18219f4ec7533610c930221d7a266a 100644 (file)
@@ -66,8 +66,10 @@ ERROR: alien-invoke-error library symbol ;
 : alien-invoke ( ... return library function parameters -- ... )
     2over alien-invoke-error ;
 
-! Callbacks are registered in a global hashtable. If you clear
-! this hashtable, they will all be blown away by code GC, beware.
+! Callbacks are registered in a global hashtable. Note that they
+! are also pinned in a special callback area, so clearing this
+! hashtable will not reclaim callbacks. It should only be
+! cleared on startup.
 SYMBOL: callbacks
 
 [ H{ } clone callbacks set-global ] "alien" add-init-hook
index 23da40d2c5d67146aaf9e9083ae4096f20fed74e..ef66cc3cd6957d32dcecd911d6a22ffdbf74f452 100644 (file)
@@ -420,7 +420,9 @@ tuple
     { "getenv" "kernel.private" (( n -- obj )) }
     { "setenv" "kernel.private" (( obj n -- )) }
     { "(exists?)" "io.files.private" (( path -- ? )) }
+    { "minor-gc" "memory" (( -- )) }
     { "gc" "memory" (( -- )) }
+    { "compact-gc" "memory" (( -- )) }
     { "gc-stats" "memory" f }
     { "(save-image)" "memory.private" (( path -- )) }
     { "(save-image-and-exit)" "memory.private" (( path -- )) }
@@ -523,6 +525,7 @@ tuple
     { "quot-compiled?" "quotations" (( quot -- ? )) }
     { "vm-ptr" "vm" (( -- ptr )) }
     { "strip-stack-traces" "kernel.private" (( -- )) }
+    { "<callback>" "alien" (( word -- alien )) }
 } [ [ first3 ] dip swap make-primitive ] each-index
 
 ! Bump build number
diff --git a/vm/callbacks.cpp b/vm/callbacks.cpp
new file mode 100644 (file)
index 0000000..2401800
--- /dev/null
@@ -0,0 +1,66 @@
+#include "master.hpp"
+
+namespace factor
+{
+
+callback_heap::callback_heap(cell size, factor_vm *myvm_) :
+       seg(new segment(size,true)),
+       here(seg->start),
+       myvm(myvm_) {}
+
+callback_heap::~callback_heap()
+{
+       delete seg;
+       seg = NULL;
+}
+
+void factor_vm::init_callbacks(cell size)
+{
+       callbacks = new callback_heap(size,this);
+}
+
+void callback_heap::update(callback *stub)
+{
+       tagged<array> code_template(myvm->userenv[CALLBACK_STUB]);
+
+       cell rel_class = untag_fixnum(array_nth(code_template.untagged(),1));
+       cell offset = untag_fixnum(array_nth(code_template.untagged(),3));
+
+       myvm->store_address_in_code_block(rel_class,
+               (cell)(stub + 1) + offset,
+               (cell)(stub->compiled + 1));
+
+       flush_icache((cell)stub,stub->size);
+}
+
+callback *callback_heap::add(code_block *compiled)
+{
+       tagged<array> code_template(myvm->userenv[CALLBACK_STUB]);
+       tagged<byte_array> insns(array_nth(code_template.untagged(),0));
+       cell size = array_capacity(insns.untagged());
+
+       cell bump = align8(size) + sizeof(callback);
+       if(here + bump > seg->end) fatal_error("Out of callback space",0);
+
+       callback *stub = (callback *)here;
+       stub->compiled = compiled;
+       memcpy(stub + 1,insns->data<void>(),size);
+
+       stub->size = align8(size);
+       here += bump;
+
+       update(stub);
+
+       return stub;
+}
+
+void factor_vm::primitive_callback()
+{
+       tagged<word> w(dpop());
+       w.untag_check(this);
+
+       callback *stub = callbacks->add(w->code);
+       box_alien(stub + 1);
+}
+
+}
diff --git a/vm/callbacks.hpp b/vm/callbacks.hpp
new file mode 100644 (file)
index 0000000..fd30443
--- /dev/null
@@ -0,0 +1,38 @@
+namespace factor
+{
+
+struct callback {
+       cell size;
+       code_block *compiled;
+       void *code() { return (void *)(this + 1); }
+};
+
+struct callback_heap {
+       segment *seg;
+       cell here;
+       factor_vm *myvm;
+
+       explicit callback_heap(cell size, factor_vm *myvm);
+       ~callback_heap();
+
+       callback *callback_heap::add(code_block *compiled);
+       void update(callback *stub);
+
+       callback *next(callback *stub)
+       {
+               return (callback *)((cell)stub + stub->size + sizeof(callback));
+       }
+
+       template<typename Iterator> void iterate(Iterator &iter)
+       {
+               callback *scan = (callback *)seg->start;
+               callback *end = (callback *)here;
+               while(scan < end)
+               {
+                       iter(scan);
+                       scan = next(scan);
+               }
+       }
+};
+
+}
index 3d752913c739494ce2bc93c1b956a12487fb0f86..0b7663e513542b5c5ce3fe7404f2b0a69e67d435 100755 (executable)
@@ -37,19 +37,16 @@ called by continuation implementation, and user code shouldn't
 be calling it at all, so we leave it as it is for now. */
 stack_frame *factor_vm::capture_start()
 {
-       stack_frame *frame = stack_chain->callstack_bottom - 1;
-       while(frame >= stack_chain->callstack_top
-               && frame_successor(frame) >= stack_chain->callstack_top)
-       {
+       stack_frame *frame = ctx->callstack_bottom - 1;
+       while(frame >= ctx->callstack_top && frame_successor(frame) >= ctx->callstack_top)
                frame = frame_successor(frame);
-       }
        return frame + 1;
 }
 
 void factor_vm::primitive_callstack()
 {
        stack_frame *top = capture_start();
-       stack_frame *bottom = stack_chain->callstack_bottom;
+       stack_frame *bottom = ctx->callstack_bottom;
 
        fixnum size = (cell)bottom - (cell)top;
        if(size < 0)
@@ -64,7 +61,7 @@ void factor_vm::primitive_set_callstack()
 {
        callstack *stack = untag_check<callstack>(dpop());
 
-       set_callstack(stack_chain->callstack_bottom,
+       set_callstack(ctx->callstack_bottom,
                stack->top(),
                untag_fixnum(stack->length),
                memcpy);
@@ -204,7 +201,7 @@ void factor_vm::primitive_set_innermost_stack_frame_quot()
 /* called before entry into Factor code. */
 void factor_vm::save_callstack_bottom(stack_frame *callstack_bottom)
 {
-       stack_chain->callstack_bottom = callstack_bottom;
+       ctx->callstack_bottom = callstack_bottom;
 }
 
 VM_ASM_API void save_callstack_bottom(stack_frame *callstack_bottom, factor_vm *myvm)
index 8099d09e2d7c233ea8f68cd8cf69c79aca3bc06e..0afd98dd0faca98e50dd4cd003d4f6fffec6c2f4 100755 (executable)
@@ -39,7 +39,7 @@ int factor_vm::number_of_parameters(relocation_type type)
        case RT_DLSYM:
                return 2;
        case RT_THIS:
-       case RT_STACK_CHAIN:
+       case RT_CONTEXT:
        case RT_MEGAMORPHIC_CACHE_HITS:
        case RT_CARDS_OFFSET:
        case RT_DECKS_OFFSET:
@@ -174,8 +174,8 @@ cell factor_vm::compute_relocation(relocation_entry rel, cell index, code_block
        }
        case RT_THIS:
                return (cell)(compiled + 1);
-       case RT_STACK_CHAIN:
-               return (cell)&stack_chain;
+       case RT_CONTEXT:
+               return (cell)&ctx;
        case RT_UNTAGGED:
                return untag_fixnum(ARG);
        case RT_MEGAMORPHIC_CACHE_HITS:
@@ -315,8 +315,8 @@ void factor_vm::relocate_code_block_step(relocation_entry rel, cell index, code_
 #endif
 
        store_address_in_code_block(relocation_class_of(rel),
-                                   relocation_offset_of(rel) + (cell)compiled->xt(),
-                                   compute_relocation(rel,index,compiled));
+               relocation_offset_of(rel) + (cell)compiled->xt(),
+               compute_relocation(rel,index,compiled));
 }
 
 struct word_references_updater {
@@ -441,10 +441,14 @@ code_block *factor_vm::allot_code_block(cell size, cell type)
 {
        heap_block *block = code->heap_allot(size + sizeof(code_block),type);
 
-       /* If allocation failed, do a code GC */
+       /* If allocation failed, do a full GC and compact the code heap.
+       A full GC that occurs as a result of the data heap filling up does not
+       trigger a compaction. This setup ensures that most GCs do not compact
+       the code heap, but if the code fills up, it probably means it will be
+       fragmented after GC anyway, so its best to compact. */
        if(block == NULL)
        {
-               primitive_full_gc();
+               primitive_compact_gc();
                block = code->heap_allot(size + sizeof(code_block),type);
 
                /* Insufficient room even after code GC, give up */
index e3d392001be590fa90afd817120dadcfbdfd1549..d31a776c0ef80ee9f80efb4ee9ce47c86be0d536 100644 (file)
@@ -20,8 +20,8 @@ enum relocation_type {
        RT_THIS,
        /* immediate literal */
        RT_IMMEDIATE,
-       /* address of stack_chain var */
-       RT_STACK_CHAIN,
+       /* address of ctx var */
+       RT_CONTEXT,
        /* untagged fixnum literal */
        RT_UNTAGGED,
        /* address of megamorphic_cache_hits var */
index d60ec189cdae12a7ea3c17d99ef8069e27dd3ba2..b7307dd7e636dcf47b7fbffbc0e4562277b65699 100755 (executable)
@@ -208,14 +208,28 @@ void factor_vm::forward_object_xts()
 
 void factor_vm::forward_context_xts()
 {
-       context *ctx = stack_chain;
+       callframe_forwarder forwarder(this);
+       iterate_active_frames(forwarder);
+}
+
+struct callback_forwarder {
+       code_heap *code;
+       callback_heap *callbacks;
 
-       while(ctx)
+       callback_forwarder(code_heap *code_, callback_heap *callbacks_) :
+               code(code_), callbacks(callbacks_) {}
+
+       void operator()(callback *stub)
        {
-               callframe_forwarder forwarder(this);
-               iterate_callstack(ctx,forwarder);
-               ctx = ctx->next;
+               stub->compiled = code->forward_code_block(stub->compiled);
+               callbacks->update(stub);
        }
+};
+
+void factor_vm::forward_callback_xts()
+{
+       callback_forwarder forwarder(code,callbacks);
+       callbacks->iterate(forwarder);
 }
 
 /* Move all free space to the end of the code heap. Live blocks must be marked
@@ -225,7 +239,11 @@ void factor_vm::compact_code_heap(bool trace_contexts_p)
 {
        code->compact_heap();
        forward_object_xts();
-       if(trace_contexts_p) forward_context_xts();
+       if(trace_contexts_p)
+       {
+               forward_context_xts();
+               forward_callback_xts();
+       }
 }
 
 struct stack_trace_stripper {
index c70c5073e22c814ab33dd3b01f8277ca8bdc000b..8156fd16930d23fa69c3abfc9d1c90fd2e2eb158 100644 (file)
@@ -132,7 +132,7 @@ template<typename TargetGeneration, typename Policy> struct collector {
 
        void trace_contexts()
        {
-               context *ctx = myvm->stack_chain;
+               context *ctx = myvm->ctx;
 
                while(ctx)
                {
index 050f4b3db66d40aeaec3a8c02c745ba21c69619f..bed044250e50283b690ef99ddd9d660546660643 100644 (file)
@@ -25,10 +25,10 @@ void factor_vm::fix_stacks()
 be stored in registers, so callbacks must save and restore the correct values */
 void factor_vm::save_stacks()
 {
-       if(stack_chain)
+       if(ctx)
        {
-               stack_chain->datastack = ds;
-               stack_chain->retainstack = rs;
+               ctx->datastack = ds;
+               ctx->retainstack = rs;
        }
 }
 
@@ -58,12 +58,12 @@ void factor_vm::dealloc_context(context *old_context)
 }
 
 /* called on entry into a compiled callback */
-void factor_vm::nest_stacks()
+void factor_vm::nest_stacks(stack_frame *magic_frame)
 {
-       context *new_context = alloc_context();
+       context *new_ctx = alloc_context();
 
-       new_context->callstack_bottom = (stack_frame *)-1;
-       new_context->callstack_top = (stack_frame *)-1;
+       new_ctx->callstack_bottom = (stack_frame *)-1;
+       new_ctx->callstack_top = (stack_frame *)-1;
 
        /* note that these register values are not necessarily valid stack
        pointers. they are merely saved non-volatile registers, and are
@@ -75,37 +75,39 @@ void factor_vm::nest_stacks()
        - Factor callback returns
        - C function restores registers
        - C function returns to Factor code */
-       new_context->datastack_save = ds;
-       new_context->retainstack_save = rs;
+       new_ctx->datastack_save = ds;
+       new_ctx->retainstack_save = rs;
+
+       new_ctx->magic_frame = magic_frame;
 
        /* save per-callback userenv */
-       new_context->current_callback_save = userenv[CURRENT_CALLBACK_ENV];
-       new_context->catchstack_save = userenv[CATCHSTACK_ENV];
+       new_ctx->current_callback_save = userenv[CURRENT_CALLBACK_ENV];
+       new_ctx->catchstack_save = userenv[CATCHSTACK_ENV];
 
-       new_context->next = stack_chain;
-       stack_chain = new_context;
+       new_ctx->next = ctx;
+       ctx = new_ctx;
 
        reset_datastack();
        reset_retainstack();
 }
 
-void nest_stacks(factor_vm *myvm)
+void nest_stacks(stack_frame *magic_frame, factor_vm *myvm)
 {
-       return myvm->nest_stacks();
+       return myvm->nest_stacks(magic_frame);
 }
 
 /* called when leaving a compiled callback */
 void factor_vm::unnest_stacks()
 {
-       ds = stack_chain->datastack_save;
-       rs = stack_chain->retainstack_save;
+       ds = ctx->datastack_save;
+       rs = ctx->retainstack_save;
 
        /* restore per-callback userenv */
-       userenv[CURRENT_CALLBACK_ENV] = stack_chain->current_callback_save;
-       userenv[CATCHSTACK_ENV] = stack_chain->catchstack_save;
+       userenv[CURRENT_CALLBACK_ENV] = ctx->current_callback_save;
+       userenv[CATCHSTACK_ENV] = ctx->catchstack_save;
 
-       context *old_ctx = stack_chain;
-       stack_chain = old_ctx->next;
+       context *old_ctx = ctx;
+       ctx = old_ctx->next;
        dealloc_context(old_ctx);
 }
 
@@ -119,7 +121,7 @@ void factor_vm::init_stacks(cell ds_size_, cell rs_size_)
 {
        ds_size = ds_size_;
        rs_size = rs_size_;
-       stack_chain = NULL;
+       ctx = NULL;
        unused_contexts = NULL;
 }
 
index ea70a7ba6f99fb6fd56a162bb39362491a1cfa8f..f66b5d0fe2e0c4474867daea68b2d5dbd59f26c6 100644 (file)
@@ -23,6 +23,18 @@ struct context {
        /* saved contents of rs register on entry to callback */
        cell retainstack_save;
 
+       /* callback-bottom stack frame, or NULL for top-level context.
+       When nest_stacks() is called, callstack layout with callbacks
+       is as follows:
+       
+       [ C function ]
+       [ callback stub in code heap ] <-- this is the magic frame
+       [ native frame: c_to_factor() ]
+       [ callback quotation frame ] <-- first call frame in call stack
+       
+       magic frame is retained so that it's XT can be traced and forwarded. */
+       stack_frame *magic_frame;
+
        /* memory region holding current datastack */
        segment *datastack_region;
 
@@ -36,15 +48,15 @@ struct context {
        context *next;
 };
 
-#define ds_bot (stack_chain->datastack_region->start)
-#define ds_top (stack_chain->datastack_region->end)
-#define rs_bot (stack_chain->retainstack_region->start)
-#define rs_top (stack_chain->retainstack_region->end)
+#define ds_bot (ctx->datastack_region->start)
+#define ds_top (ctx->datastack_region->end)
+#define rs_bot (ctx->retainstack_region->start)
+#define rs_top (ctx->retainstack_region->end)
 
 DEFPUSHPOP(d,ds)
 DEFPUSHPOP(r,rs)
 
-VM_C_API void nest_stacks(factor_vm *vm);
+VM_C_API void nest_stacks(stack_frame *magic_frame, factor_vm *vm);
 VM_C_API void unnest_stacks(factor_vm *vm);
 
 }
index 0f8c310e0678c08525b18784aa4834d74f5a5e23..da8c6032545ccadb6cf2c2b840aaf9b2bab6fe15 100755 (executable)
@@ -190,7 +190,7 @@ void factor_vm::print_callstack()
 {
        print_string("==== CALL STACK:\n");
        stack_frame_printer printer(this);
-       iterate_callstack(stack_chain,printer);
+       iterate_callstack(ctx,printer);
 }
 
 void factor_vm::dump_cell(cell x)
index 0ea3589eecba22411a01f75fc152726668f7d400..a3c0242d7eddbf2ff7117dfc98ea34665df546a1 100755 (executable)
@@ -49,12 +49,9 @@ void factor_vm::throw_error(cell error, stack_frame *callstack_top)
                actual stack pointer at the time, since the saved pointer is
                not necessarily up to date at that point. */
                if(callstack_top)
-               {
-                       callstack_top = fix_callstack_top(callstack_top,
-                               stack_chain->callstack_bottom);
-               }
+                       callstack_top = fix_callstack_top(callstack_top,ctx->callstack_bottom);
                else
-                       callstack_top = stack_chain->callstack_top;
+                       callstack_top = ctx->callstack_top;
 
                throw_impl(userenv[BREAK_ENV],callstack_top,this);
        }
@@ -130,7 +127,7 @@ void factor_vm::fp_trap_error(unsigned int fpu_status, stack_frame *signal_calls
 
 void factor_vm::primitive_call_clear()
 {
-       throw_impl(dpop(),stack_chain->callstack_bottom,this);
+       throw_impl(dpop(),ctx->callstack_bottom,this);
 }
 
 /* For testing purposes */
index 9a26cacf1ac394eb1db09cf8924d1294edb56ef2..d7f8cf0fbee1b240a1ef2371da4b620d0745f05e 100755 (executable)
@@ -39,6 +39,7 @@ void factor_vm::default_parameters(vm_parameters *p)
 
        p->secure_gc = false;
        p->fep = false;
+       p->signals = true;
 
 #ifdef WINDOWS
        p->console = false;
@@ -49,6 +50,8 @@ void factor_vm::default_parameters(vm_parameters *p)
                p->console = false;
        
 #endif
+
+       p->callback_size = 256;
 }
 
 bool factor_vm::factor_arg(const vm_char* str, const vm_char* arg, cell* value)
@@ -72,17 +75,21 @@ void factor_vm::init_parameters_from_args(vm_parameters *p, int argc, vm_char **
 
        for(i = 1; i < argc; i++)
        {
-               if(factor_arg(argv[i],STRING_LITERAL("-datastack=%d"),&p->ds_size));
-               else if(factor_arg(argv[i],STRING_LITERAL("-retainstack=%d"),&p->rs_size));
-               else if(factor_arg(argv[i],STRING_LITERAL("-young=%d"),&p->young_size));
-               else if(factor_arg(argv[i],STRING_LITERAL("-aging=%d"),&p->aging_size));
-               else if(factor_arg(argv[i],STRING_LITERAL("-tenured=%d"),&p->tenured_size));
-               else if(factor_arg(argv[i],STRING_LITERAL("-codeheap=%d"),&p->code_size));
-               else if(factor_arg(argv[i],STRING_LITERAL("-pic=%d"),&p->max_pic_size));
-               else if(STRCMP(argv[i],STRING_LITERAL("-securegc")) == 0) p->secure_gc = true;
-               else if(STRCMP(argv[i],STRING_LITERAL("-fep")) == 0) p->fep = true;
-               else if(STRNCMP(argv[i],STRING_LITERAL("-i="),3) == 0) p->image_path = argv[i] + 3;
-               else if(STRCMP(argv[i],STRING_LITERAL("-console")) == 0) p->console = true;
+               vm_char *arg = argv[i];
+               if(STRCMP(arg,"--") == 0) break;
+               else if(factor_arg(arg,STRING_LITERAL("-datastack=%d"),&p->ds_size));
+               else if(factor_arg(arg,STRING_LITERAL("-retainstack=%d"),&p->rs_size));
+               else if(factor_arg(arg,STRING_LITERAL("-young=%d"),&p->young_size));
+               else if(factor_arg(arg,STRING_LITERAL("-aging=%d"),&p->aging_size));
+               else if(factor_arg(arg,STRING_LITERAL("-tenured=%d"),&p->tenured_size));
+               else if(factor_arg(arg,STRING_LITERAL("-codeheap=%d"),&p->code_size));
+               else if(factor_arg(arg,STRING_LITERAL("-pic=%d"),&p->max_pic_size));
+               else if(factor_arg(arg,STRING_LITERAL("-callbacks=%d"),&p->callback_size));
+               else if(STRCMP(arg,STRING_LITERAL("-securegc")) == 0) p->secure_gc = true;
+               else if(STRCMP(arg,STRING_LITERAL("-fep")) == 0) p->fep = true;
+               else if(STRCMP(arg,STRING_LITERAL("-nosignals")) == 0) p->signals = false;
+               else if(STRNCMP(arg,STRING_LITERAL("-i="),3) == 0) p->image_path = arg + 3;
+               else if(STRCMP(arg,STRING_LITERAL("-console")) == 0) p->console = true;
        }
 }
 
@@ -104,6 +111,7 @@ void factor_vm::init_factor(vm_parameters *p)
        /* Kilobytes */
        p->ds_size = align_page(p->ds_size << 10);
        p->rs_size = align_page(p->rs_size << 10);
+       p->callback_size = align_page(p->callback_size << 10);
 
        /* Megabytes */
        p->young_size <<= 20;
@@ -128,10 +136,12 @@ void factor_vm::init_factor(vm_parameters *p)
        srand(current_micros());
        init_ffi();
        init_stacks(p->ds_size,p->rs_size);
+       init_callbacks(p->callback_size);
        load_image(p);
        init_c_io();
        init_inline_caching(p->max_pic_size);
-       init_signals();
+       if(p->signals)
+               init_signals();
 
        if(p->console)
                open_console();
@@ -170,7 +180,7 @@ void factor_vm::start_factor(vm_parameters *p)
 {
        if(p->fep) factorbug();
 
-       nest_stacks();
+       nest_stacks(NULL);
        c_to_factor_toplevel(userenv[BOOT_ENV]);
        unnest_stacks();
 }
index e5c95b3d79454cf688c362ff94ffa1691109c163..86f3216e9ca45fc5156a57d92c134f3bc7ac24bb 100644 (file)
@@ -26,14 +26,8 @@ struct stack_frame_marker {
 /* Mark code blocks executing in currently active stack frames. */
 void full_collector::mark_active_blocks()
 {
-       context *ctx = this->myvm->stack_chain;
-
-       while(ctx)
-       {
-               stack_frame_marker marker(this);
-               myvm->iterate_callstack(ctx,marker);
-               ctx = ctx->next;
-       }
+       stack_frame_marker marker(this);
+       myvm->iterate_active_frames(marker);
 }
 
 void full_collector::mark_object_code_block(object *obj)
@@ -66,6 +60,23 @@ void full_collector::mark_object_code_block(object *obj)
        }
 }
 
+struct callback_tracer {
+       full_collector *collector;
+
+       callback_tracer(full_collector *collector_) : collector(collector_) {}
+       
+       void operator()(callback *stub)
+       {
+               collector->mark_code_block(stub->compiled);
+       }
+};
+
+void full_collector::trace_callbacks()
+{
+       callback_tracer tracer(this);
+       myvm->callbacks->iterate(tracer);
+}
+
 /* Trace all literals referenced from a code block. Only for aging and nursery collections */
 void full_collector::trace_literal_references(code_block *compiled)
 {
@@ -95,10 +106,10 @@ void full_collector::cheneys_algorithm()
 
 /* After growing the heap, we have to perform a full relocation to update
 references to card and deck arrays. */
-struct after_growing_heap_updater {
+struct big_code_heap_updater {
        factor_vm *myvm;
 
-       after_growing_heap_updater(factor_vm *myvm_) : myvm(myvm_) {}
+       big_code_heap_updater(factor_vm *myvm_) : myvm(myvm_) {}
 
        void operator()(heap_block *block)
        {
@@ -108,10 +119,10 @@ struct after_growing_heap_updater {
 
 /* After a full GC that did not grow the heap, we have to update references
 to literals and other words. */
-struct after_full_updater {
+struct small_code_heap_updater {
        factor_vm *myvm;
 
-       after_full_updater(factor_vm *myvm_) : myvm(myvm_) {}
+       small_code_heap_updater(factor_vm *myvm_) : myvm(myvm_) {}
 
        void operator()(heap_block *block)
        {
@@ -128,6 +139,7 @@ void factor_vm::collect_full_impl(bool trace_contexts_p)
        {
                collector.trace_contexts();
                collector.mark_active_blocks();
+               collector.trace_callbacks();
        }
 
        collector.cheneys_algorithm();
@@ -139,6 +151,13 @@ void factor_vm::collect_full_impl(bool trace_contexts_p)
 /* In both cases, compact code heap before updating code blocks so that
 XTs are correct after */
 
+void factor_vm::big_code_heap_update()
+{
+       big_code_heap_updater updater(this);
+       code->free_unmarked(updater);
+       code->clear_remembered_set();
+}
+
 void factor_vm::collect_growing_heap(cell requested_bytes,
        bool trace_contexts_p,
        bool compact_code_heap_p)
@@ -151,7 +170,12 @@ void factor_vm::collect_growing_heap(cell requested_bytes,
 
        if(compact_code_heap_p) compact_code_heap(trace_contexts_p);
 
-       after_growing_heap_updater updater(this);
+       big_code_heap_update();
+}
+
+void factor_vm::small_code_heap_update()
+{
+       small_code_heap_updater updater(this);
        code->free_unmarked(updater);
        code->clear_remembered_set();
 }
@@ -163,11 +187,13 @@ void factor_vm::collect_full(bool trace_contexts_p, bool compact_code_heap_p)
        reset_generation(data->tenured);
        collect_full_impl(trace_contexts_p);
 
-       if(compact_code_heap_p) compact_code_heap(trace_contexts_p);
-
-       after_full_updater updater(this);
-       code->free_unmarked(updater);
-       code->clear_remembered_set();
+       if(compact_code_heap_p)
+       {
+               compact_code_heap(trace_contexts_p);
+               big_code_heap_update();
+       }
+       else
+               small_code_heap_update();
 }
 
 }
index d39358e0e2e3c064c9eec2ed9861dbff1aed5b34..c01f1cd4863de17ab3b728a3e72d1bc3a59d1701 100644 (file)
@@ -19,6 +19,7 @@ struct full_collector : copying_collector<tenured_space,full_policy> {
        full_collector(factor_vm *myvm_);
        void mark_active_blocks();
        void mark_object_code_block(object *object);
+       void trace_callbacks();
        void trace_literal_references(code_block *compiled);
        void mark_code_block(code_block *compiled);
        void cheneys_algorithm();
index ebfed1fa2304461bdf24f1684da4fac2f868de3a..c89add6066360379e9c15fc0ac84c07bf2f8114f 100755 (executable)
--- a/vm/gc.cpp
+++ b/vm/gc.cpp
@@ -96,17 +96,17 @@ void factor_vm::gc(gc_op op,
        current_gc = NULL;
 }
 
-void factor_vm::primitive_full_gc()
+void factor_vm::primitive_minor_gc()
 {
-       gc(collect_full_op,
+       gc(collect_nursery_op,
                0, /* requested size */
                true, /* trace contexts? */
                false /* compact code heap? */);
 }
 
-void factor_vm::primitive_minor_gc()
+void factor_vm::primitive_full_gc()
 {
-       gc(collect_nursery_op,
+       gc(collect_full_op,
                0, /* requested size */
                true, /* trace contexts? */
                false /* compact code heap? */);
index 96ec2ce93001a7d9350f4d4a0db6cb6c98fec05d..05e0d66724132fd27a88ca2fbe4ae545b3ed197c 100755 (executable)
@@ -114,7 +114,7 @@ bool factor_vm::save_image(const vm_char *filename)
 void factor_vm::primitive_save_image()
 {
        /* do a full GC to push everything into tenured space */
-       primitive_full_gc();
+       primitive_compact_gc();
 
        gc_root<byte_array> path(dpop(),this);
        path.untag_check(this);
index 80ee2556e9cacf25a4929e66b863dfa216ccce28..f0711858522a40aa5db404e424d168b86e9c579a 100755 (executable)
@@ -37,7 +37,9 @@ struct vm_parameters {
        bool secure_gc;
        bool fep;
        bool console;
+       bool signals;
        cell max_pic_size;
+       cell callback_size;
 };
 
 }
index 2058af8c43ab2e915e16033f39b5029c1e949da3..c5aed5e983c38657576a3e29787381595be62e81 100755 (executable)
@@ -82,6 +82,7 @@ namespace factor
 #include "image.hpp"
 #include "alien.hpp"
 #include "code_heap.hpp"
+#include "callbacks.hpp"
 #include "vm.hpp"
 #include "tagged.hpp"
 #include "local_roots.hpp"
index cc3c89b47e881269f4d29acc383f33287dcdeede..ea0254514827ea2c84769372e4c25b1dee02c36e 100644 (file)
@@ -52,7 +52,9 @@ PRIMITIVE_FORWARD(word_xt)
 PRIMITIVE_FORWARD(getenv)
 PRIMITIVE_FORWARD(setenv)
 PRIMITIVE_FORWARD(existsp)
+PRIMITIVE_FORWARD(minor_gc)
 PRIMITIVE_FORWARD(full_gc)
+PRIMITIVE_FORWARD(compact_gc)
 PRIMITIVE_FORWARD(gc_stats)
 PRIMITIVE_FORWARD(save_image)
 PRIMITIVE_FORWARD(save_image_and_exit)
@@ -127,6 +129,7 @@ PRIMITIVE_FORWARD(optimized_p)
 PRIMITIVE_FORWARD(quot_compiled_p)
 PRIMITIVE_FORWARD(vm_ptr)
 PRIMITIVE_FORWARD(strip_stack_traces)
+PRIMITIVE_FORWARD(callback)
 
 const primitive_type primitives[] = {
        primitive_bignum_to_fixnum,
@@ -187,7 +190,9 @@ const primitive_type primitives[] = {
        primitive_getenv,
        primitive_setenv,
        primitive_existsp,
+       primitive_minor_gc,
        primitive_full_gc,
+       primitive_compact_gc,
        primitive_gc_stats,
        primitive_save_image,
        primitive_save_image_and_exit,
@@ -290,6 +295,7 @@ const primitive_type primitives[] = {
        primitive_quot_compiled_p,
        primitive_vm_ptr,
        primitive_strip_stack_traces,
+       primitive_callback,
 };
 
 }
index 6f4da51869b7dd60e8609f65747c3bb412944fa9..60fdce1003210bb6776fc3ee0a7e85f0251c6bea 100755 (executable)
@@ -362,7 +362,7 @@ fixnum factor_vm::quot_code_offset_to_scan(cell quot_, cell offset)
 cell factor_vm::lazy_jit_compile_impl(cell quot_, stack_frame *stack)
 {
        gc_root<quotation> quot(quot_,this);
-       stack_chain->callstack_top = stack;
+       ctx->callstack_top = stack;
        jit_compile(quot.value(),true);
        return quot.value();
 }
index cfc2acfb5c0863d86f81c94171533d8e121bf1fd..9a23979066a8ea6b7c4c050343554480748403ee 100755 (executable)
@@ -59,6 +59,9 @@ enum special_object {
        JIT_EXECUTE_CALL,
        JIT_DECLARE_WORD,
 
+       /* Callback stub generation in callbacks.c */
+       CALLBACK_STUB       = 45,
+
        /* Polymorphic inline cache generation in inline_cache.c */
        PIC_LOAD            = 47,
        PIC_TAG,
index 5031260da4f78b8dfc870546f07e2a4a8654a1af..d21e1e440b1681beb0b73d74084f79825b3e36ea 100755 (executable)
--- a/vm/vm.hpp
+++ b/vm/vm.hpp
@@ -8,7 +8,7 @@ struct factor_vm
        // First five fields accessed directly by assembler. See vm.factor
 
        /* Current stacks */
-       context *stack_chain;
+       context *ctx;
        
        /* New objects are allocated here */
        zone nursery;
@@ -55,6 +55,9 @@ struct factor_vm
        /* Code heap */
        code_heap *code;
 
+       /* Pinned callback stubs */
+       callback_heap *callbacks;
+
        /* Only set if we're performing a GC */
        gc_state *current_gc;
 
@@ -96,7 +99,7 @@ struct factor_vm
        void save_stacks();
        context *alloc_context();
        void dealloc_context(context *old_context);
-       void nest_stacks();
+       void nest_stacks(stack_frame *magic_frame);
        void unnest_stacks();
        void init_stacks(cell ds_size_, cell rs_size_);
        bool stack_to_array(cell bottom, cell top);
@@ -107,6 +110,18 @@ struct factor_vm
        void primitive_set_retainstack();
        void primitive_check_datastack();
 
+       template<typename Iterator> void factor_vm::iterate_active_frames(Iterator &iter)
+       {
+               context *ctx = this->ctx;
+
+               while(ctx)
+               {
+                       iterate_callstack(ctx,iter);
+                       if(ctx->magic_frame) iter(ctx->magic_frame);
+                       ctx = ctx->next;
+               }
+       }
+
        // run
        void primitive_getenv();
        void primitive_setenv();
@@ -238,13 +253,15 @@ struct factor_vm
        void collect_nursery();
        void collect_aging();
        void collect_to_tenured();
+       void big_code_heap_update();
+       void small_code_heap_update();
        void collect_full_impl(bool trace_contexts_p);
        void collect_growing_heap(cell requested_bytes, bool trace_contexts_p, bool compact_code_heap_p);
        void collect_full(bool trace_contexts_p, bool compact_code_heap_p);
        void record_gc_stats(generation_statistics *stats);
        void gc(gc_op op, cell requested_bytes, bool trace_contexts_p, bool compact_code_heap_p);
-       void primitive_full_gc();
        void primitive_minor_gc();
+       void primitive_full_gc();
        void primitive_compact_gc();
        void primitive_gc_stats();
        void clear_gc_stats();
@@ -502,6 +519,7 @@ struct factor_vm
        void primitive_code_room();
        void forward_object_xts();
        void forward_context_xts();
+       void forward_callback_xts();
        void compact_code_heap(bool trace_contexts_p);
        void primitive_strip_stack_traces();
 
@@ -518,6 +536,10 @@ struct factor_vm
                }
        }
 
+       //callbacks
+       void factor_vm::init_callbacks(cell size);
+       void factor_vm::primitive_callback();
+
        //image
        void init_objects(image_header *h);
        void load_data_heap(FILE *file, image_header *h, vm_parameters *p);