]> gitweb.factorcode.org Git - factor.git/blob - vm/image.cpp
Merge remote-tracking branch 'malu/semantic-versioning'
[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 = TAG(value);
184                                 code_block *compiled = (code_block *)UNTAG(value);
185                                 op.store_value((cell)fixup.fixup_code(compiled) + offset);
186                                 break;
187                         }
188                 case RT_UNTAGGED:
189                         break;
190                 default:
191                         parent->store_external_address(op);
192                         break;
193                 }
194         }
195 };
196
197 struct startup_code_block_updater {
198         factor_vm *parent;
199         startup_fixup fixup;
200
201         startup_code_block_updater(factor_vm *parent_, startup_fixup fixup_) :
202                 parent(parent_), fixup(fixup_) {}
203
204         void operator()(code_block *compiled, cell size)
205         {
206                 slot_visitor<startup_fixup> data_visitor(parent,fixup);
207                 data_visitor.visit_code_block_objects(compiled);
208
209                 startup_code_block_relocation_visitor code_visitor(parent,fixup);
210                 compiled->each_instruction_operand(code_visitor);
211         }
212 };
213
214 void factor_vm::fixup_code(cell data_offset, cell code_offset)
215 {
216         startup_fixup fixup(data_offset,code_offset);
217         startup_code_block_updater updater(this,fixup);
218         code->allocator->iterate(updater,fixup);
219 }
220
221 /* Read an image file from disk, only done once during startup */
222 /* This function also initializes the data and code heaps */
223 void factor_vm::load_image(vm_parameters *p)
224 {
225         FILE *file = OPEN_READ(p->image_path);
226         if(file == NULL)
227         {
228                 std::cout << "Cannot open image file: " << p->image_path << std::endl;
229                 std::cout << strerror(errno) << std::endl;
230                 exit(1);
231         }
232
233         image_header h;
234         if(safe_fread(&h,sizeof(image_header),1,file) != 1)
235                 fatal_error("Cannot read image header",0);
236
237         if(h.magic != image_magic)
238                 fatal_error("Bad image: magic number check failed",h.magic);
239
240         if(h.version != image_version)
241                 fatal_error("Bad image: version number check failed",h.version);
242         
243         load_data_heap(file,&h,p);
244         load_code_heap(file,&h,p);
245
246         safe_fclose(file);
247
248         init_objects(&h);
249
250         cell data_offset = data->tenured->start - h.data_relocation_base;
251         cell code_offset = code->allocator->start - h.code_relocation_base;
252
253         fixup_data(data_offset,code_offset);
254         fixup_code(data_offset,code_offset);
255
256         /* Store image path name */
257         special_objects[OBJ_IMAGE] = allot_alien(false_object,(cell)p->image_path);
258 }
259
260 /* Save the current image to disk */
261 bool factor_vm::save_image(const vm_char *saving_filename, const vm_char *filename)
262 {
263         FILE* file;
264         image_header h;
265
266         file = OPEN_WRITE(saving_filename);
267         if(file == NULL)
268         {
269                 std::cout << "Cannot open image file: " << saving_filename << std::endl;
270                 std::cout << strerror(errno) << std::endl;
271                 return false;
272         }
273
274         h.magic = image_magic;
275         h.version = image_version;
276         h.data_relocation_base = data->tenured->start;
277         h.data_size = data->tenured->occupied_space();
278         h.code_relocation_base = code->allocator->start;
279         h.code_size = code->allocator->occupied_space();
280
281         h.true_object = true_object;
282         h.bignum_zero = bignum_zero;
283         h.bignum_pos_one = bignum_pos_one;
284         h.bignum_neg_one = bignum_neg_one;
285
286         for(cell i = 0; i < special_object_count; i++)
287                 h.special_objects[i] = (save_special_p(i) ? special_objects[i] : false_object);
288
289         bool ok = true;
290
291         if(safe_fwrite(&h,sizeof(image_header),1,file) != 1) ok = false;
292         if(safe_fwrite((void*)data->tenured->start,h.data_size,1,file) != 1) ok = false;
293         if(safe_fwrite(code->allocator->first_block(),h.code_size,1,file) != 1) ok = false;
294         safe_fclose(file);
295
296         if(!ok)
297                 std::cout << "save-image failed: " << strerror(errno) << std::endl;
298         else
299                 move_file(saving_filename,filename); 
300
301         return ok;
302 }
303
304 void factor_vm::primitive_save_image()
305 {
306         /* do a full GC to push everything into tenured space */
307         primitive_compact_gc();
308
309         data_root<byte_array> path2(ctx->pop(),this);
310         path2.untag_check(this);
311         data_root<byte_array> path1(ctx->pop(),this);
312         path1.untag_check(this);
313         save_image((vm_char *)(path1.untagged() + 1 ),(vm_char *)(path2.untagged() + 1));
314 }
315
316 void factor_vm::primitive_save_image_and_exit()
317 {
318         /* We unbox this before doing anything else. This is the only point
319         where we might throw an error, so we have to throw an error here since
320         later steps destroy the current image. */
321         data_root<byte_array> path2(ctx->pop(),this);
322         path2.untag_check(this);
323         data_root<byte_array> path1(ctx->pop(),this);
324         path1.untag_check(this);
325
326         /* strip out special_objects data which is set on startup anyway */
327         for(cell i = 0; i < special_object_count; i++)
328                 if(!save_special_p(i)) special_objects[i] = false_object;
329
330         gc(collect_compact_op,
331                 0, /* requested size */
332                 false /* discard objects only reachable from stacks */);
333
334         /* Save the image */
335         if(save_image((vm_char *)(path1.untagged() + 1), (vm_char *)(path2.untagged() + 1)))
336                 exit(0);
337         else
338                 exit(1);
339 }
340
341 }