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