7 void factorvm::init_inline_caching(int max_size)
9 max_pic_size = max_size;
12 void factorvm::deallocate_inline_cache(cell return_address)
14 /* Find the call target. */
15 void *old_xt = get_call_target(return_address);
16 check_code_pointer((cell)old_xt);
18 code_block *old_block = (code_block *)old_xt - 1;
19 cell old_type = old_block->type;
22 /* The call target was either another PIC,
23 or a compiled quotation (megamorphic stub) */
24 assert(old_type == PIC_TYPE || old_type == QUOTATION_TYPE);
27 if(old_type == PIC_TYPE)
28 heap_free(&code,old_block);
31 /* Figure out what kind of type check the PIC needs based on the methods
33 cell factorvm::determine_inline_cache_type(array *cache_entries)
35 bool seen_hi_tag = false, seen_tuple = false;
38 for(i = 0; i < array_capacity(cache_entries); i += 2)
40 cell klass = array_nth(cache_entries,i);
42 /* Is it a tuple layout? */
47 fixnum type = untag_fixnum(klass);
48 if(type >= HEADER_TYPE)
56 critical_error("Expected a fixnum or array",klass);
61 if(seen_hi_tag && seen_tuple) return PIC_HI_TAG_TUPLE;
62 if(seen_hi_tag && !seen_tuple) return PIC_HI_TAG;
63 if(!seen_hi_tag && seen_tuple) return PIC_TUPLE;
64 if(!seen_hi_tag && !seen_tuple) return PIC_TAG;
66 critical_error("Oops",0);
70 void factorvm::update_pic_count(cell type)
72 pic_counts[type - PIC_TAG]++;
75 struct inline_cache_jit : public jit {
78 inline_cache_jit(cell generic_word_,factorvm *vm) : jit(PIC_TYPE,generic_word_,vm) {};
80 void emit_check(cell klass);
81 void compile_inline_cache(fixnum index,
88 void inline_cache_jit::emit_check(cell klass)
91 if(TAG(klass) == FIXNUM_TYPE && untag_fixnum(klass) < HEADER_TYPE)
92 code_template = myvm->userenv[PIC_CHECK_TAG];
94 code_template = myvm->userenv[PIC_CHECK];
96 emit_with(code_template,klass);
99 /* index: 0 = top of stack, 1 = item underneath, etc
100 cache_entries: array of class/method pairs */
101 void inline_cache_jit::compile_inline_cache(fixnum index,
107 gc_root<word> generic_word(generic_word_,myvm);
108 gc_root<array> methods(methods_,myvm);
109 gc_root<array> cache_entries(cache_entries_,myvm);
111 cell inline_cache_type = myvm->determine_inline_cache_type(cache_entries.untagged());
112 myvm->update_pic_count(inline_cache_type);
114 /* Generate machine code to determine the object's class. */
115 emit_class_lookup(index,inline_cache_type);
117 /* Generate machine code to check, in turn, if the class is one of the cached entries. */
119 for(i = 0; i < array_capacity(cache_entries.untagged()); i += 2)
122 cell klass = array_nth(cache_entries.untagged(),i);
125 /* Yes? Jump to method */
126 cell method = array_nth(cache_entries.untagged(),i + 1);
127 emit_with(myvm->userenv[PIC_HIT],method);
130 /* Generate machine code to handle a cache miss, which ultimately results in
131 this function being called again.
133 The inline-cache-miss primitive call receives enough information to
134 reconstruct the PIC. */
135 push(generic_word.value());
136 push(methods.value());
137 push(tag_fixnum(index));
138 push(cache_entries.value());
139 word_special(myvm->userenv[tail_call_p ? PIC_MISS_TAIL_WORD : PIC_MISS_WORD]);
142 code_block *factorvm::compile_inline_cache(fixnum index,cell generic_word_,cell methods_,cell cache_entries_,bool tail_call_p)
144 gc_root<word> generic_word(generic_word_,this);
145 gc_root<array> methods(methods_,this);
146 gc_root<array> cache_entries(cache_entries_,this);
148 inline_cache_jit jit(generic_word.value(),this);
149 jit.compile_inline_cache(index,
150 generic_word.value(),
152 cache_entries.value(),
154 code_block *code = jit.to_code_block();
155 relocate_code_block(code);
159 /* A generic word's definition performs general method lookup. Allocates memory */
160 void *factorvm::megamorphic_call_stub(cell generic_word)
162 return untag<word>(generic_word)->xt;
165 cell factorvm::inline_cache_size(cell cache_entries)
167 return array_capacity(untag_check<array>(cache_entries)) / 2;
170 /* Allocates memory */
171 cell factorvm::add_inline_cache_entry(cell cache_entries_, cell klass_, cell method_)
173 gc_root<array> cache_entries(cache_entries_,this);
174 gc_root<object> klass(klass_,this);
175 gc_root<word> method(method_,this);
177 cell pic_size = array_capacity(cache_entries.untagged());
178 gc_root<array> new_cache_entries(reallot_array(cache_entries.untagged(),pic_size + 2),this);
179 set_array_nth(new_cache_entries.untagged(),pic_size,klass.value());
180 set_array_nth(new_cache_entries.untagged(),pic_size + 1,method.value());
181 return new_cache_entries.value();
184 void factorvm::update_pic_transitions(cell pic_size)
186 if(pic_size == max_pic_size)
187 pic_to_mega_transitions++;
188 else if(pic_size == 0)
189 cold_call_to_ic_transitions++;
190 else if(pic_size == 1)
191 ic_to_pic_transitions++;
194 /* The cache_entries parameter is either f (on cold call site) or an array (on cache miss).
195 Called from assembly with the actual return address */
196 void *factorvm::inline_cache_miss(cell return_address)
198 check_code_pointer(return_address);
200 /* Since each PIC is only referenced from a single call site,
201 if the old call target was a PIC, we can deallocate it immediately,
202 instead of leaving dead PICs around until the next GC. */
203 deallocate_inline_cache(return_address);
205 gc_root<array> cache_entries(dpop(),this);
206 fixnum index = untag_fixnum(dpop());
207 gc_root<array> methods(dpop(),this);
208 gc_root<word> generic_word(dpop(),this);
209 gc_root<object> object(((cell *)ds)[-index],this);
213 cell pic_size = inline_cache_size(cache_entries.value());
215 update_pic_transitions(pic_size);
217 if(pic_size >= max_pic_size)
218 xt = megamorphic_call_stub(generic_word.value());
221 cell klass = object_class(object.value());
222 cell method = lookup_method(object.value(),methods.value());
224 gc_root<array> new_cache_entries(add_inline_cache_entry(
225 cache_entries.value(),
228 xt = compile_inline_cache(index,
229 generic_word.value(),
231 new_cache_entries.value(),
232 tail_call_site_p(return_address))->xt();
235 /* Install the new stub. */
236 set_call_target(return_address,xt);
239 printf("Updated %s call site 0x%lx with 0x%lx\n",
240 tail_call_site_p(return_address) ? "tail" : "non-tail",
248 VM_C_API void *inline_cache_miss(cell return_address, factorvm *myvm)
251 return VM_PTR->inline_cache_miss(return_address);
255 inline void factorvm::vmprim_reset_inline_cache_stats()
257 cold_call_to_ic_transitions = ic_to_pic_transitions = pic_to_mega_transitions = 0;
259 for(i = 0; i < 4; i++) pic_counts[i] = 0;
262 PRIMITIVE(reset_inline_cache_stats)
264 PRIMITIVE_GETVM()->vmprim_reset_inline_cache_stats();
267 inline void factorvm::vmprim_inline_cache_stats()
269 growable_array stats(this);
270 stats.add(allot_cell(cold_call_to_ic_transitions));
271 stats.add(allot_cell(ic_to_pic_transitions));
272 stats.add(allot_cell(pic_to_mega_transitions));
274 for(i = 0; i < 4; i++)
275 stats.add(allot_cell(pic_counts[i]));
277 dpush(stats.elements.value());
280 PRIMITIVE(inline_cache_stats)
282 PRIMITIVE_GETVM()->vmprim_inline_cache_stats();