]> gitweb.factorcode.org Git - factor.git/blobdiff - vm/image.cpp
xmode.rules: removing test no longer needed
[factor.git] / vm / image.cpp
index 3a58b950b817084116bcb70cd9f520aca958b1b7..571cfa0d27e03e61cd62014454431df60bf6f4d9 100644 (file)
 
 namespace factor {
 
-/* Certain special objects in the image are known to the runtime */
-void factor_vm::init_objects(image_header* h) {
-  memcpy(special_objects, h->special_objects, sizeof(special_objects));
-
-  true_object = h->true_object;
-  bignum_zero = h->bignum_zero;
-  bignum_pos_one = h->bignum_pos_one;
-  bignum_neg_one = h->bignum_neg_one;
+bool factor_arg(const vm_char* str, const vm_char* arg, cell* value) {
+  int val;
+  if (SSCANF(str, arg, &val) > 0) {
+    *value = val;
+    return true;
+  }
+  return false;
+}
+
+vm_parameters::vm_parameters() {
+  embedded_image = false;
+  image_path = NULL;
+  executable_path = NULL;
+
+  datastack_size = 32 * sizeof(cell);
+  retainstack_size = 32 * sizeof(cell);
+
+#if defined(FACTOR_PPC)
+  callstack_size = 256 * sizeof(cell);
+#else
+  callstack_size = 128 * sizeof(cell);
+#endif
+
+  code_size = 96;
+  young_size = sizeof(cell) / 4;
+  aging_size = sizeof(cell) / 2;
+  tenured_size = 24 * sizeof(cell);
+
+  max_pic_size = 3;
+
+  fep = false;
+  signals = true;
+
+#ifdef WINDOWS
+  console = GetConsoleWindow() != NULL;
+#else
+  console = true;
+#endif
+
+  callback_size = 256;
+}
+
+vm_parameters::~vm_parameters() {
+  free((vm_char *)image_path);
+  free((vm_char *)executable_path);
+}
+
+void vm_parameters::init_from_args(int argc, vm_char** argv) {
+  int i = 0;
+
+  for (i = 1; i < argc; i++) {
+    vm_char* arg = argv[i];
+    if (STRCMP(arg, STRING_LITERAL("--")) == 0)
+      break;
+    else if (factor_arg(arg, STRING_LITERAL("-datastack=%d"),
+                        &datastack_size))
+      ;
+    else if (factor_arg(arg, STRING_LITERAL("-retainstack=%d"),
+                        &retainstack_size))
+      ;
+    else if (factor_arg(arg, STRING_LITERAL("-callstack=%d"),
+                        &callstack_size))
+      ;
+    else if (factor_arg(arg, STRING_LITERAL("-young=%d"),
+                        &young_size))
+      ;
+    else if (factor_arg(arg, STRING_LITERAL("-aging=%d"),
+                        &aging_size))
+      ;
+    else if (factor_arg(arg, STRING_LITERAL("-tenured=%d"),
+                        &tenured_size))
+      ;
+    else if (factor_arg(arg, STRING_LITERAL("-codeheap=%d"),
+                        &code_size))
+      ;
+    else if (factor_arg(arg, STRING_LITERAL("-pic=%d"),
+                        &max_pic_size))
+      ;
+    else if (factor_arg(arg, STRING_LITERAL("-callbacks=%d"),
+                        &callback_size))
+      ;
+    else if (STRNCMP(arg, STRING_LITERAL("-i="), 3) == 0) {
+      // In case you specify -i more than once.
+      if (image_path) {
+        free((vm_char *)image_path);
+      }
+      image_path = safe_strdup(arg + 3);
+    }
+    else if (STRCMP(arg, STRING_LITERAL("-fep")) == 0)
+      fep = true;
+    else if (STRCMP(arg, STRING_LITERAL("-no-signals")) == 0)
+      signals = false;
+  }
 }
 
 void factor_vm::load_data_heap(FILE* file, image_header* h, vm_parameters* p) {
   p->tenured_size = std::max((h->data_size * 3) / 2, p->tenured_size);
 
-  init_data_heap(p->young_size, p->aging_size, p->tenured_size);
-
+  data_heap *d = new data_heap(&nursery,
+                               p->young_size, p->aging_size, p->tenured_size);
+  set_data_heap(d);
   fixnum bytes_read =
-      safe_fread((void*)data->tenured->start, 1, h->data_size, file);
+      raw_fread((void*)data->tenured->start, 1, h->data_size, file);
 
   if ((cell)bytes_read != h->data_size) {
     std::cout << "truncated image: " << bytes_read << " bytes read, ";
@@ -33,11 +119,11 @@ void factor_vm::load_code_heap(FILE* file, image_header* h, vm_parameters* p) {
   if (h->code_size > p->code_size)
     fatal_error("Code heap too small to fit image", h->code_size);
 
-  init_code_heap(p->code_size);
+  code = new code_heap(p->code_size);
 
   if (h->code_size != 0) {
     size_t bytes_read =
-        safe_fread(code->allocator->first_block(), 1, h->code_size, file);
+        raw_fread((void*)code->allocator->start, 1, h->code_size, file);
     if (bytes_read != h->code_size) {
       std::cout << "truncated image: " << bytes_read << " bytes read, ";
       std::cout << h->code_size << " bytes expected\n";
@@ -66,130 +152,59 @@ struct startup_fixup {
     return (code_block*)((cell)obj + code_offset);
   }
 
-  object* translate_data(const object* obj) { return fixup_data((object*)obj); }
+  object* translate_data(const object* obj) {
+    return fixup_data((object*)obj);
+  }
 
   code_block* translate_code(const code_block* compiled) {
     return fixup_code((code_block*)compiled);
   }
 
-  cell size(const object* obj) { return obj->size(*this); }
+  cell size(const object* obj) {
+    return obj->size(*this);
+  }
 
-  cell size(code_block* compiled) { return compiled->size(*this); }
+  cell size(code_block* compiled) {
+    return compiled->size(*this);
+  }
 };
 
-struct start_object_updater {
-  factor_vm* parent;
-  startup_fixup fixup;
-  slot_visitor<startup_fixup> data_visitor;
-  code_block_visitor<startup_fixup> code_visitor;
-
-  start_object_updater(factor_vm* parent, startup_fixup fixup)
-      : parent(parent),
-        fixup(fixup),
-        data_visitor(slot_visitor<startup_fixup>(parent, fixup)),
-        code_visitor(code_block_visitor<startup_fixup>(parent, fixup)) {}
-
-  void operator()(object* obj, cell size) {
-    parent->data->tenured->starts.record_object_start_offset(obj);
-
-    data_visitor.visit_slots(obj);
+void factor_vm::fixup_heaps(cell data_offset, cell code_offset) {
+  startup_fixup fixup(data_offset, code_offset);
+  slot_visitor<startup_fixup> visitor(this, fixup);
+  visitor.visit_all_roots();
 
+  auto start_object_updater = [&](object *obj, cell size) {
+    (void)size;
+    data->tenured->starts.record_object_start_offset(obj);
+    visitor.visit_slots(obj);
     switch (obj->type()) {
       case ALIEN_TYPE: {
-
         alien* ptr = (alien*)obj;
-
         if (to_boolean(ptr->base))
           ptr->update_address();
         else
-          ptr->expired = parent->true_object;
+          ptr->expired = special_objects[OBJ_CANONICAL_TRUE];
         break;
       }
       case DLL_TYPE: {
-        parent->ffi_dlopen((dll*)obj);
+        ffi_dlopen((dll*)obj);
         break;
       }
       default: {
-        code_visitor.visit_object_code_block(obj);
+        visitor.visit_object_code_block(obj);
         break;
       }
     }
-  }
-};
-
-void factor_vm::fixup_data(cell data_offset, cell code_offset) {
-  startup_fixup fixup(data_offset, code_offset);
-  slot_visitor<startup_fixup> data_workhorse(this, fixup);
-  data_workhorse.visit_roots();
-
-  start_object_updater updater(this, fixup);
-  data->tenured->iterate(updater, fixup);
-}
-
-struct startup_code_block_relocation_visitor {
-  factor_vm* parent;
-  startup_fixup fixup;
-  slot_visitor<startup_fixup> data_visitor;
-
-  startup_code_block_relocation_visitor(factor_vm* parent,
-                                        startup_fixup fixup)
-      : parent(parent),
-        fixup(fixup),
-        data_visitor(slot_visitor<startup_fixup>(parent, fixup)) {}
-
-  void operator()(instruction_operand op) {
-    code_block* compiled = op.compiled;
-    cell old_offset =
-        op.rel_offset() + (cell)compiled->entry_point() - fixup.code_offset;
-
-    switch (op.rel_type()) {
-      case RT_LITERAL: {
-        cell value = op.load_value(old_offset);
-        if (immediate_p(value))
-          op.store_value(value);
-        else
-          op.store_value(
-              RETAG(fixup.fixup_data(untag<object>(value)), TAG(value)));
-        break;
-      }
-      case RT_ENTRY_POINT:
-      case RT_ENTRY_POINT_PIC:
-      case RT_ENTRY_POINT_PIC_TAIL:
-      case RT_HERE: {
-        cell value = op.load_value(old_offset);
-        cell offset = TAG(value);
-        code_block* compiled = (code_block*)UNTAG(value);
-        op.store_value((cell)fixup.fixup_code(compiled) + offset);
-        break;
-      }
-      case RT_UNTAGGED:
-        break;
-      default:
-        parent->store_external_address(op);
-        break;
-    }
-  }
-};
-
-struct startup_code_block_updater {
-  factor_vm* parent;
-  startup_fixup fixup;
-
-  startup_code_block_updater(factor_vm* parent, startup_fixup fixup)
-      : parent(parent), fixup(fixup) {}
-
-  void operator()(code_block* compiled, cell size) {
-    slot_visitor<startup_fixup> data_visitor(parent, fixup);
-    data_visitor.visit_code_block_objects(compiled);
-
-    startup_code_block_relocation_visitor code_visitor(parent, fixup);
-    compiled->each_instruction_operand(code_visitor);
-  }
-};
-
-void factor_vm::fixup_code(cell data_offset, cell code_offset) {
-  startup_fixup fixup(data_offset, code_offset);
-  startup_code_block_updater updater(this, fixup);
+  };
+  data->tenured->iterate(start_object_updater, fixup);
+
+  auto updater = [&](code_block* compiled, cell size) {
+    (void)size;
+    visitor.visit_code_block_objects(compiled);
+    cell rel_base = compiled->entry_point() - fixup.code_offset;
+    visitor.visit_instruction_operands(compiled, rel_base);
+  };
   code->allocator->iterate(updater, fixup);
 }
 
@@ -202,48 +217,36 @@ bool factor_vm::read_embedded_image_footer(FILE* file,
 
 char *threadsafe_strerror(int errnum) {
   char *buf = (char *) malloc(STRERROR_BUFFER_SIZE);
-  if(!buf) {
+  if (!buf) {
     fatal_error("Out of memory in threadsafe_strerror, errno", errnum);
   }
   THREADSAFE_STRERROR(errnum, buf, STRERROR_BUFFER_SIZE);
   return buf;
 }
 
-FILE* factor_vm::open_image(vm_parameters* p) {
+// Read an image file from disk, only done once during startup
+// This function also initializes the data and code heaps
+void factor_vm::load_image(vm_parameters* p) {
+
+  FILE* file = OPEN_READ(p->image_path);
+  if (file == NULL) {
+    std::cout << "Cannot open image file: " << AS_UTF8(p->image_path) << std::endl;
+    char *msg = threadsafe_strerror(errno);
+    std::cout << "strerror: " << msg << std::endl;
+    free(msg);
+    exit(1);
+  }
   if (p->embedded_image) {
-    FILE* file = OPEN_READ(p->executable_path);
-    if (file == NULL) {
-      std::cout << "Cannot open embedded image" << std::endl;
-      char *msg = threadsafe_strerror(errno);
-      std::cout << "strerror: " << msg << std::endl;
-      free(msg);
-      exit(1);
-    }
     embedded_image_footer footer;
     if (!read_embedded_image_footer(file, &footer)) {
       std::cout << "No embedded image" << std::endl;
       exit(1);
     }
     safe_fseek(file, (off_t)footer.image_offset, SEEK_SET);
-    return file;
-  } else
-    return OPEN_READ(p->image_path);
-}
-
-/* Read an image file from disk, only done once during startup */
-/* This function also initializes the data and code heaps */
-void factor_vm::load_image(vm_parameters* p) {
-  FILE* file = open_image(p);
-  if (file == NULL) {
-    std::cout << "Cannot open image file: " << p->image_path << std::endl;
-    char *msg = threadsafe_strerror(errno);
-    std::cout << "strerror: " << msg << std::endl;
-    free(msg);
-    exit(1);
   }
 
   image_header h;
-  if (safe_fread(&h, sizeof(image_header), 1, file) != 1)
+  if (raw_fread(&h, sizeof(image_header), 1, file) != 1)
     fatal_error("Cannot read image header", 0);
 
   if (h.magic != image_magic)
@@ -255,34 +258,22 @@ void factor_vm::load_image(vm_parameters* p) {
   load_data_heap(file, &h, p);
   load_code_heap(file, &h, p);
 
-  safe_fclose(file);
+  raw_fclose(file);
 
-  init_objects(&h);
+  // Certain special objects in the image are known to the runtime
+  memcpy(special_objects, h.special_objects, sizeof(special_objects));
 
   cell data_offset = data->tenured->start - h.data_relocation_base;
   cell code_offset = code->allocator->start - h.code_relocation_base;
-
-  fixup_data(data_offset, code_offset);
-  fixup_code(data_offset, code_offset);
-
-  /* Store image path name */
-  special_objects[OBJ_IMAGE] = allot_alien(false_object, (cell)p->image_path);
+  fixup_heaps(data_offset, code_offset);
 }
 
-/* Save the current image to disk */
+// Save the current image to disk. We don't throw any exceptions here
+// because if the 'then-die' argument is t it is not safe to do
+// so. Instead we signal failure by returning false.
 bool factor_vm::save_image(const vm_char* saving_filename,
                            const vm_char* filename) {
-  FILE* file;
-  image_header h;
-
-  file = OPEN_WRITE(saving_filename);
-  if (file == NULL) {
-    std::cout << "Cannot open image file for writing: " << saving_filename << std::endl;
-    char *msg = threadsafe_strerror(errno);
-    std::cout << "strerror: " << msg << std::endl;
-    free(msg);
-    return false;
-  }
+  image_header h = {};
 
   h.magic = image_magic;
   h.version = image_version;
@@ -291,84 +282,84 @@ bool factor_vm::save_image(const vm_char* saving_filename,
   h.code_relocation_base = code->allocator->start;
   h.code_size = code->allocator->occupied_space();
 
-  h.true_object = true_object;
-  h.bignum_zero = bignum_zero;
-  h.bignum_pos_one = bignum_pos_one;
-  h.bignum_neg_one = bignum_neg_one;
-
   for (cell i = 0; i < special_object_count; i++)
     h.special_objects[i] =
         (save_special_p(i) ? special_objects[i] : false_object);
 
-  bool ok = true;
-
+  FILE* file = OPEN_WRITE(saving_filename);
+  if (file == NULL)
+    return false;
   if (safe_fwrite(&h, sizeof(image_header), 1, file) != 1)
-    ok = false;
-  if (safe_fwrite((void*)data->tenured->start, h.data_size, 1, file) != 1)
-    ok = false;
-  if (safe_fwrite(code->allocator->first_block(), h.code_size, 1, file) != 1)
-    ok = false;
-  safe_fclose(file);
-
-  if (!ok) {
-    std::cout << "save-image failed." << std::endl;
-    char *msg = threadsafe_strerror(errno);
-    std::cout << "strerror: " << msg << std::endl;
-    free(msg);
-  }
-  else
-    move_file(saving_filename, filename);
-
-  return ok;
+    return false;
+  if (h.data_size > 0 &&
+      safe_fwrite((void*)data->tenured->start, h.data_size, 1, file) != 1)
+    return false;
+  if (h.code_size > 0 &&
+      safe_fwrite((void*)code->allocator->start, h.code_size, 1, file) != 1)
+    return false;
+  if (raw_fclose(file) == -1)
+    return false;
+  if (!move_file(saving_filename, filename))
+    return false;
+  return true;
 }
 
+// Allocates memory
 void factor_vm::primitive_save_image() {
-  /* do a full GC to push everything into tenured space */
-  primitive_compact_gc();
-
-  data_root<byte_array> path2(ctx->pop(), this);
-  path2.untag_check(this);
-  data_root<byte_array> path1(ctx->pop(), this);
-  path1.untag_check(this);
-  save_image((vm_char*)(path1.untagged() + 1),
-             (vm_char*)(path2.untagged() + 1));
-}
-
-void factor_vm::primitive_save_image_and_exit() {
-  /* We unbox this before doing anything else. This is the only point
-     where we might throw an error, so we have to throw an error here since
-     later steps destroy the current image. */
-  data_root<byte_array> path2(ctx->pop(), this);
-  path2.untag_check(this);
-  data_root<byte_array> path1(ctx->pop(), this);
-  path1.untag_check(this);
+  // We unbox this before doing anything else. This is the only point
+  // where we might throw an error, so we have to throw an error here since
+  // later steps destroy the current image.
+  bool then_die = to_boolean(ctx->pop());
+  byte_array* path2 = untag_check<byte_array>(ctx->pop());
+  byte_array* path1 = untag_check<byte_array>(ctx->pop());
+
+  // Copy the paths to non-gc memory to avoid them hanging around in
+  // the saved image.
+  vm_char* path1_saved = safe_strdup(path1->data<vm_char>());
+  vm_char* path2_saved = safe_strdup(path2->data<vm_char>());
+
+  if (then_die) {
+    // strip out special_objects data which is set on startup anyway
+    for (cell i = 0; i < special_object_count; i++)
+      if (!save_special_p(i))
+        special_objects[i] = false_object;
+
+    // dont trace objects only reachable from context stacks so we don't
+    // get volatile data saved in the image.
+    active_contexts.clear();
+    code->uninitialized_blocks.clear();
+
+    // I think clearing the callback heap should be fine too.
+    callbacks->allocator->initial_free_list(0);
+  }
 
-  /* strip out special_objects data which is set on startup anyway */
-  for (cell i = 0; i < special_object_count; i++)
-    if (!save_special_p(i))
-      special_objects[i] = false_object;
+  // do a full GC to push everything remaining into tenured space
+  primitive_compact_gc();
 
-  gc(collect_compact_op, 0, /* requested size */
-     false /* discard objects only reachable from stacks */);
+  // Save the image
+  bool ret = save_image(path1_saved, path2_saved);
+  if (then_die) {
+    exit(ret ? 0 : 1);
+  }
+  free(path1_saved);
+  free(path2_saved);
 
-  /* Save the image */
-  if (save_image((vm_char*)(path1.untagged() + 1),
-                 (vm_char*)(path2.untagged() + 1)))
-    exit(0);
-  else
-    exit(1);
+  if (!ret) {
+    general_error(ERROR_IO, tag_fixnum(errno), false_object);
+  }
 }
 
 bool factor_vm::embedded_image_p() {
   const vm_char* vm_path = vm_executable_path();
-  if (!vm_path)
-    return false;
   FILE* file = OPEN_READ(vm_path);
-  if (!file)
+  if (!file) {
+    free((vm_char *)vm_path);
     return false;
+  }
   embedded_image_footer footer;
   bool embedded_p = read_embedded_image_footer(file, &footer);
   fclose(file);
+  free((vm_char *)vm_path);
   return embedded_p;
 }