5 static cell code_block_owner(code_block* compiled) {
6 cell owner = compiled->owner;
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)
13 quotation* quot = untag<quotation>(owner);
14 array* elements = untag<array>(quot->array);
16 FACTOR_ASSERT(array_capacity(elements) == 5);
17 wrapper* wrap = untag<wrapper>(array_nth(elements, 0));
21 static cell compute_entry_point_address(cell obj) {
24 return untag<word>(obj)->entry_point;
26 return untag<quotation>(obj)->entry_point;
28 critical_error("Expected word or quotation", obj);
33 static cell compute_here_address(cell arg, cell offset, code_block* compiled) {
34 fixnum n = untag_fixnum(arg);
36 return compiled->entry_point() + offset + n;
37 return compiled->entry_point() - n;
40 cell code_block::owner_quot() const {
41 if (!optimized_p() && TAG(owner) == WORD_TYPE)
42 return untag<word>(owner)->def;
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);
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));
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;
72 cell factor_vm::compute_entry_point_pic_address(cell w_) {
74 return compute_entry_point_pic_address(w.untagged(), w->pic_def);
77 cell factor_vm::compute_entry_point_pic_tail_address(cell w_) {
79 return compute_entry_point_pic_address(w.untagged(), w->pic_tail_def);
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
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()) {
100 auto visit_func = [&](instruction_operand op) {
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));
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));
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));
132 compiled->each_instruction_operand(visit_func);
133 compiled->flush_icache();
137 /* Look up an external library symbol referenced by a compiled code
139 cell factor_vm::compute_dlsym_address(array* parameters,
142 cell symbol = array_nth(parameters, index);
143 cell library = array_nth(parameters, index + 1);
144 dll* d = to_boolean(library) ? untag<dll>(library) : NULL;
146 cell undef = (cell)factor::undefined_symbol;
147 undef = toc ? FUNCTION_TOC_POINTER(undef) : FUNCTION_CODE_POINTER(undef);
148 if (d != NULL && !d->handle)
151 FACTOR_ASSERT(TAG(symbol) == BYTE_ARRAY_TYPE);
152 symbol_char* name = alien_offset(symbol);
153 cell sym = ffi_dlsym_raw(d, name);
154 sym = toc ? FUNCTION_TOC_POINTER(sym) : FUNCTION_CODE_POINTER(sym);
155 return sym ? sym : undef;
158 cell factor_vm::lookup_external_address(relocation_type rel_type,
159 code_block *compiled,
164 return compute_dlsym_address(parameters, index, false);
166 return compiled->entry_point();
167 case RT_MEGAMORPHIC_CACHE_HITS:
168 return (cell)&dispatch_stats.megamorphic_cache_hits;
170 return (cell)this + untag_fixnum(array_nth(parameters, index));
171 case RT_CARDS_OFFSET:
173 case RT_DECKS_OFFSET:
177 return compute_dlsym_address(parameters, index, true);
179 case RT_INLINE_CACHE_MISS:
180 return (cell)&factor::inline_cache_miss;
182 return code->safepoint_page;
188 cell factor_vm::compute_external_address(instruction_operand op) {
189 code_block* compiled = op.compiled;
190 array* parameters = to_boolean(compiled->parameters)
191 ? untag<array>(compiled->parameters)
194 relocation_type rel_type = op.rel.type();
196 cell ext_addr = lookup_external_address(rel_type, compiled, parameters, idx);
197 if (ext_addr == (cell)-1) {
199 print_obj(ss, compiled->owner);
202 if (rel_type == RT_DLSYM || rel_type == RT_DLSYM_TOC) {
203 ss << "Bad symbol specifier in compute_external_address";
204 arg = array_nth(parameters, idx);
206 ss << "Bad rel type in compute_external_address";
209 critical_error(ss.str().c_str(), arg);
214 struct initial_code_block_visitor {
219 initial_code_block_visitor(factor_vm* parent, cell literals)
220 : parent(parent), literals(literals), literal_index(0) {}
222 cell next_literal() {
223 return array_nth(untag<array>(literals), literal_index++);
226 fixnum compute_operand_value(instruction_operand op) {
227 switch (op.rel.type()) {
229 return next_literal();
231 return compute_entry_point_address(next_literal());
232 case RT_ENTRY_POINT_PIC:
233 return parent->compute_entry_point_pic_address(next_literal());
234 case RT_ENTRY_POINT_PIC_TAIL:
235 return parent->compute_entry_point_pic_tail_address(next_literal());
237 return compute_here_address(
238 next_literal(), op.rel.offset(), op.compiled);
240 return untag_fixnum(next_literal());
242 return parent->compute_external_address(op);
246 void operator()(instruction_operand op) {
247 op.store_value(compute_operand_value(op));
251 /* Perform all fixups on a code block */
252 void factor_vm::initialize_code_block(code_block* compiled, cell literals) {
253 initial_code_block_visitor visitor(this, literals);
254 compiled->each_instruction_operand(visitor);
255 compiled->flush_icache();
257 /* next time we do a minor GC, we have to trace this code block, since
258 the newly-installed instruction operands might point to literals in
260 code->write_barrier(compiled);
263 void factor_vm::initialize_code_block(code_block* compiled) {
264 std::map<code_block*, cell>::iterator iter =
265 code->uninitialized_blocks.find(compiled);
266 initialize_code_block(compiled, iter->second);
267 code->uninitialized_blocks.erase(iter);
270 /* Fixup labels. This is done at compile time, not image load time */
271 void factor_vm::fixup_labels(array* labels, code_block* compiled) {
272 cell size = array_capacity(labels);
274 for (cell i = 0; i < size; i += 3) {
275 relocation_class rel_class =
276 (relocation_class) untag_fixnum(array_nth(labels, i));
277 cell offset = untag_fixnum(array_nth(labels, i + 1));
278 cell target = untag_fixnum(array_nth(labels, i + 2));
280 relocation_entry new_entry(RT_HERE, rel_class, offset);
282 instruction_operand op(new_entry, compiled, 0);
283 op.store_value(target + compiled->entry_point());
288 /* Allocates memory */
289 code_block* factor_vm::allot_code_block(cell size, code_block_type type) {
290 code_block* block = code->allocator->allot(size + sizeof(code_block));
292 /* If allocation failed, do a full GC and compact the code heap.
293 A full GC that occurs as a result of the data heap filling up does not
294 trigger a compaction. This setup ensures that most GCs do not compact
295 the code heap, but if the code fills up, it probably means it will be
296 fragmented after GC anyway, so its best to compact. */
298 primitive_compact_gc();
299 block = code->allocator->allot(size + sizeof(code_block));
301 /* Insufficient room even after code GC, give up */
303 std::cout << "Code heap used: " << code->allocator->occupied_space()
305 std::cout << "Code heap free: " << code->allocator->free_space() << "\n";
306 fatal_error("Out of memory in add-compiled-block", 0);
310 block->set_type(type);
315 /* Allocates memory */
316 code_block* factor_vm::add_code_block(code_block_type type, cell code_,
317 cell labels_, cell owner_,
318 cell relocation_, cell parameters_,
320 cell frame_size_untagged) {
321 data_root<byte_array> code(code_, this);
322 data_root<object> labels(labels_, this);
323 data_root<object> owner(owner_, this);
324 data_root<byte_array> relocation(relocation_, this);
325 data_root<array> parameters(parameters_, this);
326 data_root<array> literals(literals_, this);
328 cell code_length = array_capacity(code.untagged());
329 code_block* compiled = allot_code_block(code_length, type);
331 compiled->owner = owner.value();
333 /* slight space optimization */
334 if (relocation.type() == BYTE_ARRAY_TYPE &&
335 array_capacity(relocation.untagged()) == 0)
336 compiled->relocation = false_object;
338 compiled->relocation = relocation.value();
340 if (parameters.type() == ARRAY_TYPE &&
341 array_capacity(parameters.untagged()) == 0)
342 compiled->parameters = false_object;
344 compiled->parameters = parameters.value();
347 memcpy(compiled + 1, code.untagged() + 1, code_length);
350 if (to_boolean(labels.value()))
351 fixup_labels(labels.as<array>().untagged(), compiled);
353 compiled->set_stack_frame_size(frame_size_untagged);
355 /* Once we are ready, fill in literal and word references in this code
356 block's instruction operands. In most cases this is done right after this
357 method returns, except when compiling words with the non-optimizing
358 compiler at the beginning of bootstrap */
359 this->code->uninitialized_blocks.insert(
360 std::make_pair(compiled, literals.value()));
361 this->code->all_blocks.insert((cell)compiled);
363 /* next time we do a minor GC, we have to trace this code block, since
364 the fields of the code_block struct might point into nursery or aging */
365 this->code->write_barrier(compiled);
370 /* References to undefined symbols are patched up to call this function on
371 image load. It finds the symbol and library, and throws an error. */
372 void factor_vm::undefined_symbol() {
373 cell frame = ctx->callstack_top;
374 cell return_address = *(cell*)frame;
375 code_block* compiled = code->code_block_for_address(return_address);
377 /* Find the RT_DLSYM relocation nearest to the given return
379 cell symbol = false_object;
380 cell library = false_object;
382 auto find_symbol_at_address_visitor = [&](instruction_operand op) {
383 if (op.rel.type() == RT_DLSYM && op.pointer <= return_address) {
384 array* parameters = untag<array>(compiled->parameters);
385 cell index = op.index;
386 symbol = array_nth(parameters, index);
387 library = array_nth(parameters, index + 1);
390 compiled->each_instruction_operand(find_symbol_at_address_visitor);
392 if (!to_boolean(symbol))
393 critical_error("Can't find RT_DLSYM at return address", return_address);
395 general_error(ERROR_UNDEFINED_SYMBOL, symbol, library);
398 void undefined_symbol() {
399 return current_vm()->undefined_symbol();