/home/travis/build/MoarVM/MoarVM/src/spesh/plan.c
Line | Count | Source (jump to first uncovered line) |
1 | | #include "moar.h" |
2 | | |
3 | | /* Adds a planned specialization, provided it doesn't already exist (this may |
4 | | * happen due to further data suggesting it being logged while it was being |
5 | | * produced). */ |
6 | | void add_planned(MVMThreadContext *tc, MVMSpeshPlan *plan, MVMSpeshPlannedKind kind, |
7 | | MVMStaticFrame *sf, MVMSpeshStatsByCallsite *cs_stats, |
8 | | MVMSpeshStatsType *type_tuple, MVMSpeshStatsByType **type_stats, |
9 | 32.1k | MVMuint32 num_type_stats) { |
10 | 32.1k | MVMSpeshPlanned *p; |
11 | 32.1k | if (sf->body.bytecode_size > MVM_SPESH_MAX_BYTECODE_SIZE || |
12 | 32.1k | MVM_spesh_arg_guard_exists(tc, sf->body.spesh->body.spesh_arg_guard, cs_stats->cs, type_tuple)) { |
13 | 20.6k | /* Clean up allocated memory. |
14 | 20.6k | * NB - the only caller is plan_for_cs, which means that we could do the |
15 | 20.6k | * allocations in here, except that we need the type tuple for the |
16 | 20.6k | * lookup already. So this is messy but it works. */ |
17 | 20.6k | MVM_free(type_stats); |
18 | 20.6k | MVM_free(type_tuple); |
19 | 20.6k | return; |
20 | 20.6k | } |
21 | 11.4k | if (plan->num_planned == plan->alloc_planned) { |
22 | 1.62k | plan->alloc_planned += 16; |
23 | 1.62k | plan->planned = MVM_realloc(plan->planned, |
24 | 1.62k | plan->alloc_planned * sizeof(MVMSpeshPlanned)); |
25 | 1.62k | } |
26 | 11.4k | p = &(plan->planned[plan->num_planned++]); |
27 | 11.4k | p->kind = kind; |
28 | 11.4k | p->sf = sf; |
29 | 11.4k | p->cs_stats = cs_stats; |
30 | 11.4k | p->type_tuple = type_tuple; |
31 | 11.4k | p->type_stats = type_stats; |
32 | 11.4k | p->num_type_stats = num_type_stats; |
33 | 11.4k | if (num_type_stats) { |
34 | 10.6k | MVMuint32 i; |
35 | 10.6k | p->max_depth = type_stats[0]->max_depth; |
36 | 10.6k | for (i = 1; i < num_type_stats; i++) |
37 | 0 | if (type_stats[i]->max_depth > p->max_depth) |
38 | 0 | p->max_depth = type_stats[i]->max_depth; |
39 | 10.6k | } |
40 | 874 | else { |
41 | 874 | p->max_depth = cs_stats->max_depth; |
42 | 874 | } |
43 | 11.4k | } |
44 | | |
45 | | /* Makes a copy of an argument type tuple. */ |
46 | | MVMSpeshStatsType * copy_type_tuple(MVMThreadContext *tc, MVMCallsite *cs, |
47 | 29.0k | MVMSpeshStatsType *to_copy) { |
48 | 29.0k | size_t stats_size = cs->flag_count * sizeof(MVMSpeshStatsType); |
49 | 29.0k | MVMSpeshStatsType *result = MVM_malloc(stats_size); |
50 | 29.0k | memcpy(result, to_copy, stats_size); |
51 | 29.0k | return result; |
52 | 29.0k | } |
53 | | |
54 | | /* Considers the statistics of a given callsite + static frame pairing and |
55 | | * plans specializations to produce for it. */ |
56 | | void plan_for_cs(MVMThreadContext *tc, MVMSpeshPlan *plan, MVMStaticFrame *sf, |
57 | 27.8k | MVMSpeshStatsByCallsite *by_cs) { |
58 | 27.8k | /* See if any types tuples are hot enough. */ |
59 | 27.8k | MVMuint32 i; |
60 | 27.8k | MVMuint32 unaccounted_hits = by_cs->hits; |
61 | 27.8k | MVMuint32 unaccounted_osr_hits = by_cs->osr_hits; |
62 | 127k | for (i = 0; i < by_cs->num_by_type; i++) { |
63 | 99.6k | MVMSpeshStatsByType *by_type = &(by_cs->by_type[i]); |
64 | 99.6k | MVMuint32 hit_percent = by_cs->hits |
65 | 99.6k | ? (100 * by_type->hits) / by_cs->hits |
66 | 0 | : 0; |
67 | 99.6k | MVMuint32 osr_hit_percent = by_cs->osr_hits |
68 | 9.35k | ? (100 * by_type->osr_hits) / by_cs->osr_hits |
69 | 90.2k | : 0; |
70 | 99.6k | if (by_cs->cs && (hit_percent >= MVM_SPESH_PLAN_TT_OBS_PERCENT || |
71 | 70.7k | osr_hit_percent >= MVM_SPESH_PLAN_TT_OBS_PERCENT_OSR)) { |
72 | 29.0k | MVMSpeshStatsByType **evidence = MVM_malloc(sizeof(MVMSpeshStatsByType *)); |
73 | 29.0k | evidence[0] = by_type; |
74 | 29.0k | add_planned(tc, plan, MVM_SPESH_PLANNED_OBSERVED_TYPES, sf, by_cs, |
75 | 29.0k | copy_type_tuple(tc, by_cs->cs, by_type->arg_types), evidence, 1); |
76 | 29.0k | unaccounted_hits -= by_type->hits; |
77 | 29.0k | unaccounted_osr_hits -= by_type->osr_hits; |
78 | 29.0k | } |
79 | 70.5k | else { |
80 | 70.5k | /* TODO derived specialization planning */ |
81 | 70.5k | } |
82 | 99.6k | } |
83 | 27.8k | |
84 | 27.8k | /* If there are enough unaccounted for hits by type specializations, then |
85 | 27.8k | * plan a certain specialization. */ |
86 | 27.8k | if (unaccounted_hits && unaccounted_hits >= MVM_spesh_threshold(tc, sf) || |
87 | 24.8k | unaccounted_osr_hits >= MVM_SPESH_PLAN_CS_MIN_OSR) |
88 | 3.17k | add_planned(tc, plan, MVM_SPESH_PLANNED_CERTAIN, sf, by_cs, NULL, NULL, 0); |
89 | 27.8k | } |
90 | | |
91 | | /* Considers the statistics of a given static frame and plans specializtions |
92 | | * to produce for it. */ |
93 | 299k | void plan_for_sf(MVMThreadContext *tc, MVMSpeshPlan *plan, MVMStaticFrame *sf) { |
94 | 299k | MVMSpeshStats *ss = sf->body.spesh->body.spesh_stats; |
95 | 299k | MVMuint32 threshold = MVM_spesh_threshold(tc, sf); |
96 | 299k | if (ss->hits >= threshold || ss->osr_hits >= MVM_SPESH_PLAN_SF_MIN_OSR) { |
97 | 27.8k | /* The frame is hot enough; look through its callsites to see if any |
98 | 27.8k | * of those are. */ |
99 | 27.8k | MVMuint32 i; |
100 | 78.5k | for (i = 0; i < ss->num_by_callsite; i++) { |
101 | 50.7k | MVMSpeshStatsByCallsite *by_cs = &(ss->by_callsite[i]); |
102 | 50.7k | if (by_cs->hits >= threshold || by_cs->osr_hits >= MVM_SPESH_PLAN_CS_MIN_OSR) |
103 | 27.8k | plan_for_cs(tc, plan, sf, by_cs); |
104 | 50.7k | } |
105 | 27.8k | } |
106 | 299k | } |
107 | | |
108 | | /* Sorts the plan in descending order of maximum call depth. */ |
109 | 21.4k | void sort_plan(MVMThreadContext *tc, MVMSpeshPlanned *planned, MVMuint32 n) { |
110 | 21.4k | if (n >= 2) { |
111 | 9.91k | MVMSpeshPlanned pivot = planned[n / 2]; |
112 | 9.91k | MVMuint32 i, j; |
113 | 19.4k | for (i = 0, j = n - 1; ; i++, j--) { |
114 | 19.4k | MVMSpeshPlanned temp; |
115 | 29.3k | while (planned[i].max_depth > pivot.max_depth) |
116 | 9.89k | i++; |
117 | 26.1k | while (planned[j].max_depth < pivot.max_depth) |
118 | 6.71k | j--; |
119 | 19.4k | if (i >= j) |
120 | 9.91k | break; |
121 | 9.50k | temp = planned[i]; |
122 | 9.50k | planned[i] = planned[j]; |
123 | 9.50k | planned[j] = temp; |
124 | 9.50k | } |
125 | 9.91k | sort_plan(tc, planned, i); |
126 | 9.91k | sort_plan(tc, planned + i, n - i); |
127 | 9.91k | } |
128 | 21.4k | } |
129 | | |
130 | | /* Forms a specialization plan from considering all frames whose statics have |
131 | | * changed. */ |
132 | 1.58k | MVMSpeshPlan * MVM_spesh_plan(MVMThreadContext *tc, MVMObject *updated_static_frames) { |
133 | 1.58k | MVMSpeshPlan *plan = MVM_calloc(1, sizeof(MVMSpeshPlan)); |
134 | 1.58k | MVMint64 updated = MVM_repr_elems(tc, updated_static_frames); |
135 | 1.58k | MVMint64 i; |
136 | 1.58k | #if MVM_GC_DEBUG |
137 | | tc->in_spesh = 1; |
138 | | #endif |
139 | 301k | for (i = 0; i < updated; i++) { |
140 | 299k | MVMObject *sf = MVM_repr_at_pos_o(tc, updated_static_frames, i); |
141 | 299k | plan_for_sf(tc, plan, (MVMStaticFrame *)sf); |
142 | 299k | } |
143 | 1.58k | sort_plan(tc, plan->planned, plan->num_planned); |
144 | 1.58k | #if MVM_GC_DEBUG |
145 | | tc->in_spesh = 0; |
146 | | #endif |
147 | 1.58k | return plan; |
148 | 1.58k | } |
149 | | |
150 | | /* Marks garbage-collectable objects held in the spesh plan. */ |
151 | 338 | void MVM_spesh_plan_gc_mark(MVMThreadContext *tc, MVMSpeshPlan *plan, MVMGCWorklist *worklist) { |
152 | 338 | MVMuint32 i; |
153 | 338 | if (!plan) |
154 | 260 | return; |
155 | 986 | for (i = 0; i < plan->num_planned; i++) { |
156 | 908 | MVMSpeshPlanned *p = &(plan->planned[i]); |
157 | 908 | MVM_gc_worklist_add(tc, worklist, &(p->sf)); |
158 | 908 | if (p->type_tuple) { |
159 | 828 | MVMCallsite *cs = p->cs_stats->cs; |
160 | 828 | MVMuint32 j; |
161 | 2.49k | for (j = 0; j < cs->flag_count; j++) { |
162 | 1.67k | if (cs->arg_flags[j] & MVM_CALLSITE_ARG_OBJ) { |
163 | 1.48k | MVM_gc_worklist_add(tc, worklist, &(p->type_tuple[j].type)); |
164 | 1.48k | MVM_gc_worklist_add(tc, worklist, &(p->type_tuple[j].decont_type)); |
165 | 1.48k | } |
166 | 1.67k | } |
167 | 828 | } |
168 | 908 | } |
169 | 78 | } |
170 | | |
171 | 0 | void MVM_spesh_plan_gc_describe(MVMThreadContext *tc, MVMHeapSnapshotState *ss, MVMSpeshPlan *plan) { |
172 | 0 | MVMuint32 i; |
173 | 0 | if (!plan) |
174 | 0 | return; |
175 | 0 | for (i = 0; i < plan->num_planned; i++) { |
176 | 0 | MVMSpeshPlanned *p = &(plan->planned[i]); |
177 | 0 | MVM_profile_heap_add_collectable_rel_const_cstr(tc, ss, |
178 | 0 | (MVMCollectable*)(p->sf), "staticframe"); |
179 | 0 | if (p->type_tuple) { |
180 | 0 | MVMCallsite *cs = p->cs_stats->cs; |
181 | 0 | MVMuint32 j; |
182 | 0 | for (j = 0; j < cs->flag_count; j++) { |
183 | 0 | if (cs->arg_flags[j] & MVM_CALLSITE_ARG_OBJ) { |
184 | 0 | MVM_profile_heap_add_collectable_rel_const_cstr(tc, ss, |
185 | 0 | (MVMCollectable*)(p->type_tuple[j].type), "argument type"); |
186 | 0 | MVM_profile_heap_add_collectable_rel_const_cstr(tc, ss, |
187 | 0 | (MVMCollectable*)(p->type_tuple[j].decont_type), "argument decont type"); |
188 | 0 | } |
189 | 0 | } |
190 | 0 | } |
191 | 0 | } |
192 | 0 | } |
193 | | |
194 | | /* Frees all memory associated with a specialization plan. */ |
195 | 1.47k | void MVM_spesh_plan_destroy(MVMThreadContext *tc, MVMSpeshPlan *plan) { |
196 | 1.47k | MVMuint32 i; |
197 | 12.0k | for (i = 0; i < plan->num_planned; i++) { |
198 | 10.5k | MVM_free(plan->planned[i].type_stats); |
199 | 10.5k | MVM_free(plan->planned[i].type_tuple); |
200 | 10.5k | } |
201 | 1.47k | MVM_free(plan->planned); |
202 | 1.47k | MVM_free(plan); |
203 | 1.47k | } |