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