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