6 void factor_vm::init_inline_caching(int max_size)
8 max_pic_size = max_size;
11 void factor_vm::deallocate_inline_cache(cell return_address)
13 /* Find the call target. */
14 void *old_xt = get_call_target(return_address);
15 check_code_pointer((cell)old_xt);
17 code_block *old_block = (code_block *)old_xt - 1;
18 cell old_type = old_block->type;
21 /* The call target was either another PIC,
22 or a compiled quotation (megamorphic stub) */
23 assert(old_type == PIC_TYPE || old_type == QUOTATION_TYPE);
26 if(old_type == PIC_TYPE)
27 code->heap_free(old_block);
30 /* Figure out what kind of type check the PIC needs based on the methods
32 cell factor_vm::determine_inline_cache_type(array *cache_entries)
34 bool seen_hi_tag = false, seen_tuple = false;
37 for(i = 0; i < array_capacity(cache_entries); i += 2)
39 cell klass = array_nth(cache_entries,i);
41 /* Is it a tuple layout? */
46 fixnum type = untag_fixnum(klass);
47 if(type >= HEADER_TYPE)
55 critical_error("Expected a fixnum or array",klass);
60 if(seen_hi_tag && seen_tuple) return PIC_HI_TAG_TUPLE;
61 if(seen_hi_tag && !seen_tuple) return PIC_HI_TAG;
62 if(!seen_hi_tag && seen_tuple) return PIC_TUPLE;
63 if(!seen_hi_tag && !seen_tuple) return PIC_TAG;
65 critical_error("Oops",0);
69 void factor_vm::update_pic_count(cell type)
71 pic_counts[type - PIC_TAG]++;
74 struct inline_cache_jit : public jit {
77 inline_cache_jit(cell generic_word_,factor_vm *vm) : jit(PIC_TYPE,generic_word_,vm) {};
79 void emit_check(cell klass);
80 void compile_inline_cache(fixnum index,
87 void inline_cache_jit::emit_check(cell klass)
90 if(TAG(klass) == FIXNUM_TYPE && untag_fixnum(klass) < HEADER_TYPE)
91 code_template = parent_vm->userenv[PIC_CHECK_TAG];
93 code_template = parent_vm->userenv[PIC_CHECK];
95 emit_with(code_template,klass);
98 /* index: 0 = top of stack, 1 = item underneath, etc
99 cache_entries: array of class/method pairs */
100 void inline_cache_jit::compile_inline_cache(fixnum index,
106 gc_root<word> generic_word(generic_word_,parent_vm);
107 gc_root<array> methods(methods_,parent_vm);
108 gc_root<array> cache_entries(cache_entries_,parent_vm);
110 cell inline_cache_type = parent_vm->determine_inline_cache_type(cache_entries.untagged());
111 parent_vm->update_pic_count(inline_cache_type);
113 /* Generate machine code to determine the object's class. */
114 emit_class_lookup(index,inline_cache_type);
116 /* Generate machine code to check, in turn, if the class is one of the cached entries. */
118 for(i = 0; i < array_capacity(cache_entries.untagged()); i += 2)
121 cell klass = array_nth(cache_entries.untagged(),i);
124 /* Yes? Jump to method */
125 cell method = array_nth(cache_entries.untagged(),i + 1);
126 emit_with(parent_vm->userenv[PIC_HIT],method);
129 /* Generate machine code to handle a cache miss, which ultimately results in
130 this function being called again.
132 The inline-cache-miss primitive call receives enough information to
133 reconstruct the PIC. */
134 push(generic_word.value());
135 push(methods.value());
136 push(tag_fixnum(index));
137 push(cache_entries.value());
138 word_special(parent_vm->userenv[tail_call_p ? PIC_MISS_TAIL_WORD : PIC_MISS_WORD]);
141 code_block *factor_vm::compile_inline_cache(fixnum index,cell generic_word_,cell methods_,cell cache_entries_,bool tail_call_p)
143 gc_root<word> generic_word(generic_word_,this);
144 gc_root<array> methods(methods_,this);
145 gc_root<array> cache_entries(cache_entries_,this);
147 inline_cache_jit jit(generic_word.value(),this);
148 jit.compile_inline_cache(index,
149 generic_word.value(),
151 cache_entries.value(),
153 code_block *code = jit.to_code_block();
154 relocate_code_block(code);
158 /* A generic word's definition performs general method lookup. Allocates memory */
159 void *factor_vm::megamorphic_call_stub(cell generic_word)
161 return untag<word>(generic_word)->xt;
164 cell factor_vm::inline_cache_size(cell cache_entries)
166 return array_capacity(untag_check<array>(cache_entries)) / 2;
169 /* Allocates memory */
170 cell factor_vm::add_inline_cache_entry(cell cache_entries_, cell klass_, cell method_)
172 gc_root<array> cache_entries(cache_entries_,this);
173 gc_root<object> klass(klass_,this);
174 gc_root<word> method(method_,this);
176 cell pic_size = array_capacity(cache_entries.untagged());
177 gc_root<array> new_cache_entries(reallot_array(cache_entries.untagged(),pic_size + 2),this);
178 set_array_nth(new_cache_entries.untagged(),pic_size,klass.value());
179 set_array_nth(new_cache_entries.untagged(),pic_size + 1,method.value());
180 return new_cache_entries.value();
183 void factor_vm::update_pic_transitions(cell pic_size)
185 if(pic_size == max_pic_size)
186 pic_to_mega_transitions++;
187 else if(pic_size == 0)
188 cold_call_to_ic_transitions++;
189 else if(pic_size == 1)
190 ic_to_pic_transitions++;
193 /* The cache_entries parameter is either f (on cold call site) or an array (on cache miss).
194 Called from assembly with the actual return address */
195 void *factor_vm::inline_cache_miss(cell return_address)
197 check_code_pointer(return_address);
199 /* Since each PIC is only referenced from a single call site,
200 if the old call target was a PIC, we can deallocate it immediately,
201 instead of leaving dead PICs around until the next GC. */
202 deallocate_inline_cache(return_address);
204 gc_root<array> cache_entries(dpop(),this);
205 fixnum index = untag_fixnum(dpop());
206 gc_root<array> methods(dpop(),this);
207 gc_root<word> generic_word(dpop(),this);
208 gc_root<object> object(((cell *)ds)[-index],this);
212 cell pic_size = inline_cache_size(cache_entries.value());
214 update_pic_transitions(pic_size);
216 if(pic_size >= max_pic_size)
217 xt = megamorphic_call_stub(generic_word.value());
220 cell klass = object_class(object.value());
221 cell method = lookup_method(object.value(),methods.value());
223 gc_root<array> new_cache_entries(add_inline_cache_entry(
224 cache_entries.value(),
227 xt = compile_inline_cache(index,
228 generic_word.value(),
230 new_cache_entries.value(),
231 tail_call_site_p(return_address))->xt();
234 /* Install the new stub. */
235 set_call_target(return_address,xt);
238 printf("Updated %s call site 0x%lx with 0x%lx\n",
239 tail_call_site_p(return_address) ? "tail" : "non-tail",
247 VM_C_API void *inline_cache_miss(cell return_address, factor_vm *myvm)
250 return VM_PTR->inline_cache_miss(return_address);
253 inline void factor_vm::primitive_reset_inline_cache_stats()
255 cold_call_to_ic_transitions = ic_to_pic_transitions = pic_to_mega_transitions = 0;
257 for(i = 0; i < 4; i++) pic_counts[i] = 0;
260 PRIMITIVE_FORWARD(reset_inline_cache_stats)
262 inline void factor_vm::primitive_inline_cache_stats()
264 growable_array stats(this);
265 stats.add(allot_cell(cold_call_to_ic_transitions));
266 stats.add(allot_cell(ic_to_pic_transitions));
267 stats.add(allot_cell(pic_to_mega_transitions));
269 for(i = 0; i < 4; i++)
270 stats.add(allot_cell(pic_counts[i]));
272 dpush(stats.elements.value());
275 PRIMITIVE_FORWARD(inline_cache_stats)