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