1 /*
2  * %CopyrightBegin%
3  *
4  * Copyright Ericsson AB 2004-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  * GC support procedures
22  */
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 
27 #define ERL_WANT_GC_INTERNALS__
28 
29 #include "global.h"
30 
31 #include "erl_gc.h"
32 
33 #include "hipe_stack.h"
34 #include "hipe_gc.h"
35 
fullsweep_nstack(Process * p,Eterm * n_htop)36 Eterm *fullsweep_nstack(Process *p, Eterm *n_htop)
37 {
38     /* known nstack walk state */
39     Eterm *nsp;
40     Eterm *nsp_end;
41     const struct hipe_sdesc *sdesc;
42     unsigned int sdesc_size;
43     unsigned long ra;
44     unsigned int i;
45     unsigned int mask;
46     /* arch-specific nstack walk state */
47     struct nstack_walk_state walk_state;
48 
49     ASSERT(!p->hipe.gc_is_unsafe);
50 
51     if (!p->hipe.nstack) {
52 	ASSERT(!p->hipe.nsp && !p->hipe.nstend);
53 	return n_htop;
54     }
55     if (!nstack_walk_init_check(p))
56 	return n_htop;
57 
58     ASSERT(p->hipe.nsp && p->hipe.nstend);
59     nsp = nstack_walk_nsp_begin(p);
60     nsp_end = p->hipe.nstgraylim;
61     if (nsp_end)
62 	nstack_walk_kill_trap(p, nsp_end);
63     nsp_end = nstack_walk_nsp_end(p);
64 
65     sdesc = nstack_walk_init_sdesc(p, &walk_state);
66 
67     for (;;) {
68 	if (nstack_walk_nsp_reached_end(nsp, nsp_end)) {
69 	    if (nsp == nsp_end) {
70 		if (nsp) {
71 		    /* see the HIGH_WATER update in fullsweep_heap() */
72 		    p->hipe.nstblacklim = nsp; /* nsp == nsp_end */
73 		    nstack_walk_update_trap(p, walk_state.sdesc0);
74 		}
75 		return n_htop;
76 	    }
77 	    fprintf(stderr, "%s: passed end of stack\r\n", __FUNCTION__);
78 	    break;
79 	}
80 	sdesc_size = nstack_walk_frame_size(sdesc);
81 	i = 0;
82 	mask = sdesc->livebits[0];
83 	for (;;) {
84 	    if (mask & 1) {
85 		Eterm *nsp_i = nstack_walk_frame_index(nsp, i);
86 		Eterm gval = *nsp_i;
87 		if (is_boxed(gval)) {
88 		    Eterm *ptr = boxed_val(gval);
89 		    Eterm val = *ptr;
90 		    if (IS_MOVED_BOXED(val)) {
91 			ASSERT(is_boxed(val));
92 			*nsp_i = val;
93 		    } else if (!erts_is_literal(gval, ptr)) {
94 			move_boxed(ptr, val, &n_htop, nsp_i);
95 		    }
96 		} else if (is_list(gval)) {
97 		    Eterm *ptr = list_val(gval);
98 		    Eterm val = *ptr;
99 		    if (IS_MOVED_CONS(val)) {
100 			*nsp_i = ptr[1];
101 		    } else if (!erts_is_literal(gval, ptr)) {
102 			ASSERT(erts_dbg_within_proc(ptr, p, NULL));
103 			move_cons(ptr, val, &n_htop, nsp_i);
104 		    }
105 		}
106 	    }
107 	    if (++i >= sdesc_size)
108 		break;
109 	    if (i & 31)
110 		mask >>= 1;
111 	    else
112 		mask = sdesc->livebits[i >> 5];
113 	}
114 	ra = nstack_walk_frame_ra(nsp, sdesc);
115 	sdesc = hipe_find_sdesc(ra);
116 	nsp = nstack_walk_next_frame(nsp, sdesc_size);
117     }
118     abort();
119 }
120 
gensweep_nstack(Process * p,Eterm ** ptr_old_htop,Eterm ** ptr_n_htop)121 void gensweep_nstack(Process *p, Eterm **ptr_old_htop, Eterm **ptr_n_htop)
122 {
123     /* known nstack walk state */
124     Eterm *nsp;
125     Eterm *nsp_end;
126     const struct hipe_sdesc *sdesc;
127     unsigned int sdesc_size;
128     unsigned long ra;
129     unsigned int i;
130     unsigned int mask;
131     /* arch-specific nstack walk state */
132     struct nstack_walk_state walk_state;
133     char *oh;
134     Uint oh_size;
135 
136     /* gensweep-specific state */
137     Eterm *old_htop, *n_htop;
138     char *mature;
139     Uint mature_size;
140 
141     ASSERT(!p->hipe.gc_is_unsafe);
142 
143     if (!p->hipe.nstack) {
144 	ASSERT(!p->hipe.nsp && !p->hipe.nstend);
145 	return;
146     }
147     if (!nstack_walk_init_check(p))
148 	return;
149 
150     ASSERT(p->hipe.nsp && p->hipe.nstend);
151     nsp = nstack_walk_nsp_begin(p);
152     nsp_end = p->hipe.nstgraylim;
153     if (nsp_end) {
154 	/* if gray limit passed black limit, reset black limit */
155 	if (nstack_walk_gray_passed_black(nsp_end, p->hipe.nstblacklim))
156 	    p->hipe.nstblacklim = nsp_end;
157 	nstack_walk_kill_trap(p, nsp_end);
158 	nsp_end = p->hipe.nstblacklim;
159     } else
160 	nsp_end = nstack_walk_nsp_end(p);
161 
162     sdesc = nstack_walk_init_sdesc(p, &walk_state);
163 
164     old_htop = *ptr_old_htop;
165     n_htop = *ptr_n_htop;
166     mature = (char *) (p->abandoned_heap ? p->abandoned_heap : p->heap);
167     mature_size = (char*)HIGH_WATER(p) - mature;
168     oh = (char*)OLD_HEAP(p);
169     oh_size = (char*)OLD_HTOP(p) - oh;
170 
171     for (;;) {
172 	if (nstack_walk_nsp_reached_end(nsp, nsp_end)) {
173 	    if (nsp == nsp_end) {
174 		*ptr_old_htop = old_htop;
175 		*ptr_n_htop = n_htop;
176 		if (nsp) {
177 		    /* see the HIGH_WATER update in gen_gc() */
178 		    if (HEAP_START(p) != HIGH_WATER(p)) {
179 			p->hipe.nstblacklim =
180 			    p->hipe.nstgraylim
181 			    ? p->hipe.nstgraylim
182 			    : nsp; /* nsp == nsp_end */
183 		    } else {
184 			/* blacklim = graylim ? blacklim : end */
185 			if (!p->hipe.nstgraylim)
186 			    p->hipe.nstblacklim = nsp; /* nsp == nsp_end */
187 		    }
188 		    nstack_walk_update_trap(p, walk_state.sdesc0);
189 		}
190 		return;
191 	    }
192 	    fprintf(stderr, "%s: passed end of stack\r\n", __FUNCTION__);
193 	    break;
194 	}
195 	sdesc_size = nstack_walk_frame_size(sdesc);
196 	i = 0;
197 	mask = sdesc->livebits[0];
198 	for (;;) {
199 	    if (mask & 1) {
200 		Eterm *nsp_i = nstack_walk_frame_index(nsp, i);
201 		Eterm gval = *nsp_i;
202 		if (is_boxed(gval)) {
203 		    Eterm *ptr = boxed_val(gval);
204 		    Eterm val = *ptr;
205 		    if (IS_MOVED_BOXED(val)) {
206 			ASSERT(is_boxed(val));
207 			*nsp_i = val;
208 		    } else if (ErtsInArea(ptr, mature, mature_size)) {
209 			move_boxed(ptr, val, &old_htop, nsp_i);
210 		    } else if (ErtsInYoungGen(gval, ptr, oh, oh_size)) {
211 			ASSERT(erts_dbg_within_proc(ptr, p, NULL));
212 			move_boxed(ptr, val, &n_htop, nsp_i);
213 		    }
214 		} else if (is_list(gval)) {
215 		    Eterm *ptr = list_val(gval);
216 		    Eterm val = *ptr;
217 		    if (IS_MOVED_CONS(val)) {
218 			*nsp_i = ptr[1];
219 		    } else if (ErtsInArea(ptr, mature, mature_size)) {
220 			move_cons(ptr, val, &old_htop, nsp_i);
221 		    } else if (ErtsInYoungGen(gval, ptr, oh, oh_size)) {
222 			ASSERT(erts_dbg_within_proc(ptr, p, NULL));
223 			move_cons(ptr, val, &n_htop, nsp_i);
224 		    }
225 		}
226 	    }
227 	    if (++i >= sdesc_size)
228 		break;
229 	    if (i & 31)
230 		mask >>= 1;
231 	    else
232 		mask = sdesc->livebits[i >> 5];
233 	}
234 	ra = nstack_walk_frame_ra(nsp, sdesc);
235 	sdesc = hipe_find_sdesc(ra);
236 	nsp = nstack_walk_next_frame(nsp, sdesc_size);
237     }
238     abort();
239 }
240 
sweep_literals_nstack(Process * p,Eterm * old_htop,char * area,Uint area_size)241 Eterm *sweep_literals_nstack(Process *p, Eterm *old_htop, char *area,
242 			     Uint area_size)
243 {
244     /* known nstack walk state */
245     Eterm *nsp;
246     Eterm *nsp_end;
247     const struct hipe_sdesc *sdesc;
248     /* arch-specific nstack walk state */
249     struct nstack_walk_state walk_state;
250 
251     ASSERT(!p->hipe.gc_is_unsafe);
252 
253     if (!p->hipe.nstack) {
254 	ASSERT(!p->hipe.nsp && !p->hipe.nstend);
255 	return old_htop;
256     }
257     if (!nstack_walk_init_check(p))
258 	return old_htop;
259 
260     ASSERT(p->hipe.nsp && p->hipe.nstend);
261     nsp = nstack_walk_nsp_begin(p);
262     nsp_end = nstack_walk_nsp_end(p);
263     sdesc = nstack_walk_init_sdesc_ignore_trap(p, &walk_state);
264 
265     while (!nstack_walk_nsp_reached_end(nsp, nsp_end)) {
266 	unsigned long ra;
267 	unsigned sdesc_size = nstack_walk_frame_size(sdesc);
268 	unsigned i = 0;
269 	unsigned mask = sdesc->livebits[0];
270 	for (;;) {
271 	    if (mask & 1) {
272 		Eterm *nsp_i = nstack_walk_frame_index(nsp, i);
273 		Eterm gval = *nsp_i;
274 		if (is_boxed(gval)) {
275 		    Eterm *ptr = boxed_val(gval);
276 		    Eterm val = *ptr;
277 		    if (IS_MOVED_BOXED(val)) {
278 			ASSERT(is_boxed(val));
279 			*nsp_i = val;
280 		    } else if (ErtsInArea(ptr, area, area_size)) {
281 			move_boxed(ptr, val, &old_htop, nsp_i);
282 		    }
283 		} else if (is_list(gval)) {
284 		    Eterm *ptr = list_val(gval);
285 		    Eterm val = *ptr;
286 		    if (IS_MOVED_CONS(val)) {
287 			*nsp_i = ptr[1];
288 		    } else if (ErtsInArea(ptr, area, area_size)) {
289 			move_cons(ptr, val, &old_htop, nsp_i);
290 		    }
291 		}
292 	    }
293 	    if (++i >= sdesc_size)
294 		break;
295 	    if (i & 31)
296 		mask >>= 1;
297 	    else
298 		mask = sdesc->livebits[i >> 5];
299 	}
300 	ra = nstack_walk_frame_ra(nsp, sdesc);
301 	if (ra == (unsigned long)nbif_stack_trap_ra)
302 	    ra = (unsigned long)p->hipe.ngra;
303 	sdesc = hipe_find_sdesc(ra);
304 	nsp = nstack_walk_next_frame(nsp, sdesc_size);
305     }
306     return old_htop;
307 }
308 
309 int
nstack_any_heap_ref_ptrs(Process * rp,char * mod_start,Uint mod_size)310 nstack_any_heap_ref_ptrs(Process *rp, char* mod_start, Uint mod_size)
311 {
312     Eterm *nsp;
313     Eterm *nsp_end;
314     const struct hipe_sdesc *sdesc;
315     /* arch-specific nstack walk state */
316     struct nstack_walk_state walk_state;
317 
318     ASSERT(!rp->hipe.gc_is_unsafe);
319 
320     if (!rp->hipe.nstack || !nstack_walk_init_check(rp)) return 0;
321     ASSERT(rp->hipe.nsp && rp->hipe.nstend);
322     nsp = nstack_walk_nsp_begin(rp);
323     nsp_end = nstack_walk_nsp_end(rp);
324     sdesc = nstack_walk_init_sdesc_ignore_trap(rp, &walk_state);
325 
326     while (!nstack_walk_nsp_reached_end(nsp, nsp_end)) {
327 	unsigned long ra;
328 	unsigned sdesc_size = nstack_walk_frame_size(sdesc);
329 	unsigned i = 0;
330 	unsigned mask = sdesc->livebits[0];
331 	for (;;) {
332 	    if (mask & 1) {
333 		Eterm *nsp_i = nstack_walk_frame_index(nsp, i);
334 		Eterm val = *nsp_i;
335 		switch (primary_tag(val)) {
336 		case TAG_PRIMARY_BOXED:
337 		case TAG_PRIMARY_LIST:
338 		    if (ErtsInArea(val, mod_start, mod_size)) {
339 			return 1;
340 		    }
341 		    break;
342 		}
343 	    }
344 	    if (++i >= sdesc_size)
345 		break;
346 	    if (i & 31)
347 		mask >>= 1;
348 	    else
349 		mask = sdesc->livebits[i >> 5];
350 	}
351 	ra = nstack_walk_frame_ra(nsp, sdesc);
352 	if (ra == (unsigned long)nbif_stack_trap_ra)
353 	    ra = (unsigned long)rp->hipe.ngra;
354 	sdesc = hipe_find_sdesc(ra);
355 	nsp = nstack_walk_next_frame(nsp, sdesc_size);
356     }
357     return 0;
358 }
359 
360 int
nstack_any_cps_in_segment(Process * p,char * seg_start,Uint seg_size)361 nstack_any_cps_in_segment(Process *p, char* seg_start, Uint seg_size)
362 {
363     Eterm *nsp;
364     Eterm *nsp_end;
365     const struct hipe_sdesc *sdesc;
366     /* arch-specific nstack walk state */
367     struct nstack_walk_state walk_state;
368 
369     if (!p->hipe.nstack || !nstack_walk_init_check(p))
370 	return 0;
371     ASSERT(p->hipe.nsp && p->hipe.nstend);
372     nsp = nstack_walk_nsp_begin(p);
373     nsp_end = nstack_walk_nsp_end(p);
374     sdesc = nstack_walk_init_sdesc_ignore_trap(p, &walk_state);
375 
376     /* Check the topmost frame */
377     if (ErtsInArea(sdesc->bucket.hvalue, seg_start, seg_size))
378 	return 1;
379 
380     while (!nstack_walk_nsp_reached_end(nsp, nsp_end)) {
381 	unsigned sdesc_size = nstack_walk_frame_size(sdesc);
382 	unsigned long ra = nstack_walk_frame_ra(nsp, sdesc);
383 	if (ra == (unsigned long)nbif_stack_trap_ra)
384 	    ra = (unsigned long)p->hipe.ngra;
385 	if (ErtsInArea(ra, seg_start, seg_size))
386             return 1;
387         sdesc = hipe_find_sdesc(ra);
388         nsp = nstack_walk_next_frame(nsp, sdesc_size);
389     }
390     return 0;
391 }
392