#include "master.hpp"
-namespace factor
-{
-
-callback_heap::callback_heap(cell size, factor_vm *parent_) :
- seg(new segment(size,true)),
- here(seg->start),
- parent(parent_) {}
-
-callback_heap::~callback_heap()
-{
- delete seg;
- seg = NULL;
+namespace factor {
+
+bool return_takes_param_p() {
+#if defined(FACTOR_X86) || defined(FACTOR_AMD64)
+ return true;
+#else
+ return false;
+#endif
}
-void factor_vm::init_callbacks(cell size)
-{
- callbacks = new callback_heap(size,this);
-}
-
-void callback_heap::update(code_block *stub)
-{
- tagged<array> code_template(parent->special_objects[CALLBACK_STUB]);
-
- cell rel_class = untag_fixnum(array_nth(code_template.untagged(),1));
- cell rel_type = untag_fixnum(array_nth(code_template.untagged(),2));
- cell offset = untag_fixnum(array_nth(code_template.untagged(),3));
+callback_heap::callback_heap(cell size, factor_vm* parent) {
+ seg = new segment(size, true);
+ if (!seg)
+ fatal_error("Out of memory in callback_heap constructor", size);
+ allocator = new free_list_allocator<code_block>(size, seg->start);
+ this->parent = parent;
- relocation_entry rel(
- (relocation_type)rel_type,
- (relocation_class)rel_class,
- offset);
-
- instruction_operand op(rel,stub,0);
- op.store_value((cell)callback_xt(stub));
-
- stub->flush_icache();
}
-code_block *callback_heap::add(cell owner)
-{
- tagged<array> code_template(parent->special_objects[CALLBACK_STUB]);
- tagged<byte_array> insns(array_nth(code_template.untagged(),0));
- cell size = array_capacity(insns.untagged());
-
- cell bump = align(size + sizeof(code_block),data_alignment);
- if(here + bump > seg->end) fatal_error("Out of callback space",0);
+callback_heap::~callback_heap() {
+ delete allocator;
+ allocator = NULL;
+ delete seg;
+ seg = NULL;
+}
- free_heap_block *free_block = (free_heap_block *)here;
- free_block->make_free(bump);
- here += bump;
+instruction_operand callback_heap::callback_operand(code_block* stub,
+ cell index) {
+ tagged<array> code_template(parent->special_objects[CALLBACK_STUB]);
+ tagged<byte_array> relocation_template(
+ array_nth(code_template.untagged(), 0));
- code_block *stub = (code_block *)free_block;
- stub->owner = owner;
- stub->parameters = false_object;
- stub->relocation = false_object;
+ relocation_entry entry(relocation_template->data<relocation_entry>()[index]);
+ return instruction_operand(entry, stub, 0);
+}
- memcpy(stub->xt(),insns->data<void>(),size);
- update(stub);
+void callback_heap::store_callback_operand(code_block* stub, cell index,
+ cell value) {
+ instruction_operand op = callback_operand(stub, index);
+ op.store_value(value);
+}
- return stub;
+void callback_heap::update(code_block* stub) {
+ word* w = untag<word>(stub->owner);
+ store_callback_operand(stub, 1, w->entry_point);
+ stub->flush_icache();
}
-struct callback_updater {
- callback_heap *callbacks;
+code_block* callback_heap::add(cell owner, cell return_rewind) {
+ // code_template is a 2-tuple where the first element contains the
+ // relocations and the second a byte array of compiled assembly
+ // code. The code assumes that there are four relocations on x86 and
+ // three on ppc.
+ tagged<array> code_template(parent->special_objects[CALLBACK_STUB]);
+ tagged<byte_array> insns(array_nth(code_template.untagged(), 1));
+ cell size = array_capacity(insns.untagged());
+
+ cell bump = align(size + sizeof(code_block), data_alignment);
+ code_block* stub = allocator->allot(bump);
+ if (!stub) {
+ parent->general_error(ERROR_CALLBACK_SPACE_OVERFLOW,
+ false_object,
+ false_object);
+ }
+ stub->header = bump & ~7;
+ stub->owner = owner;
+ stub->parameters = false_object;
+ stub->relocation = false_object;
+
+ memcpy((void*)stub->entry_point(), insns->data<void>(), size);
+
+ // Store VM pointer in two relocations.
+ store_callback_operand(stub, 0, (cell)parent);
+ store_callback_operand(stub, 2, (cell)parent);
+
+ // On x86, the RET instruction takes an argument which depends on
+ // the callback's calling convention
+ if (return_takes_param_p())
+ store_callback_operand(stub, 3, return_rewind);
+
+ update(stub);
+ return stub;
+}
- explicit callback_updater(callback_heap *callbacks_) : callbacks(callbacks_) {}
+// Allocates memory (add(), allot_alien())
+void factor_vm::primitive_callback() {
+ cell return_rewind = to_cell(ctx->pop());
+ tagged<word> w(ctx->pop());
+ check_tagged(w);
- void operator()(code_block *stub)
- {
- callbacks->update(stub);
- }
-};
+ cell func = callbacks->add(w.value(), return_rewind)->entry_point();
+ CODE_TO_FUNCTION_POINTER_CALLBACK(this, func);
+ ctx->push(allot_alien(func));
+}
-void callback_heap::update()
-{
- callback_updater updater(this);
- each_callback(updater);
+void factor_vm::primitive_free_callback() {
+ void* entry_point = alien_offset(ctx->pop());
+ code_block* stub = (code_block*)entry_point - 1;
+ callbacks->allocator->free(stub);
}
-void factor_vm::primitive_callback()
-{
- tagged<word> w(ctx->pop());
- w.untag_check(this);
- ctx->push(allot_alien(callbacks->add(w.value())->xt()));
+// Allocates memory
+void factor_vm::primitive_callback_room() {
+ allocator_room room = callbacks->allocator->as_allocator_room();
+ ctx->push(tag<byte_array>(byte_array_from_value(&room)));
}
}