source: trunk/source/lisp-kernel/image.c @ 15366

Last change on this file since 15366 was 15366, checked in by gb, 7 years ago

Change the write-barrier implementation on x86:
managed_static_area->refbits tracks references to (potential)
ephemeral objects and is traversed by the EGC; the new global
managed_static_refbits tracks references from the managed_static_area to
(potential) dynamic objects and is traversed by full GC.

To support this:

  • add a couple of new kernel globals (actually, conditionally redefine a few PPC-specific things) to keep track of the number of dnodes in the managed static area and the new managed_static_bitvector. (We might want to just add new globals and leave the PPC-specific things there; the managed_static_area is currently X86-specific, but we might want to use it on other architectures.)
  • change the image loading/saving code to restore/save managed_static_refbits
  • change purify/impurify on x86 to maintain both bitvectors
  • change the GC to traverse both bitvectors (in different situations)
  • initialize and maintain the new kernel globals
  • provide a way of reserving address space for reasons other than heap initialization
  • implement the new write barrier in the affected x86 subprimitives
  • maintain both bitvectors in pc_luser_xp
  • other incidental changes.

This seems to work reliably on X8664 Linux. Some stuff needs to be tested
on Win64 and the X8632 write-barrier code probably needs lots of testing
(that machine hardly has any registers ...). Other platforms shouldn't
be affected, but we'll see.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.6 KB
Line 
1/*
2   Copyright (C) 2002-2009 Clozure Associates
3   This file is part of Clozure CL. 
4
5   Clozure CL is licensed under the terms of the Lisp Lesser GNU Public
6   License , known as the LLGPL and distributed with Clozure CL as the
7   file "LICENSE".  The LLGPL consists of a preamble and the LGPL,
8   which is distributed with Clozure CL as the file "LGPL".  Where these
9   conflict, the preamble takes precedence. 
10
11   Clozure CL is referenced in the preamble as the "LIBRARY."
12
13   The LLGPL is also available online at
14   http://opensource.franz.com/preamble.html
15*/
16
17#include "lisp.h"
18#include "lisp_globals.h"
19#include "area.h"
20#include "image.h"
21#include "gc.h"
22#include <errno.h>
23#include <unistd.h>
24#ifndef WINDOWS
25#include <sys/mman.h>
26#endif
27#include <stdio.h>
28#include <limits.h>
29#include <time.h>
30
31
32#if defined(PPC64) || defined(X8632)
33#define RELOCATABLE_FULLTAG_MASK \
34  ((1<<fulltag_cons)|(1<<fulltag_misc))
35#else
36#ifdef X8664
37#define RELOCATABLE_FULLTAG_MASK \
38  ((1<<fulltag_cons)|(1<<fulltag_misc)|(1<<fulltag_symbol)|(1<<fulltag_function))
39#else
40#define RELOCATABLE_FULLTAG_MASK \
41  ((1<<fulltag_cons)|(1<<fulltag_nil)|(1<<fulltag_misc))
42#endif
43#endif
44
45void
46relocate_area_contents(area *a, LispObj bias)
47{
48  LispObj
49    *start = (LispObj *)(a->low), 
50    *end = (LispObj *)(a->active),
51    low = (LispObj)image_base - bias,
52    high = ptr_to_lispobj(active_dynamic_area->active) - bias,
53    w0, w1;
54  int fulltag;
55  Boolean fixnum_after_header_is_link = false;
56
57  while (start < end) {
58    w0 = *start;
59    fulltag = fulltag_of(w0);
60    if (immheader_tag_p(fulltag)) {
61      start = (LispObj *)skip_over_ivector((natural)start, w0);
62    } else {
63#ifdef X86
64      if (header_subtag(w0) == subtag_function) {
65#ifdef X8664
66        int skip = ((int) start[1])+1;
67#else
68        extern void update_self_references(LispObj *);
69        extern natural imm_word_count(LispObj);
70
71        natural skip = (natural)imm_word_count(((LispObj)start)+fulltag_misc)+1;
72        update_self_references(start);
73#endif
74     
75        start += skip;
76        if (((LispObj) start) & node_size) {
77          --start;
78        }
79        w0 = *start;
80        fulltag = fulltag_of(w0);
81      }
82#endif
83#ifdef ARM
84      if ((header_subtag(w0) == subtag_function) ||
85          (header_subtag(w0) == subtag_pseudofunction)) {
86        w1 = start[1];
87        if ((w1 >= low) && (w1 < high)) {
88          start[1]=(w1+bias);
89        }
90        start+=2;
91        w0 = *start;
92        fulltag = fulltag_of(w0);
93      }
94#endif
95      if (header_subtag(w0) == subtag_weak) {
96        fixnum_after_header_is_link = true;
97      }
98      if (header_subtag(w0) == subtag_hash_vector) {
99        hash_table_vector_header *hashp = (hash_table_vector_header *)start;
100       
101        if (hashp->flags & nhash_track_keys_mask) {
102          hashp->flags |= nhash_key_moved_mask;
103        }
104        fixnum_after_header_is_link = true;
105      }
106
107      if ((w0 >= low) && (w0 < high) &&
108          ((1<<fulltag) & RELOCATABLE_FULLTAG_MASK)) {
109        *start = (w0+bias);
110      }
111      w1 = *++start;
112      fulltag = fulltag_of(w1);
113      if ((w1 >= low) && (w1 < high) &&
114          (fixnum_after_header_is_link ||
115           ((1<<fulltag) & RELOCATABLE_FULLTAG_MASK))) {
116        *start = (w1+bias);
117      }
118      fixnum_after_header_is_link = false;
119      ++start;
120    }
121  }
122  if (start > end) {
123    Bug(NULL, "Overran area bounds in relocate_area_contents");
124  }
125}
126     
127
128
129
130off_t
131seek_to_next_page(int fd)
132{
133  off_t pos = LSEEK(fd, 0, SEEK_CUR);
134  pos = align_to_power_of_2(pos, log2_page_size);
135  return LSEEK(fd, pos, SEEK_SET);
136}
137 
138/*
139  fd is positioned to EOF; header has been allocated by caller.
140  If we find a trailer (and that leads us to the header), read
141  the header & return true else return false.
142*/
143Boolean
144find_openmcl_image_file_header(int fd, openmcl_image_file_header *header)
145{
146  openmcl_image_file_trailer trailer;
147  int disp;
148  off_t pos;
149  unsigned version, flags;
150
151  pos = LSEEK(fd, 0, SEEK_END);
152  if (pos < 0) {
153    return false;
154  }
155  pos -= sizeof(trailer);
156
157  if (LSEEK(fd, pos, SEEK_SET) < 0) {
158    return false;
159  }
160  if (read(fd, &trailer, sizeof(trailer)) != sizeof(trailer)) {
161    return false;
162  }
163  if ((trailer.sig0 != IMAGE_SIG0) ||
164      (trailer.sig1 != IMAGE_SIG1) ||
165      (trailer.sig2 != IMAGE_SIG2)) {
166    return false;
167  }
168  disp = trailer.delta;
169 
170  if (disp >= 0) {
171    return false;
172  }
173  if (LSEEK(fd, disp, SEEK_CUR) < 0) {
174    return false;
175  }
176  if (read(fd, header, sizeof(openmcl_image_file_header)) !=
177      sizeof(openmcl_image_file_header)) {
178    return false;
179  }
180  if ((header->sig0 != IMAGE_SIG0) ||
181      (header->sig1 != IMAGE_SIG1) ||
182      (header->sig2 != IMAGE_SIG2) ||
183      (header->sig3 != IMAGE_SIG3)) {
184    return false;
185  }
186  version = (header->abi_version) & 0xffff;
187  if (version < ABI_VERSION_MIN) {
188    fprintf(dbgout, "Heap image is too old for this kernel.\n");
189    return false;
190  }
191  if (version > ABI_VERSION_MAX) {
192    fprintf(dbgout, "Heap image is too new for this kernel.\n");
193    return false;
194  }
195  flags = header->flags;
196  if (flags != PLATFORM) {
197    fprintf(dbgout, "Heap image was saved for another platform.\n");
198    return false;
199  }
200  return true;
201}
202
203void
204load_image_section(int fd, openmcl_image_section_header *sect)
205{
206  extern area* allocate_dynamic_area(natural);
207  off_t
208    pos = seek_to_next_page(fd), advance;
209  natural
210    mem_size = sect->memory_size;
211  char *addr;
212  area *a;
213
214  advance = mem_size;
215  switch(sect->code) {
216  case AREA_READONLY:
217    if (mem_size != 0) {
218      if (!MapFile(pure_space_active,
219                   pos,
220                   align_to_power_of_2(mem_size,log2_page_size),
221                   MEMPROTECT_RX,
222                   fd)) {
223        return;
224      }
225    }
226    a = new_area(pure_space_active, pure_space_limit, AREA_READONLY);
227    pure_space_active += mem_size;
228    a->active = pure_space_active;
229    sect->area = a;     
230    break;
231
232  case AREA_STATIC:
233    if (!MapFile(static_space_active,
234                 pos,
235                 align_to_power_of_2(mem_size,log2_page_size),
236                 MEMPROTECT_RWX,
237                 fd)) {
238      return;
239    }
240    a = new_area(static_space_active, static_space_limit, AREA_STATIC);
241    static_space_active += mem_size;
242    a->active = static_space_active;
243    sect->area = a;
244    break;
245
246  case AREA_DYNAMIC:
247    a = allocate_dynamic_area(mem_size);
248    if (!MapFile(a->low,
249                 pos,
250                 align_to_power_of_2(mem_size,log2_page_size),
251                 MEMPROTECT_RWX,
252                 fd)) {
253      return;
254    }
255
256    a->static_dnodes = sect->static_dnodes;
257    sect->area = a;
258    break;
259
260  case AREA_MANAGED_STATIC:
261    a = new_area(pure_space_limit, pure_space_limit+align_to_power_of_2(mem_size,log2_page_size), AREA_MANAGED_STATIC);
262    a->active = a->low+mem_size;
263    if (mem_size) {
264      natural
265        refbits_size = align_to_power_of_2((((mem_size>>dnode_shift)+7)>>3),
266                                           log2_page_size);
267      if (!MapFile(a->low,
268                   pos,
269                   align_to_power_of_2(mem_size,log2_page_size),
270                   MEMPROTECT_RWX,
271                   fd)) {
272        return;
273      }
274      if (!CommitMemory(global_mark_ref_bits,refbits_size)) {
275        return;
276      }
277      /* Need to save/restore persistent refbits. */
278      if (!MapFile(managed_static_refbits,
279                   align_to_power_of_2(pos+mem_size,log2_page_size),
280                   refbits_size,
281                   MEMPROTECT_RW,
282                   fd)) {
283        return;
284      }
285      advance += refbits_size;
286    }
287    sect->area = a;
288    a->ndnodes = area_dnode(a->active, a->low);
289    managed_static_area = a;
290    lisp_global(REF_BASE) = (LispObj) a->low;
291    break;
292
293    /* In many respects, the static_cons_area is part of the dynamic
294       area; it's physically adjacent to it (immediately precedes the
295       dynamic area in memory) and its contents are subject to full
296       GC (but not compaction.)  It's maintained as a seperate section
297       in the image file, at least for now. */
298
299
300  case AREA_STATIC_CONS:
301    addr = (char *) lisp_global(HEAP_START);
302    a = new_area(addr-align_to_power_of_2(mem_size,log2_page_size), addr, AREA_STATIC_CONS);
303    if (mem_size) {     
304      if (!MapFile(a->low,
305                   pos,
306                   align_to_power_of_2(mem_size,log2_page_size),
307                   MEMPROTECT_RWX,
308                   fd)) {
309        return;
310      }
311    }
312    a->ndnodes = area_dnode(a->active, a->low);
313    sect->area = a;
314    static_cons_area = a;
315    break;
316
317  default:
318    return;
319   
320  }
321  LSEEK(fd, pos+advance, SEEK_SET);
322}
323
324
325LispObj
326load_openmcl_image(int fd, openmcl_image_file_header *h)
327{
328  LispObj image_nil = 0;
329  area *a;
330  if (find_openmcl_image_file_header(fd, h)) {
331    int i, nsections = h->nsections;
332    openmcl_image_section_header sections[nsections], *sect=sections;
333    LispObj bias = image_base - ACTUAL_IMAGE_BASE(h);
334#if (WORD_SIZE== 64)
335    signed_natural section_data_delta = 
336      ((signed_natural)(h->section_data_offset_high) << 32L) | h->section_data_offset_low;
337#endif
338
339    if (read (fd, sections, nsections*sizeof(openmcl_image_section_header)) !=
340        nsections * sizeof(openmcl_image_section_header)) {
341      return 0;
342    }
343#if WORD_SIZE == 64
344    LSEEK(fd, section_data_delta, SEEK_CUR);
345#endif
346    for (i = 0; i < nsections; i++, sect++) {
347      load_image_section(fd, sect);
348      a = sect->area;
349      if (a == NULL) {
350        return 0;
351      }
352    }
353
354    for (i = 0, sect = sections; i < nsections; i++, sect++) {
355      a = sect->area;
356      switch(sect->code) {
357      case AREA_STATIC:
358        nilreg_area = a;
359#ifdef PPC
360#ifdef PPC64
361        image_nil = ptr_to_lispobj(a->low + (1024*4) + sizeof(lispsymbol) + fulltag_misc);
362#else
363        image_nil = (LispObj)(a->low + 8 + 8 + (1024*4) + fulltag_nil);
364#endif
365#endif
366#ifdef X86
367#ifdef X8664
368        image_nil = (LispObj)(a->low) + (1024*4) + fulltag_nil;
369#else
370        image_nil = (LispObj)(a->low) + (1024*4) + fulltag_cons;
371#endif
372#endif
373#ifdef ARM
374        image_nil = (LispObj)(a->low) + (1024*4) + fulltag_nil;
375#endif
376        set_nil(image_nil);
377        if (bias) {
378          LispObj weakvll = lisp_global(WEAKVLL);
379
380          if ((weakvll >= ((LispObj)image_base-bias)) &&
381              (weakvll < (ptr_to_lispobj(active_dynamic_area->active)-bias))) {
382            lisp_global(WEAKVLL) = weakvll+bias;
383          }
384          relocate_area_contents(a, bias);
385        }
386        make_dynamic_heap_executable(a->low, a->active);
387        add_area_holding_area_lock(a);
388        break;
389       
390      case AREA_READONLY:
391        if (bias && 
392            (managed_static_area->active != managed_static_area->low)) {
393          UnProtectMemory(a->low, a->active-a->low);
394          relocate_area_contents(a, bias);
395          ProtectMemory(a->low, a->active-a->low);
396        }
397        readonly_area = a;
398        add_area_holding_area_lock(a);
399        break;
400      }
401    }
402    for (i = 0, sect = sections; i < nsections; i++, sect++) {
403      a = sect->area;
404      switch(sect->code) {
405      case AREA_MANAGED_STATIC:
406        if (bias) {
407          relocate_area_contents(a, bias);
408        }
409        add_area_holding_area_lock(a);
410        break;
411      case AREA_STATIC_CONS:
412        break;
413      case AREA_DYNAMIC:
414        lower_heap_start(static_cons_area->low,a);
415        if (bias) {
416          relocate_area_contents(a, bias);
417        }
418        resize_dynamic_heap(a->active, lisp_heap_gc_threshold);
419        xMakeDataExecutable(a->low, a->active - a->low);
420        break;
421      }
422    }
423  }
424  return image_nil;
425}
426 
427void
428prepare_to_write_dynamic_space(area *a)
429{
430  LispObj
431    *start = (LispObj *)(a->low),
432    *end = (LispObj *) (a->active),
433    x1;
434  int tag, subtag, element_count;
435
436  while (start < end) {
437    x1 = *start;
438    tag = fulltag_of(x1);
439    if (immheader_tag_p(tag)) {
440      subtag = header_subtag(x1);
441      if (subtag == subtag_macptr) {
442        if ((start[1] >= (natural)0x10000) && (start[1] < (natural)-0x10000)) {
443          /* Leave small pointers alone */
444          *start = make_header(subtag_dead_macptr,header_element_count(x1));
445        }
446      }
447      start = (LispObj *)skip_over_ivector((natural)start, x1);
448    } else if (nodeheader_tag_p(tag)) {
449      element_count = header_element_count(x1) | 1;
450      start += (element_count+1);
451    } else {
452      start += 2;
453    }
454  }
455}
456
457 
458
459int
460write_file_and_section_headers(int fd, 
461                               openmcl_image_file_header *file_header,
462                               openmcl_image_section_header* section_headers,
463                               int nsections,
464                               off_t *header_pos)
465{
466  *header_pos = seek_to_next_page(fd);
467
468  if (LSEEK (fd, *header_pos, SEEK_SET) < 0) {
469    return errno;
470  }
471  if (write(fd, file_header, sizeof(*file_header)) != sizeof(*file_header)) {
472    return errno;
473  }
474  if (write(fd, section_headers, sizeof(section_headers[0])*nsections)
475      != (sizeof(section_headers[0])*nsections)) {
476    return errno;
477  }
478  return 0;
479}
480 
481natural
482writebuf(int fd, char *bytes, natural n)
483{
484  natural remain = n, this_size;
485  signed_natural result;
486
487  while (remain) {
488    this_size = remain;
489    if (this_size > INT_MAX) {
490      this_size = INT_MAX;
491    }
492    result = write(fd, bytes, this_size);
493    if (result < 0) {
494      return errno;
495    }
496    bytes += result;
497
498    remain -= result;
499  }
500  return 0;
501}
502
503void
504prepare_to_write_static_space(Boolean egc_was_enabled)
505{
506  area *g0_area = g1_area->younger;
507  int i;
508
509  /* Save GC config */
510  lisp_global(LISP_HEAP_THRESHOLD) = lisp_heap_gc_threshold;
511  lisp_global(G0_THRESHOLD) = g0_area->threshold;
512  lisp_global(G1_THRESHOLD) = g1_area->threshold;
513  lisp_global(G2_THRESHOLD) = g2_area->threshold;
514  lisp_global(EGC_ENABLED) = (LispObj)egc_was_enabled;
515  lisp_global(GC_NOTIFY_THRESHOLD) = lisp_heap_notify_threshold;
516  /*
517    lisp_global(GC_NUM) and lisp_global(FWDNUM) are persistent,
518    as is DELETED_STATIC_PAIRS.
519    Nothing else is even meaningful at this point.
520    Except for those things that've become meaningful since that
521    comment was written.
522  */
523  for (i = MIN_KERNEL_GLOBAL; i < 0; i++) {
524    switch (i) {
525    case FREE_STATIC_CONSES:
526    case FWDNUM:
527    case GC_NUM:
528    case STATIC_CONSES:
529    case WEAK_GC_METHOD:
530    case LISP_HEAP_THRESHOLD:
531    case EGC_ENABLED:
532    case G0_THRESHOLD:
533    case G1_THRESHOLD:
534    case G2_THRESHOLD:
535    case GC_NOTIFY_THRESHOLD:
536      break;
537    case WEAKVLL:
538      break;
539    default:
540      lisp_global(i) = 0;
541    }
542  }
543}
544
545
546OSErr
547save_application_internal(unsigned fd, Boolean egc_was_enabled)
548{
549  openmcl_image_file_header fh;
550  openmcl_image_section_header sections[NUM_IMAGE_SECTIONS];
551  openmcl_image_file_trailer trailer;
552  area *areas[NUM_IMAGE_SECTIONS], *a;
553  int i, err;
554  off_t header_pos, eof_pos;
555#if WORD_SIZE == 64
556  off_t image_data_pos;
557  signed_natural section_data_delta;
558#endif
559
560  /*
561    Coerce macptrs to dead_macptrs.
562  */
563 
564  prepare_to_write_dynamic_space(active_dynamic_area);
565  prepare_to_write_dynamic_space(managed_static_area);
566
567  /*
568     If we ever support continuing after saving an image,
569     undo this .. */
570
571  if (static_cons_area->high > static_cons_area->low) {
572    active_dynamic_area->low = static_cons_area->high;
573    tenured_area->static_dnodes -= area_dnode(static_cons_area->high, static_cons_area->low);
574  }
575
576  areas[0] = nilreg_area; 
577  areas[1] = readonly_area;
578  areas[2] = active_dynamic_area;
579  areas[3] = managed_static_area;
580  areas[4] = static_cons_area;
581  for (i = 0; i < NUM_IMAGE_SECTIONS; i++) {
582    a = areas[i];
583    sections[i].code = a->code;
584    sections[i].area = NULL;
585    sections[i].memory_size  = a->active - a->low;
586    if (a == active_dynamic_area) {
587      sections[i].static_dnodes = tenured_area->static_dnodes;
588    } else {
589      sections[i].static_dnodes = 0;
590    }
591  }
592  fh.sig0 = IMAGE_SIG0;
593  fh.sig1 = IMAGE_SIG1;
594  fh.sig2 = IMAGE_SIG2;
595  fh.sig3 = IMAGE_SIG3;
596  fh.timestamp = time(NULL);
597  CANONICAL_IMAGE_BASE(&fh) = IMAGE_BASE_ADDRESS;
598  ACTUAL_IMAGE_BASE(&fh) = image_base;
599  fh.nsections = NUM_IMAGE_SECTIONS;
600  fh.abi_version=ABI_VERSION_CURRENT;
601#if WORD_SIZE == 64
602  fh.section_data_offset_high = 0;
603  fh.section_data_offset_low = 0;
604#else
605  fh.pad0[0] = fh.pad0[1] = 0;
606  fh.pad1[0] = fh.pad1[1] = fh.pad1[2] = fh.pad1[3] = 0;
607#endif
608  fh.flags = PLATFORM;
609
610#if WORD_SIZE == 64
611  image_data_pos = seek_to_next_page(fd);
612#else
613  err = write_file_and_section_headers(fd, &fh, sections, NUM_IMAGE_SECTIONS, &header_pos);
614  if (err) {
615    return err;
616  }
617#endif
618
619  prepare_to_write_static_space(egc_was_enabled);
620
621
622
623  for (i = 0; i < NUM_IMAGE_SECTIONS; i++) {
624    natural n;
625    a = areas[i];
626    seek_to_next_page(fd);
627    n = sections[i].memory_size;
628    if (writebuf(fd, a->low, n)) {
629        return errno;
630    }
631    if (n &&  ((sections[i].code) == AREA_MANAGED_STATIC)) {
632      natural ndnodes = area_dnode(a->active, a->low);
633      natural nrefbytes = align_to_power_of_2((ndnodes+7)>>3,log2_page_size);
634
635      seek_to_next_page(fd);
636      if (writebuf(fd,(char*)managed_static_refbits,nrefbytes)) {
637        return errno;
638      }
639    }
640  }
641
642#if WORD_SIZE == 64
643  seek_to_next_page(fd);
644  section_data_delta = -((LSEEK(fd,0,SEEK_CUR)+sizeof(fh)+sizeof(sections)) -
645                         image_data_pos);
646  fh.section_data_offset_high = (int)(section_data_delta>>32L);
647  fh.section_data_offset_low = (unsigned)section_data_delta;
648  err =  write_file_and_section_headers(fd, &fh, sections, NUM_IMAGE_SECTIONS, &header_pos);
649  if (err) {
650    return err;
651  } 
652#endif
653
654  trailer.sig0 = IMAGE_SIG0;
655  trailer.sig1 = IMAGE_SIG1;
656  trailer.sig2 = IMAGE_SIG2;
657  eof_pos = LSEEK(fd, 0, SEEK_CUR) + sizeof(trailer);
658  trailer.delta = (int) (header_pos-eof_pos);
659  if (write(fd, &trailer, sizeof(trailer)) == sizeof(trailer)) {
660#ifndef WINDOWS
661    fsync(fd);
662#endif
663    close(fd);
664    return 0;
665  } 
666  i = errno;
667  close(fd);
668  return i;
669}
670
671OSErr
672save_application(int fd, Boolean egc_was_enabled)
673{
674#ifdef DARWIN
675#ifdef X86
676  extern void save_native_library(int, Boolean);
677 
678  if (fd < 0) {
679    save_native_library(-fd, egc_was_enabled);
680    return 0;
681  }
682#endif
683#endif
684  return save_application_internal(fd, egc_was_enabled);
685}
686
687     
688
689
690
Note: See TracBrowser for help on using the repository browser.