]> gitweb.factorcode.org Git - factor.git/blob - vm/code_blocks.cpp
VM: undoing 22bb0cfecfc5c09953b613e3ad01d2a713585ea7 (#1513)
[factor.git] / vm / code_blocks.cpp
1 #include "master.hpp"
2
3 namespace factor {
4
5 static cell code_block_owner(code_block* compiled) {
6   tagged<object> owner(compiled->owner);
7
8   // Cold generic word call sites point to quotations that call the
9   // inline-cache-miss and inline-cache-miss-tail primitives.
10   if (owner.type() == QUOTATION_TYPE) {
11     tagged<quotation> quot(owner.as<quotation>());
12     tagged<array> elements(quot->array);
13
14     FACTOR_ASSERT(array_capacity(elements.untagged()) == 5);
15     FACTOR_ASSERT(array_nth(elements.untagged(), 4) ==
16                   special_objects[PIC_MISS_WORD] ||
17                   array_nth(elements.untagged(), 4) ==
18                   special_objects[PIC_MISS_TAIL_WORD]);
19
20     tagged<wrapper> word_wrapper(array_nth(elements.untagged(), 0));
21     return word_wrapper->object;
22   }
23   return compiled->owner;
24 }
25
26 static cell compute_entry_point_address(cell obj) {
27   switch (tagged<object>(obj).type()) {
28     case WORD_TYPE:
29       return untag<word>(obj)->entry_point;
30     case QUOTATION_TYPE:
31       return untag<quotation>(obj)->entry_point;
32     default:
33       critical_error("Expected word or quotation", obj);
34       return 0;
35   }
36 }
37
38 static cell compute_here_address(cell arg, cell offset, code_block* compiled) {
39   fixnum n = untag_fixnum(arg);
40   if (n >= 0)
41     return compiled->entry_point() + offset + n;
42   return compiled->entry_point() - n;
43 }
44
45 cell code_block::owner_quot() const {
46   tagged<object> executing(owner);
47   if (type() != CODE_BLOCK_OPTIMIZED && executing->type() == WORD_TYPE)
48     executing = executing.as<word>()->def;
49   return executing.value();
50 }
51
52 // If the code block is an unoptimized quotation, we can calculate the
53 // scan offset. In all other cases -1 is returned.
54 // Allocates memory (quot_code_offset_to_scan)
55 cell code_block::scan(factor_vm* vm, cell addr) const {
56   if (type() != CODE_BLOCK_UNOPTIMIZED) {
57     return tag_fixnum(-1);
58   }
59
60   tagged<object> obj(owner);
61   if (obj.type() == WORD_TYPE)
62     obj = obj.as<word>()->def;
63   if (obj.type() != QUOTATION_TYPE)
64     return tag_fixnum(-1);
65   cell ofs = offset(addr);
66   return tag_fixnum(vm->quot_code_offset_to_scan(obj.value(), ofs));
67 }
68
69 cell factor_vm::compute_entry_point_pic_address(word* w, cell tagged_quot) {
70   if (!to_boolean(tagged_quot) || max_pic_size == 0)
71     return w->entry_point;
72   quotation* q = untag<quotation>(tagged_quot);
73   if (quotation_compiled_p(q))
74     return q->entry_point;
75   return w->entry_point;
76 }
77
78 cell factor_vm::compute_entry_point_pic_address(cell w_) {
79   tagged<word> w(w_);
80   return compute_entry_point_pic_address(w.untagged(), w->pic_def);
81 }
82
83 cell factor_vm::compute_entry_point_pic_tail_address(cell w_) {
84   tagged<word> w(w_);
85   return compute_entry_point_pic_address(w.untagged(), w->pic_tail_def);
86 }
87
88 // Relocate new code blocks completely; updating references to literals,
89 // dlsyms, and words. For all other words in the code heap, we only need
90 // to update references to other words, without worrying about literals
91 // or dlsyms.
92 void factor_vm::update_word_references(code_block* compiled,
93                                        bool reset_inline_caches) {
94   if (code->uninitialized_p(compiled)) {
95     initialize_code_block(compiled);
96     // update_word_references() is always applied to every block in
97     // the code heap. Since it resets all call sites to point to
98     // their canonical entry point (cold entry point for non-tail calls,
99     // standard entry point for tail calls), it means that no PICs
100     // are referenced after this is done. So instead of polluting
101     // the code heap with dead PICs that will be freed on the next
102     // GC, we add them to the free list immediately.
103   } else if (reset_inline_caches && compiled->pic_p()) {
104     code->free(compiled);
105   } else {
106     auto visit_func = [&](instruction_operand op) {
107
108       switch (op.rel.type()) {
109         case RT_ENTRY_POINT: {
110           code_block* dest = op.load_code_block();
111           cell owner = dest->owner;
112           if (to_boolean(owner))
113             op.store_value(compute_entry_point_address(owner));
114           break;
115         }
116         case RT_ENTRY_POINT_PIC:  {
117           code_block* dest = op.load_code_block();
118           if (reset_inline_caches || !dest->pic_p()) {
119             cell owner = code_block_owner(dest);
120             if (to_boolean(owner))
121               op.store_value(compute_entry_point_pic_address(owner));
122           }
123           break;
124         }
125         case RT_ENTRY_POINT_PIC_TAIL: {
126           code_block* dest = op.load_code_block();
127           if (reset_inline_caches || !dest->pic_p()) {
128             cell owner = code_block_owner(dest);
129             if (to_boolean(owner))
130               op.store_value(compute_entry_point_pic_tail_address(owner));
131           }
132           break;
133         }
134         default:
135           break;
136       }
137     };
138     compiled->each_instruction_operand(visit_func);
139     compiled->flush_icache();
140   }
141 }
142
143 // Look up an external library symbol referenced by a compiled code block
144 cell factor_vm::compute_dlsym_address(array* parameters,
145                                       cell index,
146                                       bool toc) {
147   cell symbol = array_nth(parameters, index);
148   cell library = array_nth(parameters, index + 1);
149   dll* d = to_boolean(library) ? untag<dll>(library) : NULL;
150
151   cell undef = (cell)factor::undefined_symbol;
152   undef = toc ? FUNCTION_TOC_POINTER(undef) : FUNCTION_CODE_POINTER(undef);
153   if (d != NULL && !d->handle)
154     return undef;
155
156   FACTOR_ASSERT(TAG(symbol) == BYTE_ARRAY_TYPE);
157   symbol_char* name = alien_offset(symbol);
158   cell sym = ffi_dlsym_raw(d, name);
159   sym = toc ? FUNCTION_TOC_POINTER(sym) : FUNCTION_CODE_POINTER(sym);
160   return sym ? sym : undef;
161 }
162
163 cell factor_vm::lookup_external_address(relocation_type rel_type,
164                                         code_block *compiled,
165                                         array* parameters,
166                                         cell index) {
167   switch (rel_type) {
168     case RT_DLSYM:
169       return compute_dlsym_address(parameters, index, false);
170     case RT_THIS:
171       return compiled->entry_point();
172     case RT_MEGAMORPHIC_CACHE_HITS:
173       return (cell)&dispatch_stats.megamorphic_cache_hits;
174     case RT_VM:
175       return (cell)this + untag_fixnum(array_nth(parameters, index));
176     case RT_CARDS_OFFSET:
177       return cards_offset;
178     case RT_DECKS_OFFSET:
179       return decks_offset;
180 #ifdef FACTOR_PPC
181     case RT_DLSYM_TOC:
182       return compute_dlsym_address(parameters, index, true);
183 #endif
184     case RT_INLINE_CACHE_MISS:
185       return (cell)&factor::inline_cache_miss;
186     case RT_SAFEPOINT:
187       return code->safepoint_page;
188     default:
189       return -1;
190   }
191 }
192
193 cell factor_vm::compute_external_address(instruction_operand op) {
194   code_block* compiled = op.compiled;
195   array* parameters = to_boolean(compiled->parameters)
196       ? untag<array>(compiled->parameters)
197       : NULL;
198   cell idx = op.index;
199   relocation_type rel_type = op.rel.type();
200
201   cell ext_addr = lookup_external_address(rel_type, compiled, parameters, idx);
202   if (ext_addr == (cell)-1) {
203     ostringstream ss;
204     print_obj(ss, compiled->owner);
205     ss << ": ";
206     cell arg;
207     if (rel_type == RT_DLSYM || rel_type == RT_DLSYM_TOC) {
208       ss << "Bad symbol specifier in compute_external_address";
209       arg = array_nth(parameters, idx);
210     } else {
211       ss << "Bad rel type in compute_external_address";
212       arg = rel_type;
213     }
214     critical_error(ss.str().c_str(), arg);
215   }
216   return ext_addr;
217 }
218
219 struct initial_code_block_visitor {
220   factor_vm* parent;
221   cell literals;
222   cell literal_index;
223
224   initial_code_block_visitor(factor_vm* parent, cell literals)
225       : parent(parent), literals(literals), literal_index(0) {}
226
227   cell next_literal() {
228     return array_nth(untag<array>(literals), literal_index++);
229   }
230
231   fixnum compute_operand_value(instruction_operand op) {
232     switch (op.rel.type()) {
233       case RT_LITERAL:
234         return next_literal();
235       case RT_ENTRY_POINT:
236         return compute_entry_point_address(next_literal());
237       case RT_ENTRY_POINT_PIC:
238         return parent->compute_entry_point_pic_address(next_literal());
239       case RT_ENTRY_POINT_PIC_TAIL:
240         return parent->compute_entry_point_pic_tail_address(next_literal());
241       case RT_HERE:
242         return compute_here_address(
243             next_literal(), op.rel.offset(), op.compiled);
244       case RT_UNTAGGED:
245         return untag_fixnum(next_literal());
246       default:
247         return parent->compute_external_address(op);
248     }
249   }
250
251   void operator()(instruction_operand op) {
252     op.store_value(compute_operand_value(op));
253   }
254 };
255
256 // Perform all fixups on a code block
257 void factor_vm::initialize_code_block(code_block* compiled, cell literals) {
258   initial_code_block_visitor visitor(this, literals);
259   compiled->each_instruction_operand(visitor);
260   compiled->flush_icache();
261
262   // next time we do a minor GC, we have to trace this code block, since
263   // the newly-installed instruction operands might point to literals in
264   // nursery or aging
265   code->write_barrier(compiled);
266 }
267
268 void factor_vm::initialize_code_block(code_block* compiled) {
269   std::map<code_block*, cell>::iterator iter =
270       code->uninitialized_blocks.find(compiled);
271   initialize_code_block(compiled, iter->second);
272   code->uninitialized_blocks.erase(iter);
273 }
274
275 // Fixup labels. This is done at compile time, not image load time
276 void factor_vm::fixup_labels(array* labels, code_block* compiled) {
277   cell size = array_capacity(labels);
278
279   for (cell i = 0; i < size; i += 3) {
280     relocation_class rel_class =
281         (relocation_class) untag_fixnum(array_nth(labels, i));
282     cell offset = untag_fixnum(array_nth(labels, i + 1));
283     cell target = untag_fixnum(array_nth(labels, i + 2));
284
285     relocation_entry new_entry(RT_HERE, rel_class, offset);
286
287     instruction_operand op(new_entry, compiled, 0);
288     op.store_value(target + compiled->entry_point());
289   }
290 }
291
292 // Might GC
293 // Allocates memory
294 code_block* factor_vm::add_code_block(code_block_type type, cell code_,
295                                       cell labels_, cell owner_,
296                                       cell relocation_, cell parameters_,
297                                       cell literals_,
298                                       cell frame_size_untagged) {
299   data_root<byte_array> code(code_, this);
300   data_root<object> labels(labels_, this);
301   data_root<object> owner(owner_, this);
302   data_root<byte_array> relocation(relocation_, this);
303   data_root<array> parameters(parameters_, this);
304   data_root<array> literals(literals_, this);
305
306   cell code_length = array_capacity(code.untagged());
307   code_block* compiled = allot_code_block(code_length, type);
308
309   compiled->owner = owner.value();
310
311   // slight space optimization
312   if (relocation.type() == BYTE_ARRAY_TYPE &&
313       array_capacity(relocation.untagged()) == 0)
314     compiled->relocation = false_object;
315   else
316     compiled->relocation = relocation.value();
317
318   if (parameters.type() == ARRAY_TYPE &&
319       array_capacity(parameters.untagged()) == 0)
320     compiled->parameters = false_object;
321   else
322     compiled->parameters = parameters.value();
323
324   // code
325   memcpy(compiled + 1, code.untagged() + 1, code_length);
326
327   // fixup labels
328   if (to_boolean(labels.value()))
329     fixup_labels(labels.as<array>().untagged(), compiled);
330
331   compiled->set_stack_frame_size(frame_size_untagged);
332
333   // Once we are ready, fill in literal and word references in this code
334   // block's instruction operands. In most cases this is done right after this
335   // method returns, except when compiling words with the non-optimizing
336   // compiler at the beginning of bootstrap
337   this->code->uninitialized_blocks.insert(
338       std::make_pair(compiled, literals.value()));
339   this->code->all_blocks.insert((cell)compiled);
340
341   return compiled;
342 }
343
344 // References to undefined symbols are patched up to call this function on
345 // image load. It finds the symbol and library, and throws an error.
346 void factor_vm::undefined_symbol() {
347   cell frame = ctx->callstack_top;
348   cell return_address = *(cell*)frame;
349   code_block* compiled = code->code_block_for_address(return_address);
350
351   // Find the RT_DLSYM relocation nearest to the given return address.
352   cell symbol = false_object;
353   cell library = false_object;
354
355   auto find_symbol_at_address_visitor = [&](instruction_operand op) {
356     if (op.rel.type() == RT_DLSYM && op.pointer <= return_address) {
357       array* parameters = untag<array>(compiled->parameters);
358       cell index = op.index;
359       symbol = array_nth(parameters, index);
360       library = array_nth(parameters, index + 1);
361     }
362   };
363   compiled->each_instruction_operand(find_symbol_at_address_visitor);
364
365   if (!to_boolean(symbol))
366     critical_error("Can't find RT_DLSYM at return address", return_address);
367   else
368     general_error(ERROR_UNDEFINED_SYMBOL, symbol, library);
369 }
370
371 void undefined_symbol() {
372   return current_vm()->undefined_symbol();
373 }
374 }