5 cell code_block::owner_quot() const {
6 tagged<object> executing(owner);
7 if (!optimized_p() && executing->type() == WORD_TYPE)
8 executing = executing.as<word>()->def;
9 return executing.value();
12 /* If the code block is an unoptimized quotation, we can calculate the
13 scan offset. In all other cases -1 is returned. */
14 cell code_block::scan(factor_vm* vm, cell addr) const {
15 if (type() != code_block_unoptimized) {
16 return tag_fixnum(-1);
19 tagged<object> obj(owner);
20 if (obj.type_p(WORD_TYPE))
21 obj = obj.as<word>()->def;
22 if (!obj.type_p(QUOTATION_TYPE))
23 return tag_fixnum(-1);
25 cell ofs = offset(addr);
26 return tag_fixnum(vm->quot_code_offset_to_scan(obj.value(), ofs));
29 cell factor_vm::compute_entry_point_address(cell obj) {
30 switch (tagged<object>(obj).type()) {
32 return untag<word>(obj)->entry_point;
34 return untag<quotation>(obj)->entry_point;
36 critical_error("Expected word or quotation", obj);
41 cell factor_vm::compute_entry_point_pic_address(word* w, cell tagged_quot) {
42 if (!to_boolean(tagged_quot) || max_pic_size == 0)
43 return w->entry_point;
44 quotation* q = untag<quotation>(tagged_quot);
45 if (quotation_compiled_p(q))
46 return q->entry_point;
47 return w->entry_point;
50 cell factor_vm::compute_entry_point_pic_address(cell w_) {
52 return compute_entry_point_pic_address(w.untagged(), w->pic_def);
55 cell factor_vm::compute_entry_point_pic_tail_address(cell w_) {
57 return compute_entry_point_pic_address(w.untagged(), w->pic_tail_def);
60 cell factor_vm::code_block_owner(code_block* compiled) {
61 tagged<object> owner(compiled->owner);
63 /* Cold generic word call sites point to quotations that call the
64 inline-cache-miss and inline-cache-miss-tail primitives. */
65 if (owner.type_p(QUOTATION_TYPE)) {
66 tagged<quotation> quot(owner.as<quotation>());
67 tagged<array> elements(quot->array);
69 FACTOR_ASSERT(array_capacity(elements.untagged()) == 5);
70 FACTOR_ASSERT(array_nth(elements.untagged(), 4) ==
71 special_objects[PIC_MISS_WORD] ||
72 array_nth(elements.untagged(), 4) ==
73 special_objects[PIC_MISS_TAIL_WORD]);
75 tagged<wrapper> word_wrapper(array_nth(elements.untagged(), 0));
76 return word_wrapper->object;
78 return compiled->owner;
81 struct update_word_references_relocation_visitor {
83 bool reset_inline_caches;
85 update_word_references_relocation_visitor(factor_vm* parent,
86 bool reset_inline_caches)
87 : parent(parent), reset_inline_caches(reset_inline_caches) {}
89 void operator()(instruction_operand op) {
90 code_block* compiled = op.load_code_block();
91 switch (op.rel_type()) {
92 case RT_ENTRY_POINT: {
93 cell owner = compiled->owner;
94 if (to_boolean(owner))
95 op.store_value(parent->compute_entry_point_address(owner));
98 case RT_ENTRY_POINT_PIC: {
99 if (reset_inline_caches || !compiled->pic_p()) {
100 cell owner = parent->code_block_owner(compiled);
101 if (to_boolean(owner))
102 op.store_value(parent->compute_entry_point_pic_address(owner));
106 case RT_ENTRY_POINT_PIC_TAIL: {
107 if (reset_inline_caches || !compiled->pic_p()) {
108 cell owner = parent->code_block_owner(compiled);
109 if (to_boolean(owner))
110 op.store_value(parent->compute_entry_point_pic_tail_address(owner));
120 /* Relocate new code blocks completely; updating references to literals,
121 dlsyms, and words. For all other words in the code heap, we only need
122 to update references to other words, without worrying about literals
124 void factor_vm::update_word_references(code_block* compiled,
125 bool reset_inline_caches) {
126 if (code->uninitialized_p(compiled))
127 initialize_code_block(compiled);
128 /* update_word_references() is always applied to every block in
129 the code heap. Since it resets all call sites to point to
130 their canonical entry point (cold entry point for non-tail calls,
131 standard entry point for tail calls), it means that no PICs
132 are referenced after this is done. So instead of polluting
133 the code heap with dead PICs that will be freed on the next
134 GC, we add them to the free list immediately. */
135 else if (reset_inline_caches && compiled->pic_p())
136 code->free(compiled);
138 update_word_references_relocation_visitor visitor(this,
139 reset_inline_caches);
140 compiled->each_instruction_operand(visitor);
141 compiled->flush_icache();
145 /* Look up an external library symbol referenced by a compiled code
147 cell factor_vm::compute_dlsym_address(array* parameters,
150 cell symbol = array_nth(parameters, index);
151 cell library = array_nth(parameters, index + 1);
152 dll* d = to_boolean(library) ? untag<dll>(library) : NULL;
154 cell undef = (cell)factor::undefined_symbol;
155 undef = toc ? FUNCTION_TOC_POINTER(undef) : FUNCTION_CODE_POINTER(undef);
156 if (d != NULL && !d->handle)
159 cell type = TAG(symbol);
160 if (type == BYTE_ARRAY_TYPE) {
162 symbol_char* name = alien_offset(symbol);
163 cell sym = ffi_dlsym_raw(d, name);
164 sym = toc ? FUNCTION_TOC_POINTER(sym) : FUNCTION_CODE_POINTER(sym);
165 return sym ? sym : undef;
167 } else if (type == ARRAY_TYPE) {
169 array* names = untag<array>(symbol);
170 for (cell i = 0; i < array_capacity(names); i++) {
171 symbol_char* name = alien_offset(array_nth(names, i));
172 cell sym = ffi_dlsym_raw(d, name);
173 sym = toc ? FUNCTION_TOC_POINTER(sym) : FUNCTION_CODE_POINTER(sym);
183 cell factor_vm::compute_vm_address(cell arg) {
184 return (cell)this + untag_fixnum(arg);
187 cell factor_vm::lookup_external_address(relocation_type rel_type,
188 code_block *compiled,
193 return compute_dlsym_address(parameters, index, false);
195 return compiled->entry_point();
196 case RT_MEGAMORPHIC_CACHE_HITS:
197 return (cell)&dispatch_stats.megamorphic_cache_hits;
199 return compute_vm_address(array_nth(parameters, index));
200 case RT_CARDS_OFFSET:
202 case RT_DECKS_OFFSET:
205 case RT_EXCEPTION_HANDLER:
206 return (cell)&factor::exception_handler;
210 return compute_dlsym_address(parameters, index, true);
212 case RT_INLINE_CACHE_MISS:
213 return (cell)&factor::inline_cache_miss;
215 return code->safepoint_page;
221 cell factor_vm::compute_external_address(instruction_operand op) {
222 code_block* compiled = op.compiled;
223 array* parameters = to_boolean(compiled->parameters)
224 ? untag<array>(compiled->parameters)
227 relocation_type rel_type = op.rel_type();
229 cell ext_addr = lookup_external_address(rel_type, compiled, parameters, idx);
230 if (ext_addr == (cell)-1) {
232 print_obj(ss, compiled->owner);
235 if (rel_type == RT_DLSYM || rel_type == RT_DLSYM_TOC) {
236 ss << "Bad symbol specifier in compute_external_address";
237 arg = array_nth(parameters, idx);
239 ss << "Bad rel type in compute_external_address";
242 critical_error(ss.str().c_str(), arg);
247 cell factor_vm::compute_here_address(cell arg, cell offset,
248 code_block* compiled) {
249 fixnum n = untag_fixnum(arg);
251 return compiled->entry_point() + offset + n;
252 return compiled->entry_point() - n;
255 struct initial_code_block_visitor {
260 initial_code_block_visitor(factor_vm* parent, cell literals)
261 : parent(parent), literals(literals), literal_index(0) {}
263 cell next_literal() {
264 return array_nth(untag<array>(literals), literal_index++);
267 fixnum compute_operand_value(instruction_operand op) {
268 switch (op.rel_type()) {
270 return next_literal();
272 return parent->compute_entry_point_address(next_literal());
273 case RT_ENTRY_POINT_PIC:
274 return parent->compute_entry_point_pic_address(next_literal());
275 case RT_ENTRY_POINT_PIC_TAIL:
276 return parent->compute_entry_point_pic_tail_address(next_literal());
278 return parent->compute_here_address(
279 next_literal(), op.rel_offset(), op.compiled);
281 return untag_fixnum(next_literal());
283 return parent->compute_external_address(op);
287 void operator()(instruction_operand op) {
288 op.store_value(compute_operand_value(op));
292 /* Perform all fixups on a code block */
293 void factor_vm::initialize_code_block(code_block* compiled, cell literals) {
294 initial_code_block_visitor visitor(this, literals);
295 compiled->each_instruction_operand(visitor);
296 compiled->flush_icache();
298 /* next time we do a minor GC, we have to trace this code block, since
299 the newly-installed instruction operands might point to literals in
301 code->write_barrier(compiled);
304 void factor_vm::initialize_code_block(code_block* compiled) {
305 std::map<code_block*, cell>::iterator iter =
306 code->uninitialized_blocks.find(compiled);
307 initialize_code_block(compiled, iter->second);
308 code->uninitialized_blocks.erase(iter);
311 /* Fixup labels. This is done at compile time, not image load time */
312 void factor_vm::fixup_labels(array* labels, code_block* compiled) {
313 cell size = array_capacity(labels);
315 for (cell i = 0; i < size; i += 3) {
316 relocation_class rel_class =
317 (relocation_class) untag_fixnum(array_nth(labels, i));
318 cell offset = untag_fixnum(array_nth(labels, i + 1));
319 cell target = untag_fixnum(array_nth(labels, i + 2));
321 relocation_entry new_entry(RT_HERE, rel_class, offset);
323 instruction_operand op(new_entry, compiled, 0);
324 op.store_value(target + compiled->entry_point());
329 /* Allocates memory */
330 code_block* factor_vm::allot_code_block(cell size, code_block_type type) {
331 code_block* block = code->allocator->allot(size + sizeof(code_block));
333 /* If allocation failed, do a full GC and compact the code heap.
334 A full GC that occurs as a result of the data heap filling up does not
335 trigger a compaction. This setup ensures that most GCs do not compact
336 the code heap, but if the code fills up, it probably means it will be
337 fragmented after GC anyway, so its best to compact. */
339 primitive_compact_gc();
340 block = code->allocator->allot(size + sizeof(code_block));
342 /* Insufficient room even after code GC, give up */
344 std::cout << "Code heap used: " << code->allocator->occupied_space()
346 std::cout << "Code heap free: " << code->allocator->free_space() << "\n";
347 fatal_error("Out of memory in add-compiled-block", 0);
351 block->set_type(type);
356 /* Allocates memory */
357 code_block* factor_vm::add_code_block(code_block_type type, cell code_,
358 cell labels_, cell owner_,
359 cell relocation_, cell parameters_,
361 cell frame_size_untagged) {
362 data_root<byte_array> code(code_, this);
363 data_root<object> labels(labels_, this);
364 data_root<object> owner(owner_, this);
365 data_root<byte_array> relocation(relocation_, this);
366 data_root<array> parameters(parameters_, this);
367 data_root<array> literals(literals_, this);
369 cell code_length = array_capacity(code.untagged());
370 code_block* compiled = allot_code_block(code_length, type);
372 compiled->owner = owner.value();
374 /* slight space optimization */
375 if (relocation.type() == BYTE_ARRAY_TYPE &&
376 array_capacity(relocation.untagged()) == 0)
377 compiled->relocation = false_object;
379 compiled->relocation = relocation.value();
381 if (parameters.type() == ARRAY_TYPE &&
382 array_capacity(parameters.untagged()) == 0)
383 compiled->parameters = false_object;
385 compiled->parameters = parameters.value();
388 memcpy(compiled + 1, code.untagged() + 1, code_length);
391 if (to_boolean(labels.value()))
392 fixup_labels(labels.as<array>().untagged(), compiled);
394 compiled->set_stack_frame_size(frame_size_untagged);
396 /* Once we are ready, fill in literal and word references in this code
397 block's instruction operands. In most cases this is done right after this
398 method returns, except when compiling words with the non-optimizing
399 compiler at the beginning of bootstrap */
400 this->code->uninitialized_blocks.insert(
401 std::make_pair(compiled, literals.value()));
402 this->code->all_blocks.insert((cell)compiled);
404 /* next time we do a minor GC, we have to trace this code block, since
405 the fields of the code_block struct might point into nursery or aging */
406 this->code->write_barrier(compiled);
411 /* References to undefined symbols are patched up to call this function on
412 image load. It finds the symbol and library, and throws an error. */
413 void factor_vm::undefined_symbol() {
414 cell frame = ctx->callstack_top;
415 cell return_address = *(cell*)frame;
416 code_block* compiled = code->code_block_for_address(return_address);
418 /* Find the RT_DLSYM relocation nearest to the given return
420 cell symbol = false_object;
421 cell library = false_object;
423 auto find_symbol_at_address_visitor = [&](instruction_operand op) {
424 if (op.rel_type() == RT_DLSYM && op.pointer <= return_address) {
425 array* parameters = untag<array>(compiled->parameters);
426 cell index = op.index;
427 symbol = array_nth(parameters, index);
428 library = array_nth(parameters, index + 1);
431 compiled->each_instruction_operand(find_symbol_at_address_visitor);
433 if (!to_boolean(symbol))
434 critical_error("Can't find RT_DLSYM at return address", return_address);
436 general_error(ERROR_UNDEFINED_SYMBOL, symbol, library);
439 void undefined_symbol() {
440 return current_vm()->undefined_symbol();