]> gitweb.factorcode.org Git - factor.git/blob - vm/image.cpp
VM: merge of slot_visitor and code_block_visitor
[factor.git] / vm / image.cpp
1 #include "master.hpp"
2
3 namespace factor {
4
5 /* Certain special objects in the image are known to the runtime */
6 void factor_vm::init_objects(image_header* h) {
7   memcpy(special_objects, h->special_objects, sizeof(special_objects));
8
9   true_object = h->true_object;
10   bignum_zero = h->bignum_zero;
11   bignum_pos_one = h->bignum_pos_one;
12   bignum_neg_one = h->bignum_neg_one;
13 }
14
15 void factor_vm::load_data_heap(FILE* file, image_header* h, vm_parameters* p) {
16   p->tenured_size = std::max((h->data_size * 3) / 2, p->tenured_size);
17
18   init_data_heap(p->young_size, p->aging_size, p->tenured_size);
19
20   fixnum bytes_read =
21       safe_fread((void*)data->tenured->start, 1, h->data_size, file);
22
23   if ((cell)bytes_read != h->data_size) {
24     std::cout << "truncated image: " << bytes_read << " bytes read, ";
25     std::cout << h->data_size << " bytes expected\n";
26     fatal_error("load_data_heap failed", 0);
27   }
28
29   data->tenured->initial_free_list(h->data_size);
30 }
31
32 void factor_vm::load_code_heap(FILE* file, image_header* h, vm_parameters* p) {
33   if (h->code_size > p->code_size)
34     fatal_error("Code heap too small to fit image", h->code_size);
35
36   init_code_heap(p->code_size);
37
38   if (h->code_size != 0) {
39     size_t bytes_read =
40         safe_fread((void*)code->allocator->start, 1, h->code_size, file);
41     if (bytes_read != h->code_size) {
42       std::cout << "truncated image: " << bytes_read << " bytes read, ";
43       std::cout << h->code_size << " bytes expected\n";
44       fatal_error("load_code_heap failed", 0);
45     }
46   }
47
48   code->allocator->initial_free_list(h->code_size);
49   code->initialize_all_blocks_set();
50 }
51
52 struct startup_fixup {
53   static const bool translated_code_block_map = true;
54
55   cell data_offset;
56   cell code_offset;
57
58   startup_fixup(cell data_offset, cell code_offset)
59       : data_offset(data_offset), code_offset(code_offset) {}
60
61   object* fixup_data(object* obj) {
62     return (object*)((cell)obj + data_offset);
63   }
64
65   code_block* fixup_code(code_block* obj) {
66     return (code_block*)((cell)obj + code_offset);
67   }
68
69   object* translate_data(const object* obj) { return fixup_data((object*)obj); }
70
71   code_block* translate_code(const code_block* compiled) {
72     return fixup_code((code_block*)compiled);
73   }
74
75   cell size(const object* obj) { return obj->size(*this); }
76
77   cell size(code_block* compiled) { return compiled->size(*this); }
78 };
79
80 struct start_object_updater {
81   factor_vm* parent;
82   startup_fixup fixup;
83   slot_visitor<startup_fixup> visitor;
84
85   start_object_updater(factor_vm* parent, startup_fixup fixup)
86       : parent(parent),
87         fixup(fixup),
88         visitor(slot_visitor<startup_fixup>(parent, fixup)) { }
89
90   void operator()(object* obj, cell size) {
91     parent->data->tenured->starts.record_object_start_offset(obj);
92
93     visitor.visit_slots(obj);
94
95     switch (obj->type()) {
96       case ALIEN_TYPE: {
97
98         alien* ptr = (alien*)obj;
99
100         if (to_boolean(ptr->base))
101           ptr->update_address();
102         else
103           ptr->expired = parent->true_object;
104         break;
105       }
106       case DLL_TYPE: {
107         parent->ffi_dlopen((dll*)obj);
108         break;
109       }
110       default: {
111         visitor.visit_object_code_block(obj);
112         break;
113       }
114     }
115   }
116 };
117
118 void factor_vm::fixup_data(cell data_offset, cell code_offset) {
119   startup_fixup fixup(data_offset, code_offset);
120   slot_visitor<startup_fixup> data_workhorse(this, fixup);
121   data_workhorse.visit_roots();
122
123   start_object_updater updater(this, fixup);
124   data->tenured->iterate(updater, fixup);
125 }
126
127 struct startup_code_block_relocation_visitor {
128   factor_vm* parent;
129   startup_fixup fixup;
130   slot_visitor<startup_fixup> data_visitor;
131
132   startup_code_block_relocation_visitor(factor_vm* parent,
133                                         startup_fixup fixup)
134       : parent(parent),
135         fixup(fixup),
136         data_visitor(slot_visitor<startup_fixup>(parent, fixup)) {}
137
138   void operator()(instruction_operand op) {
139     code_block* compiled = op.compiled;
140     cell old_offset =
141         op.rel_offset() + compiled->entry_point() - fixup.code_offset;
142
143     switch (op.rel_type()) {
144       case RT_LITERAL: {
145         cell value = op.load_value(old_offset);
146         if (immediate_p(value))
147           op.store_value(value);
148         else
149           op.store_value(
150               RETAG(fixup.fixup_data(untag<object>(value)), TAG(value)));
151         break;
152       }
153       case RT_ENTRY_POINT:
154       case RT_ENTRY_POINT_PIC:
155       case RT_ENTRY_POINT_PIC_TAIL:
156       case RT_HERE: {
157         cell value = op.load_value(old_offset);
158         cell offset = TAG(value);
159         code_block* compiled = (code_block*)UNTAG(value);
160         op.store_value((cell)fixup.fixup_code(compiled) + offset);
161         break;
162       }
163       case RT_UNTAGGED:
164         break;
165       default:
166         parent->store_external_address(op);
167         break;
168     }
169   }
170 };
171
172 struct startup_code_block_updater {
173   factor_vm* parent;
174   startup_fixup fixup;
175
176   startup_code_block_updater(factor_vm* parent, startup_fixup fixup)
177       : parent(parent), fixup(fixup) {}
178
179   void operator()(code_block* compiled, cell size) {
180     slot_visitor<startup_fixup> data_visitor(parent, fixup);
181     data_visitor.visit_code_block_objects(compiled);
182
183     startup_code_block_relocation_visitor code_visitor(parent, fixup);
184     compiled->each_instruction_operand(code_visitor);
185   }
186 };
187
188 void factor_vm::fixup_code(cell data_offset, cell code_offset) {
189   startup_fixup fixup(data_offset, code_offset);
190   startup_code_block_updater updater(this, fixup);
191   code->allocator->iterate(updater, fixup);
192 }
193
194 bool factor_vm::read_embedded_image_footer(FILE* file,
195                                            embedded_image_footer* footer) {
196   safe_fseek(file, -(off_t)sizeof(embedded_image_footer), SEEK_END);
197   safe_fread(footer, (off_t)sizeof(embedded_image_footer), 1, file);
198   return footer->magic == image_magic;
199 }
200
201 char *threadsafe_strerror(int errnum) {
202   char *buf = (char *) malloc(STRERROR_BUFFER_SIZE);
203   if(!buf) {
204     fatal_error("Out of memory in threadsafe_strerror, errno", errnum);
205   }
206   THREADSAFE_STRERROR(errnum, buf, STRERROR_BUFFER_SIZE);
207   return buf;
208 }
209
210 FILE* factor_vm::open_image(vm_parameters* p) {
211   if (p->embedded_image) {
212     FILE* file = OPEN_READ(p->executable_path);
213     if (file == NULL) {
214       std::cout << "Cannot open embedded image" << std::endl;
215       char *msg = threadsafe_strerror(errno);
216       std::cout << "strerror: " << msg << std::endl;
217       free(msg);
218       exit(1);
219     }
220     embedded_image_footer footer;
221     if (!read_embedded_image_footer(file, &footer)) {
222       std::cout << "No embedded image" << std::endl;
223       exit(1);
224     }
225     safe_fseek(file, (off_t)footer.image_offset, SEEK_SET);
226     return file;
227   } else
228     return OPEN_READ(p->image_path);
229 }
230
231 /* Read an image file from disk, only done once during startup */
232 /* This function also initializes the data and code heaps */
233 void factor_vm::load_image(vm_parameters* p) {
234   FILE* file = open_image(p);
235   if (file == NULL) {
236     std::cout << "Cannot open image file: " << p->image_path << std::endl;
237     char *msg = threadsafe_strerror(errno);
238     std::cout << "strerror: " << msg << std::endl;
239     free(msg);
240     exit(1);
241   }
242
243   image_header h;
244   if (safe_fread(&h, sizeof(image_header), 1, file) != 1)
245     fatal_error("Cannot read image header", 0);
246
247   if (h.magic != image_magic)
248     fatal_error("Bad image: magic number check failed", h.magic);
249
250   if (h.version != image_version)
251     fatal_error("Bad image: version number check failed", h.version);
252
253   load_data_heap(file, &h, p);
254   load_code_heap(file, &h, p);
255
256   safe_fclose(file);
257
258   init_objects(&h);
259
260   cell data_offset = data->tenured->start - h.data_relocation_base;
261   cell code_offset = code->allocator->start - h.code_relocation_base;
262
263   fixup_data(data_offset, code_offset);
264   fixup_code(data_offset, code_offset);
265
266   /* Store image path name */
267   special_objects[OBJ_IMAGE] = allot_alien(false_object, (cell)p->image_path);
268 }
269
270 /* Save the current image to disk */
271 bool factor_vm::save_image(const vm_char* saving_filename,
272                            const vm_char* filename) {
273   FILE* file;
274   image_header h;
275
276   file = OPEN_WRITE(saving_filename);
277   if (file == NULL) {
278     std::cout << "Cannot open image file for writing: " << saving_filename << std::endl;
279     char *msg = threadsafe_strerror(errno);
280     std::cout << "strerror: " << msg << std::endl;
281     free(msg);
282     return false;
283   }
284
285   h.magic = image_magic;
286   h.version = image_version;
287   h.data_relocation_base = data->tenured->start;
288   h.data_size = data->tenured->occupied_space();
289   h.code_relocation_base = code->allocator->start;
290   h.code_size = code->allocator->occupied_space();
291
292   h.true_object = true_object;
293   h.bignum_zero = bignum_zero;
294   h.bignum_pos_one = bignum_pos_one;
295   h.bignum_neg_one = bignum_neg_one;
296
297   for (cell i = 0; i < special_object_count; i++)
298     h.special_objects[i] =
299         (save_special_p(i) ? special_objects[i] : false_object);
300
301   bool ok = true;
302
303   if (safe_fwrite(&h, sizeof(image_header), 1, file) != 1)
304     ok = false;
305   if (safe_fwrite((void*)data->tenured->start, h.data_size, 1, file) != 1)
306     ok = false;
307   if (safe_fwrite((void*)code->allocator->start, h.code_size, 1, file) != 1)
308     ok = false;
309   safe_fclose(file);
310
311   if (!ok) {
312     std::cout << "save-image failed." << std::endl;
313     char *msg = threadsafe_strerror(errno);
314     std::cout << "strerror: " << msg << std::endl;
315     free(msg);
316   }
317   else
318     move_file(saving_filename, filename);
319
320   return ok;
321 }
322
323 void factor_vm::primitive_save_image() {
324   /* do a full GC to push everything into tenured space */
325   primitive_compact_gc();
326
327   data_root<byte_array> path2(ctx->pop(), this);
328   path2.untag_check(this);
329   data_root<byte_array> path1(ctx->pop(), this);
330   path1.untag_check(this);
331   save_image((vm_char*)(path1.untagged() + 1),
332              (vm_char*)(path2.untagged() + 1));
333 }
334
335 /* Allocates memory */
336 void factor_vm::primitive_save_image_and_exit() {
337   /* We unbox this before doing anything else. This is the only point
338      where we might throw an error, so we have to throw an error here since
339      later steps destroy the current image. */
340   data_root<byte_array> path2(ctx->pop(), this);
341   path2.untag_check(this);
342   data_root<byte_array> path1(ctx->pop(), this);
343   path1.untag_check(this);
344
345   /* strip out special_objects data which is set on startup anyway */
346   for (cell i = 0; i < special_object_count; i++)
347     if (!save_special_p(i))
348       special_objects[i] = false_object;
349
350   gc(collect_compact_op, 0, /* requested size */
351      false /* discard objects only reachable from stacks */);
352
353   /* Save the image */
354   if (save_image((vm_char*)(path1.untagged() + 1),
355                  (vm_char*)(path2.untagged() + 1)))
356     exit(0);
357   else
358     exit(1);
359 }
360
361 bool factor_vm::embedded_image_p() {
362   const vm_char* vm_path = vm_executable_path();
363   if (!vm_path)
364     return false;
365   FILE* file = OPEN_READ(vm_path);
366   if (!file)
367     return false;
368   embedded_image_footer footer;
369   bool embedded_p = read_embedded_image_footer(file, &footer);
370   fclose(file);
371   return embedded_p;
372 }
373
374 }