1 /*
2  * %CopyrightBegin%
3  *
4  * Copyright Ericsson AB 2016-2018. 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 #ifdef HAVE_CONFIG_H
22 #  include "config.h"
23 #endif
24 
25 #define ERTS_WANT_NFUNC_SCHED_INTERNALS__
26 
27 #include "global.h"
28 #include "erl_process.h"
29 #include "bif.h"
30 #include "erl_nfunc_sched.h"
31 #include "erl_trace.h"
32 #include "jit/beam_asm.h"
33 
34 ErtsNativeFunc *
erts_new_proc_nfunc(Process * c_p,int argc)35 erts_new_proc_nfunc(Process *c_p, int argc)
36 {
37     ErtsNativeFunc *nep, *old_nep;
38     size_t size;
39 
40     size = sizeof(ErtsNativeFunc) + (argc-1)*sizeof(Eterm);
41     nep = erts_alloc(ERTS_ALC_T_NFUNC_TRAP_WRAPPER, size);
42 
43     nep->argc = -1; /* unused marker */
44     nep->argv_size = argc;
45     old_nep = ERTS_PROC_SET_NFUNC_TRAP_WRAPPER(c_p, nep);
46     if (old_nep) {
47 	erts_free(ERTS_ALC_T_NFUNC_TRAP_WRAPPER, old_nep);
48     }
49     return nep;
50 }
51 
52 void
erts_destroy_nfunc(Process * p)53 erts_destroy_nfunc(Process *p)
54 {
55     ErtsNativeFunc *nep = ERTS_PROC_SET_NFUNC_TRAP_WRAPPER(p, NULL);
56     if (nep) {
57 	if (nep->m)
58 	    erts_nfunc_cleanup_nif_mod(nep);
59 	erts_free(ERTS_ALC_T_NFUNC_TRAP_WRAPPER, nep);
60     }
61 }
62 
63 ErtsNativeFunc *
erts_nfunc_schedule(Process * c_p,Process * dirty_shadow_proc,const ErtsCodeMFA * mfa,ErtsCodePtr pc,BeamInstr instr,void * dfunc,void * ifunc,Eterm mod,Eterm func,int argc,const Eterm * argv)64 erts_nfunc_schedule(Process *c_p, Process *dirty_shadow_proc,
65 			 const ErtsCodeMFA *mfa, ErtsCodePtr pc,
66 			 BeamInstr instr,
67 			 void *dfunc, void *ifunc,
68 			 Eterm mod, Eterm func,
69 			 int argc, const Eterm *argv)
70 {
71     Process *used_proc;
72     ErtsSchedulerData *esdp;
73     Eterm* reg;
74     ErtsNativeFunc* nep;
75     int i;
76 
77     ERTS_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p)
78 		       & ERTS_PROC_LOCK_MAIN);
79 
80     if (dirty_shadow_proc) {
81 	esdp = erts_get_scheduler_data();
82 	ASSERT(esdp && ERTS_SCHEDULER_IS_DIRTY(esdp));
83 
84 	used_proc = dirty_shadow_proc;
85     }
86     else {
87 	esdp = erts_proc_sched_data(c_p);
88 	ASSERT(esdp && !ERTS_SCHEDULER_IS_DIRTY(esdp));
89 
90 	used_proc = c_p;
91 	ERTS_VBUMP_ALL_REDS(c_p);
92     }
93 
94     reg = esdp->registers->x_reg_array.d;
95 
96     if (mfa)
97 	nep = erts_get_proc_nfunc(c_p, (int) mfa->arity);
98     else {
99 	/* If no mfa, this is not the first schedule... */
100 	nep = ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(c_p);
101 	ASSERT(nep && nep->argc >= 0);
102     }
103 
104     if (nep->argc < 0) {
105 	/*
106 	 * First schedule; save things that might
107 	 * need to be restored...
108 	 */
109 	for (i = 0; i < (int) mfa->arity; i++)
110 	    nep->argv[i] = reg[i];
111 	nep->pc = pc;
112 	nep->mfa = mfa;
113 	nep->current = c_p->current;
114 	ASSERT(argc >= 0);
115 	nep->argc = (int) mfa->arity;
116 	nep->m = NULL;
117 
118 	ASSERT(!erts_check_nfunc_in_area(c_p,
119 					      (char *) nep,
120 					      (sizeof(ErtsNativeFunc)
121 					       + (sizeof(Eterm)
122 						  *(nep->argc-1)))));
123     }
124     /* Copy new arguments into register array if necessary... */
125     if (reg != argv) {
126 	for (i = 0; i < argc; i++)
127 	    reg[i] = argv[i];
128     }
129     ASSERT(is_atom(mod) && is_atom(func));
130     nep->trampoline.info.mfa.module = mod;
131     nep->trampoline.info.mfa.function = func;
132     nep->trampoline.info.mfa.arity = (Uint) argc;
133 #ifdef BEAMASM
134     nep->trampoline.trace[0] = (BeamInstr) instr; /* call_bif || call_nif */
135 #endif
136     nep->trampoline.call_op = (BeamInstr) instr; /* call_bif || call_nif */
137     nep->trampoline.dfunc = (BeamInstr) dfunc;
138     nep->func = ifunc;
139     used_proc->arity = argc;
140     used_proc->freason = TRAP;
141 #ifndef BEAMASM
142     used_proc->i = (ErtsCodePtr)&nep->trampoline.call_op;
143 #else
144     ERTS_CT_ASSERT(sizeof(nep->trampoline.trace) == BEAM_ASM_FUNC_PROLOGUE_SIZE);
145     used_proc->i = (ErtsCodePtr)&nep->trampoline.trace;
146     ASSERT_MFA(erts_code_to_codemfa(used_proc->i));
147 #endif
148     return nep;
149 }
150