/home/travis/build/MoarVM/MoarVM/src/spesh/worker.c
Line | Count | Source |
1 | | #include "moar.h" |
2 | | |
3 | | /* The specialization worker thread receives logs from other threads about |
4 | | * calls and types that showed up at runtime. It uses this to produce |
5 | | * specialized versions of code. */ |
6 | | |
7 | | /* Enters the work loop. */ |
8 | 144 | static void worker(MVMThreadContext *tc, MVMCallsite *callsite, MVMRegister *args) { |
9 | 144 | MVMObject *updated_static_frames = MVM_repr_alloc_init(tc, |
10 | 144 | tc->instance->boot_types.BOOTArray); |
11 | 144 | MVMObject *previous_static_frames = MVM_repr_alloc_init(tc, |
12 | 144 | tc->instance->boot_types.BOOTArray); |
13 | 144 | |
14 | 144 | tc->instance->speshworker_thread_id = tc->thread_obj->body.thread_id; |
15 | 144 | |
16 | 144 | MVMROOT2(tc, updated_static_frames, previous_static_frames, { |
17 | 144 | while (1) { |
18 | 144 | MVMObject *log_obj; |
19 | 144 | MVMuint64 start_time; |
20 | 144 | unsigned int interval_id; |
21 | 144 | if (MVM_spesh_debug_enabled(tc)) |
22 | 144 | start_time = uv_hrtime(); |
23 | 144 | log_obj = MVM_repr_shift_o(tc, tc->instance->spesh_queue); |
24 | 144 | if (MVM_spesh_debug_enabled(tc)) { |
25 | 144 | MVM_spesh_debug_printf(tc, |
26 | 144 | "Received Logs\n" |
27 | 144 | "=============\n\n" |
28 | 144 | "Was waiting %dus for logs on the log queue.\n\n", |
29 | 144 | (int)((uv_hrtime() - start_time) / 1000)); |
30 | 144 | } |
31 | 144 | |
32 | 144 | if (tc->instance->main_thread->prof_data) |
33 | 144 | MVM_profiler_log_spesh_start(tc); |
34 | 144 | |
35 | 144 | interval_id = MVM_telemetry_interval_start(tc, "spesh worker consuming a log"); |
36 | 144 | |
37 | 144 | uv_mutex_lock(&(tc->instance->mutex_spesh_sync)); |
38 | 144 | tc->instance->spesh_working = 1; |
39 | 144 | uv_mutex_unlock(&(tc->instance->mutex_spesh_sync)); |
40 | 144 | |
41 | 144 | tc->instance->spesh_stats_version++; |
42 | 144 | if (log_obj->st->REPR->ID == MVM_REPR_ID_MVMSpeshLog) { |
43 | 144 | MVMSpeshLog *sl = (MVMSpeshLog *)log_obj; |
44 | 144 | MVM_telemetry_interval_annotate((uintptr_t)sl->body.thread->body.tc, interval_id, "from this thread"); |
45 | 144 | MVMROOT(tc, sl, { |
46 | 144 | MVMThreadContext *stc; |
47 | 144 | MVMuint32 i; |
48 | 144 | MVMuint32 n; |
49 | 144 | |
50 | 144 | /* Update stats, and if we're logging dump each of them. */ |
51 | 144 | tc->instance->spesh_stats_version++; |
52 | 144 | if (MVM_spesh_debug_enabled(tc)) |
53 | 144 | start_time = uv_hrtime(); |
54 | 144 | MVM_spesh_stats_update(tc, sl, updated_static_frames); |
55 | 144 | n = MVM_repr_elems(tc, updated_static_frames); |
56 | 144 | if (MVM_spesh_debug_enabled(tc)) { |
57 | 144 | MVM_spesh_debug_printf(tc, |
58 | 144 | "Statistics Updated\n" |
59 | 144 | "==================\n" |
60 | 144 | "%d frames had their statistics updated in %dus.\n\n", |
61 | 144 | (int)n, (int)((uv_hrtime() - start_time) / 1000)); |
62 | 144 | for (i = 0; i < n; i++) { |
63 | 144 | char *dump = MVM_spesh_dump_stats(tc, (MVMStaticFrame* ) |
64 | 144 | MVM_repr_at_pos_o(tc, updated_static_frames, i)); |
65 | 144 | MVM_spesh_debug_printf(tc, "%s==========\n\n", dump); |
66 | 144 | MVM_free(dump); |
67 | 144 | } |
68 | 144 | } |
69 | 144 | MVM_telemetry_interval_annotate((uintptr_t)n, interval_id, "stats for this many frames"); |
70 | 144 | GC_SYNC_POINT(tc); |
71 | 144 | |
72 | 144 | /* Form a specialization plan. */ |
73 | 144 | if (MVM_spesh_debug_enabled(tc)) |
74 | 144 | start_time = uv_hrtime(); |
75 | 144 | tc->instance->spesh_plan = MVM_spesh_plan(tc, updated_static_frames); |
76 | 144 | if (MVM_spesh_debug_enabled(tc)) { |
77 | 144 | n = tc->instance->spesh_plan->num_planned; |
78 | 144 | MVM_spesh_debug_printf(tc, |
79 | 144 | "Specialization Plan\n" |
80 | 144 | "===================\n" |
81 | 144 | "%u specialization(s) will be produced (planned in %dus).\n\n", |
82 | 144 | n, (int)((uv_hrtime() - start_time) / 1000)); |
83 | 144 | for (i = 0; i < n; i++) { |
84 | 144 | char *dump = MVM_spesh_dump_planned(tc, |
85 | 144 | &(tc->instance->spesh_plan->planned[i])); |
86 | 144 | MVM_spesh_debug_printf(tc, "%s==========\n\n", dump); |
87 | 144 | MVM_free(dump); |
88 | 144 | } |
89 | 144 | } |
90 | 144 | MVM_telemetry_interval_annotate((uintptr_t)tc->instance->spesh_plan->num_planned, interval_id, |
91 | 144 | "this many specializations planned"); |
92 | 144 | GC_SYNC_POINT(tc); |
93 | 144 | |
94 | 144 | /* Implement the plan and then discard it. */ |
95 | 144 | n = tc->instance->spesh_plan->num_planned; |
96 | 144 | for (i = 0; i < n; i++) { |
97 | 144 | MVM_spesh_candidate_add(tc, &(tc->instance->spesh_plan->planned[i])); |
98 | 144 | GC_SYNC_POINT(tc); |
99 | 144 | } |
100 | 144 | MVM_spesh_plan_destroy(tc, tc->instance->spesh_plan); |
101 | 144 | tc->instance->spesh_plan = NULL; |
102 | 144 | |
103 | 144 | /* Clear up stats that didn't get updated for a while, |
104 | 144 | * then add frames updated this time into the previously |
105 | 144 | * updated array. */ |
106 | 144 | MVM_spesh_stats_cleanup(tc, previous_static_frames); |
107 | 144 | n = MVM_repr_elems(tc, updated_static_frames); |
108 | 144 | for (i = 0; i < n; i++) |
109 | 144 | MVM_repr_push_o(tc, previous_static_frames, |
110 | 144 | MVM_repr_at_pos_o(tc, updated_static_frames, i)); |
111 | 144 | |
112 | 144 | /* Clear updated static frames array. */ |
113 | 144 | MVM_repr_pos_set_elems(tc, updated_static_frames, 0); |
114 | 144 | |
115 | 144 | /* Allow the sending thread to produce more logs again, |
116 | 144 | * putting a new spesh log in place if needed. */ |
117 | 144 | stc = sl->body.thread->body.tc; |
118 | 144 | if (stc && !sl->body.was_compunit_bumped) |
119 | 144 | if (MVM_incr(&(stc->spesh_log_quota)) == 0) { |
120 | 144 | stc->spesh_log = MVM_spesh_log_create(tc, sl->body.thread); |
121 | 144 | MVM_telemetry_timestamp(stc, "logging restored after quota had run out"); |
122 | 144 | } |
123 | 144 | |
124 | 144 | /* If needed, signal sending thread that it can continue. */ |
125 | 144 | if (sl->body.block_mutex) { |
126 | 144 | uv_mutex_lock(sl->body.block_mutex); |
127 | 144 | MVM_store(&(sl->body.completed), 1); |
128 | 144 | uv_cond_signal(sl->body.block_condvar); |
129 | 144 | uv_mutex_unlock(sl->body.block_mutex); |
130 | 144 | } |
131 | 144 | }); |
132 | 144 | } |
133 | 144 | else { |
134 | 144 | MVM_panic(1, "Unexpected object sent to specialization worker"); |
135 | 144 | } |
136 | 144 | |
137 | 144 | MVM_telemetry_interval_stop(tc, interval_id, "spesh worker finished"); |
138 | 144 | |
139 | 144 | if (tc->instance->main_thread->prof_data) |
140 | 144 | MVM_profiler_log_spesh_end(tc); |
141 | 144 | |
142 | 144 | uv_mutex_lock(&(tc->instance->mutex_spesh_sync)); |
143 | 144 | tc->instance->spesh_working = 0; |
144 | 144 | uv_cond_broadcast(&(tc->instance->cond_spesh_sync)); |
145 | 144 | uv_mutex_unlock(&(tc->instance->mutex_spesh_sync)); |
146 | 144 | } |
147 | 144 | }); |
148 | 144 | } |
149 | | |
150 | 144 | void MVM_spesh_worker_setup(MVMThreadContext *tc) { |
151 | 144 | if (tc->instance->spesh_enabled) { |
152 | 144 | MVMObject *worker_entry_point; |
153 | 144 | tc->instance->spesh_queue = MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTQueue); |
154 | 144 | worker_entry_point = MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTCCode); |
155 | 144 | ((MVMCFunction *)worker_entry_point)->body.func = worker; |
156 | 144 | MVM_thread_run(tc, MVM_thread_new(tc, worker_entry_point, 1)); |
157 | 144 | } |
158 | 144 | } |