1 /*
2 * %CopyrightBegin%
3 *
4 * Copyright Ericsson AB 2020-2020. All Rights Reserved.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 * %CopyrightEnd%
19 */
20
21 #include "beam_asm.hpp"
22
23 extern "C"
24 {
25 #include "bif.h"
26 #include "code_ix.h"
27 #include "erl_proc_sig_queue.h"
28 #ifdef USE_VM_PROBES
29 # include "dtrace-wrapper.h"
30 #endif
31 }
32
33 #ifdef ERTS_SUPPORT_OLD_RECV_MARK_INSTRS
34
recv_mark(Process * p)35 static void recv_mark(Process *p) {
36 /* inlined here... */
37 erts_msgq_recv_marker_insert_bind(p, erts_old_recv_marker_id);
38 }
39
recv_mark_set(Process * p)40 static void recv_mark_set(Process *p) {
41 /* inlined here... */
42 erts_msgq_recv_marker_set_save(p, erts_old_recv_marker_id);
43 }
44
emit_i_recv_mark()45 void BeamModuleAssembler::emit_i_recv_mark() {
46 /*
47 * OLD INSTRUCTION: This instruction is to be removed
48 * in OTP 26.
49 *
50 * Save the current end of message queue
51 */
52 emit_enter_runtime();
53
54 a.mov(ARG1, c_p);
55 runtime_call<1>(recv_mark);
56
57 emit_leave_runtime();
58 }
59
emit_i_recv_set()60 void BeamModuleAssembler::emit_i_recv_set() {
61 /*
62 * OLD INSTRUCTION: This instruction is to be removed
63 * in OTP 26.
64 *
65 * If previously saved recv mark, set save pointer to it
66 */
67 emit_enter_runtime();
68
69 a.mov(ARG1, c_p);
70 runtime_call<1>(recv_mark_set);
71
72 emit_leave_runtime();
73 }
74
75 #endif /* ERTS_SUPPORT_OLD_RECV_MARK_INSTRS */
76
emit_recv_marker_reserve(const ArgVal & Dst)77 void BeamModuleAssembler::emit_recv_marker_reserve(const ArgVal &Dst) {
78 emit_enter_runtime();
79
80 a.mov(ARG1, c_p);
81 runtime_call<1>(erts_msgq_recv_marker_insert);
82
83 emit_leave_runtime();
84
85 mov_arg(Dst, RET);
86 }
87
emit_recv_marker_bind(const ArgVal & Marker,const ArgVal & Reference)88 void BeamModuleAssembler::emit_recv_marker_bind(const ArgVal &Marker,
89 const ArgVal &Reference) {
90 mov_arg(ARG2, Marker);
91 mov_arg(ARG3, Reference);
92
93 emit_enter_runtime();
94
95 a.mov(ARG1, c_p);
96 runtime_call<3>(erts_msgq_recv_marker_bind);
97
98 emit_leave_runtime();
99 }
100
emit_recv_marker_clear(const ArgVal & Reference)101 void BeamModuleAssembler::emit_recv_marker_clear(const ArgVal &Reference) {
102 mov_arg(ARG2, Reference);
103
104 emit_enter_runtime();
105
106 a.mov(ARG1, c_p);
107 runtime_call<2>(erts_msgq_recv_marker_clear);
108
109 emit_leave_runtime();
110 }
111
emit_recv_marker_use(const ArgVal & Reference)112 void BeamModuleAssembler::emit_recv_marker_use(const ArgVal &Reference) {
113 mov_arg(ARG2, Reference);
114
115 emit_enter_runtime();
116
117 a.mov(ARG1, c_p);
118 runtime_call<2>(erts_msgq_recv_marker_set_save);
119
120 emit_leave_runtime();
121 }
122
123 #ifdef ERTS_ENABLE_LOCK_CHECK
erts_lc_proc_sig_receive_helper(Process * c_p,int fcalls,int neg_o_reds,ErtsMessage ** msgpp,int * get_outp)124 int erts_lc_proc_sig_receive_helper(Process *c_p,
125 int fcalls,
126 int neg_o_reds,
127 ErtsMessage **msgpp,
128 int *get_outp) {
129 int res;
130 /*
131 * erts_proc_sig_receive_helper() may temporarliy release
132 * its own main lock...
133 */
134 ERTS_UNREQ_PROC_MAIN_LOCK(c_p);
135 res = erts_proc_sig_receive_helper(c_p,
136 fcalls,
137 neg_o_reds,
138 msgpp,
139 get_outp);
140 ERTS_REQ_PROC_MAIN_LOCK(c_p);
141 return res;
142 }
143 #endif
144
emit_i_loop_rec_shared()145 void BeamGlobalAssembler::emit_i_loop_rec_shared() {
146 Label restart = a.newLabel(), peek_message = a.newLabel(),
147 schedule_out = a.newLabel(), check_is_distributed = a.newLabel(),
148 done = a.newLabel();
149
150 x86::Mem await_addr = TMP_MEM1q, message_ptr = TMP_MEM2q,
151 get_out = TMP_MEM3d;
152
153 a.or_(x86::dword_ptr(c_p, offsetof(Process, flags)), imm(F_DELAY_GC));
154 a.mov(x86::qword_ptr(c_p, offsetof(Process, i)), ARG1);
155 a.mov(await_addr, ARG2);
156
157 a.bind(restart);
158 {
159 a.test(FCALLS, FCALLS);
160 a.jle(schedule_out);
161
162 /* !! FALL THROUGH !! */
163 }
164
165 comment("Peek next message");
166 a.bind(peek_message);
167 {
168 #ifdef DEBUG
169 /* Want asserts in erts_msgq_peek_msg()... */
170 emit_enter_runtime();
171 a.mov(ARG1, c_p);
172 runtime_call<1>(erts_msgq_peek_msg);
173 emit_leave_runtime();
174 a.mov(ARG1, RET);
175 #else
176 a.mov(ARG1, x86::qword_ptr(c_p, offsetof(Process, sig_qs.save)));
177 a.mov(ARG1, x86::qword_ptr(ARG1));
178 #endif
179 a.test(ARG1, ARG1);
180 a.jne(check_is_distributed);
181
182 comment("Inner queue empty, fetch more from outer/middle queues");
183
184 emit_enter_runtime<Update::eReductions | Update::eStack |
185 Update::eHeap>();
186
187 a.mov(message_ptr, imm(0));
188 a.mov(ARG1, c_p);
189 a.mov(ARG2, FCALLS);
190 mov_imm(ARG3, 0);
191 a.lea(ARG4, message_ptr);
192 a.lea(ARG5, get_out);
193 #ifdef ERTS_ENABLE_LOCK_CHECK
194 runtime_call<5>(erts_lc_proc_sig_receive_helper);
195 #else
196 runtime_call<5>(erts_proc_sig_receive_helper);
197 #endif
198
199 /* erts_proc_sig_receive_helper merely inspects FCALLS, so we don't
200 * need to update it here.
201 *
202 * Also note that another process may have loaded new code and sent us
203 * a message to notify us about it, so we must update the active code
204 * index. */
205 emit_leave_runtime<Update::eStack | Update::eHeap |
206 Update::eCodeIndex>();
207
208 a.sub(FCALLS, RET);
209
210 /* Need to spill message_ptr to ARG1 as check_is_distributed uses it */
211 a.mov(ARG1, message_ptr);
212 a.test(ARG1, ARG1);
213 /* NOTE: Short won't reach if JIT_HARD_DEBUG is defined. */
214 a.jne(check_is_distributed);
215
216 /* Did we receive a signal or run out of reds? */
217 a.cmp(get_out, imm(0));
218 a.short_().jne(schedule_out);
219
220 /* The queue is empty and we're not yielding or exiting, so we'll jump
221 * to our wait/timeout instruction.
222 *
223 * Note that the message queue lock is still held in this case. */
224 a.and_(x86::dword_ptr(c_p, offsetof(Process, flags)), imm(~F_DELAY_GC));
225
226 emit_discard_cp();
227 a.jmp(await_addr);
228 }
229
230 a.bind(schedule_out);
231 {
232 /* We either ran out of reductions or received an exit signal; schedule
233 * ourselves out. The yield address (`c_p->i`) was set on ingress. */
234 a.and_(x86::dword_ptr(c_p, offsetof(Process, flags)), imm(~F_DELAY_GC));
235 a.mov(x86::qword_ptr(c_p, offsetof(Process, arity)), imm(0));
236 a.mov(x86::qword_ptr(c_p, offsetof(Process, current)), imm(0));
237
238 emit_discard_cp();
239 a.jmp(labels[do_schedule]);
240 }
241
242 comment("Check if message is distributed");
243 a.bind(check_is_distributed);
244 {
245 a.cmp(x86::qword_ptr(ARG1, offsetof(ErtsSignal, common.tag)),
246 imm(THE_NON_VALUE));
247 /* NOTE: Short won't reach if JIT_HARD_DEBUG is defined. */
248 a.jne(done);
249
250 a.sub(FCALLS, imm(10));
251
252 emit_enter_runtime();
253
254 a.mov(ARG2, ARG1);
255 a.mov(ARG1, c_p);
256 runtime_call<2>(beam_jit_decode_dist);
257
258 emit_leave_runtime();
259
260 a.test(RET, RET);
261 a.je(restart);
262
263 a.mov(ARG1, RET);
264 /* !! FALL THROUGH !! */
265 }
266
267 a.bind(done);
268 {
269 a.mov(ARG1, x86::qword_ptr(ARG1, offsetof(ErtsMessage, m[0])));
270 a.mov(getXRef(0), ARG1);
271
272 a.ret();
273 }
274 }
275
emit_i_loop_rec(const ArgVal & Wait)276 void BeamModuleAssembler::emit_i_loop_rec(const ArgVal &Wait) {
277 Label entry = a.newLabel();
278
279 align_erlang_cp();
280 a.bind(entry);
281
282 a.lea(ARG1, x86::qword_ptr(entry));
283 a.lea(ARG2, x86::qword_ptr(labels[Wait.getValue()]));
284 fragment_call(ga->get_i_loop_rec_shared());
285 }
286
emit_remove_message()287 void BeamModuleAssembler::emit_remove_message() {
288 /* HTOP and E are passed explicitly and only read from, so we don't need to
289 * swap them out. */
290 a.mov(ARG3, HTOP);
291 a.mov(ARG4, E);
292
293 emit_enter_runtime();
294
295 a.mov(ARG1, c_p);
296 a.mov(ARG2, FCALLS);
297 a.mov(ARG5, active_code_ix);
298 runtime_call<5>(beam_jit_remove_message);
299 a.mov(FCALLS, RET);
300
301 emit_leave_runtime();
302 }
303
emit_loop_rec_end(const ArgVal & Dest)304 void BeamModuleAssembler::emit_loop_rec_end(const ArgVal &Dest) {
305 emit_enter_runtime();
306
307 a.mov(ARG1, c_p);
308 runtime_call<1>(erts_msgq_set_save_next);
309
310 emit_leave_runtime();
311
312 a.dec(FCALLS);
313 a.jmp(labels[Dest.getValue()]);
314 }
315
emit_wait_unlocked(const ArgVal & Dest)316 void BeamModuleAssembler::emit_wait_unlocked(const ArgVal &Dest) {
317 emit_enter_runtime();
318
319 a.mov(ARG1, c_p);
320 a.lea(ARG2, x86::qword_ptr(labels[Dest.getValue()]));
321 runtime_call<2>(beam_jit_wait_unlocked);
322
323 emit_leave_runtime();
324
325 abs_jmp(ga->get_do_schedule());
326 }
327
emit_wait_locked(const ArgVal & Dest)328 void BeamModuleAssembler::emit_wait_locked(const ArgVal &Dest) {
329 emit_enter_runtime();
330
331 a.mov(ARG1, c_p);
332 a.lea(ARG2, x86::qword_ptr(labels[Dest.getValue()]));
333 runtime_call<2>(beam_jit_wait_locked);
334
335 emit_leave_runtime();
336
337 abs_jmp(ga->get_do_schedule());
338 }
339
emit_wait_timeout_unlocked(const ArgVal & Src,const ArgVal & Dest)340 void BeamModuleAssembler::emit_wait_timeout_unlocked(const ArgVal &Src,
341 const ArgVal &Dest) {
342 emit_enter_runtime();
343
344 a.mov(ARG1, c_p);
345 runtime_call<1>(beam_jit_take_receive_lock);
346
347 emit_leave_runtime();
348
349 emit_wait_timeout_locked(Src, Dest);
350 }
351
emit_wait_timeout_locked(const ArgVal & Src,const ArgVal & Dest)352 void BeamModuleAssembler::emit_wait_timeout_locked(const ArgVal &Src,
353 const ArgVal &Dest) {
354 Label wait = a.newLabel(), next = a.newLabel();
355
356 mov_arg(ARG2, Src);
357
358 emit_enter_runtime();
359
360 a.mov(ARG1, c_p);
361 a.lea(ARG3, x86::qword_ptr(next));
362 runtime_call<3>(beam_jit_wait_timeout);
363
364 emit_leave_runtime();
365
366 ERTS_CT_ASSERT(RET_next < RET_wait && RET_wait < RET_badarg);
367 a.cmp(RET, RET_wait);
368 a.short_().je(wait);
369 #ifdef JIT_HARD_DEBUG
370 a.jl(next);
371 #else
372 a.short_().jl(next);
373 #endif
374
375 emit_handle_error(currLabel, (ErtsCodeMFA *)nullptr);
376
377 a.bind(wait);
378 emit_wait_locked(Dest);
379
380 align_erlang_cp();
381 a.bind(next);
382 }
383
emit_timeout_locked()384 void BeamModuleAssembler::emit_timeout_locked() {
385 emit_enter_runtime();
386
387 a.mov(ARG1, c_p);
388 runtime_call<1>(beam_jit_timeout_locked);
389
390 emit_leave_runtime();
391 }
392
emit_timeout()393 void BeamModuleAssembler::emit_timeout() {
394 emit_enter_runtime();
395
396 a.mov(ARG1, c_p);
397 runtime_call<1>(beam_jit_timeout);
398
399 emit_leave_runtime();
400 }
401