1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-05-20 11:40:18 +02:00

VM support for raw slots

* libguile/loader.c (scm_find_slot_map_unlocked): Rename from
  scm_find_dead_slot_map_unlocked.

* libguile/vm.c (struct slot_map_cache_entry, struct slot_map_cache)
  (find_slot_map): Rename, changing "dead_slot" to "slot".
  (enum slot_desc): New type.
  (scm_i_vm_mark_stack): Interpret slot maps as having two bits per
  slot, allowing us to indicate that a slot is live but not a pointer.

* module/language/cps/compile-bytecode.scm (compile-function): Adapt to
  emit-slot-map name change.

* module/system/vm/assembler.scm (<asm>): Rename dead-slot-maps field to
  slot-maps.
  (emit-slot-map): Rename from emit-dead-slot-map.
  (link-frame-maps): 2 bits per slot.

* module/language/cps/slot-allocation.scm (lookup-slot-map): Rename from
  lookup-dead-slot-map.
  (compute-var-representations): New function.
  (allocate-slots): Adapt to encode two-bit slot representations.
This commit is contained in:
Andy Wingo 2015-10-28 16:40:53 +00:00
parent dd77a818ba
commit e7660a607c
7 changed files with 139 additions and 64 deletions

View file

@ -427,6 +427,10 @@ A table mapping addresses in the @code{.rtl-text} to procedure names.
@itemx .guile.docstrs
@itemx .guile.docstrs.strtab
Side tables of procedure properties, arities, and docstrings.
@item .guile.docstrs.strtab
Side table of frame maps, describing the set of live slots for ever
return point in the program text, and whether those slots are pointers
are not. Used by the garbage collector.
@item .debug_info
@itemx .debug_abbrev
@itemx .debug_str

View file

@ -1,5 +1,5 @@
/* Copyright (C) 2001, 2009, 2010, 2011, 2012
* 2013, 2014 Free Software Foundation, Inc.
* 2013, 2014, 2015 Free Software Foundation, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
@ -748,7 +748,7 @@ verify (sizeof (struct frame_map_prefix) == 8);
verify (sizeof (struct frame_map_header) == 8);
const scm_t_uint8 *
scm_find_dead_slot_map_unlocked (const scm_t_uint32 *ip)
scm_find_slot_map_unlocked (const scm_t_uint32 *ip)
{
struct mapped_elf_image *image;
char *base;

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2001, 2009, 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc.
/* Copyright (C) 2001, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Free Software Foundation, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
@ -25,7 +25,7 @@ SCM_API SCM scm_load_thunk_from_file (SCM filename);
SCM_API SCM scm_load_thunk_from_memory (SCM bv);
SCM_INTERNAL const scm_t_uint8 *
scm_find_dead_slot_map_unlocked (const scm_t_uint32 *ip);
scm_find_slot_map_unlocked (const scm_t_uint32 *ip);
SCM_INTERNAL void scm_bootstrap_loader (void);
SCM_INTERNAL void scm_init_loader (void);

View file

@ -895,31 +895,31 @@ return_unused_stack_to_os (struct scm_vm *vp)
#endif
}
#define DEAD_SLOT_MAP_CACHE_SIZE 32U
struct dead_slot_map_cache_entry
#define SLOT_MAP_CACHE_SIZE 32U
struct slot_map_cache_entry
{
scm_t_uint32 *ip;
const scm_t_uint8 *map;
};
struct dead_slot_map_cache
struct slot_map_cache
{
struct dead_slot_map_cache_entry entries[DEAD_SLOT_MAP_CACHE_SIZE];
struct slot_map_cache_entry entries[SLOT_MAP_CACHE_SIZE];
};
static const scm_t_uint8 *
find_dead_slot_map (scm_t_uint32 *ip, struct dead_slot_map_cache *cache)
find_slot_map (scm_t_uint32 *ip, struct slot_map_cache *cache)
{
/* The lower two bits should be zero. FIXME: Use a better hash
function; we don't expose scm_raw_hashq currently. */
size_t slot = (((scm_t_uintptr) ip) >> 2) % DEAD_SLOT_MAP_CACHE_SIZE;
size_t slot = (((scm_t_uintptr) ip) >> 2) % SLOT_MAP_CACHE_SIZE;
const scm_t_uint8 *map;
if (cache->entries[slot].ip == ip)
map = cache->entries[slot].map;
else
{
map = scm_find_dead_slot_map_unlocked (ip);
map = scm_find_slot_map_unlocked (ip);
cache->entries[slot].ip = ip;
cache->entries[slot].map = map;
}
@ -927,21 +927,29 @@ find_dead_slot_map (scm_t_uint32 *ip, struct dead_slot_map_cache *cache)
return map;
}
enum slot_desc
{
SLOT_DESC_DEAD = 0,
SLOT_DESC_LIVE_RAW = 1,
SLOT_DESC_LIVE_SCM = 2,
SLOT_DESC_UNUSED = 3
};
/* Mark the active VM stack region. */
struct GC_ms_entry *
scm_i_vm_mark_stack (struct scm_vm *vp, struct GC_ms_entry *mark_stack_ptr,
struct GC_ms_entry *mark_stack_limit)
{
union scm_vm_stack_element *sp, *fp;
/* The first frame will be marked conservatively (without a dead
slot map). This is because GC can happen at any point within the
hottest activation, due to multiple threads or per-instruction
hooks, and providing dead slot maps for all points in a program
would take a prohibitive amount of space. */
const scm_t_uint8 *dead_slots = NULL;
/* The first frame will be marked conservatively (without a slot map).
This is because GC can happen at any point within the hottest
activation, due to multiple threads or per-instruction hooks, and
providing slot maps for all points in a program would take a
prohibitive amount of space. */
const scm_t_uint8 *slot_map = NULL;
void *upper = (void *) GC_greatest_plausible_heap_addr;
void *lower = (void *) GC_least_plausible_heap_addr;
struct dead_slot_map_cache cache;
struct slot_map_cache cache;
memset (&cache, 0, sizeof (cache));
@ -953,24 +961,29 @@ scm_i_vm_mark_stack (struct scm_vm *vp, struct GC_ms_entry *mark_stack_ptr,
size_t slot = nlocals - 1;
for (slot = nlocals - 1; sp < fp; sp++, slot--)
{
enum slot_desc desc = SLOT_DESC_LIVE_SCM;
if (slot_map)
desc = (slot_map[slot / 4U] >> ((slot % 4U) * 2)) & 3U;
switch (desc)
{
case SLOT_DESC_LIVE_RAW:
break;
case SLOT_DESC_UNUSED:
case SLOT_DESC_LIVE_SCM:
if (SCM_NIMP (sp->as_scm) &&
sp->as_ptr >= lower && sp->as_ptr <= upper)
{
if (dead_slots)
{
if (dead_slots[slot / 8U] & (1U << (slot % 8U)))
{
/* This value may become dead as a result of GC,
so we can't just leave it on the stack. */
sp->as_scm = SCM_UNSPECIFIED;
continue;
}
}
mark_stack_ptr = GC_mark_and_push (sp->as_ptr,
mark_stack_ptr,
mark_stack_limit,
NULL);
break;
case SLOT_DESC_DEAD:
/* This value may become dead as a result of GC,
so we can't just leave it on the stack. */
sp->as_scm = SCM_UNSPECIFIED;
break;
}
}
sp = SCM_FRAME_PREVIOUS_SP (fp);
@ -978,7 +991,7 @@ scm_i_vm_mark_stack (struct scm_vm *vp, struct GC_ms_entry *mark_stack_ptr,
Note that there may be other reasons to not have a dead slots
map, e.g. if all of the frame's slots below the callee frame
are live. */
dead_slots = find_dead_slot_map (SCM_FRAME_RETURN_ADDRESS (fp), &cache);
slot_map = find_slot_map (SCM_FRAME_RETURN_ADDRESS (fp), &cache);
}
return_unused_stack_to_os (vp);

View file

@ -379,8 +379,7 @@
((src . dst) (emit-mov asm (from-sp dst) (from-sp src))))
(lookup-parallel-moves label allocation))
(emit-call asm proc-slot nargs)
(emit-dead-slot-map asm proc-slot
(lookup-dead-slot-map label allocation))
(emit-slot-map asm proc-slot (lookup-slot-map label allocation))
(cond
((and (= 1 nreq) (and rest-var) (not (maybe-slot rest-var))
(match (lookup-parallel-moves k allocation)

View file

@ -40,7 +40,7 @@
lookup-nlocals
lookup-call-proc-slot
lookup-parallel-moves
lookup-dead-slot-map))
lookup-slot-map))
(define-record-type $allocation
(make-allocation slots constant-values call-allocs shuffles frame-sizes)
@ -84,10 +84,10 @@
(frame-sizes allocation-frame-sizes))
(define-record-type $call-alloc
(make-call-alloc proc-slot dead-slot-map)
(make-call-alloc proc-slot slot-map)
call-alloc?
(proc-slot call-alloc-proc-slot)
(dead-slot-map call-alloc-dead-slot-map))
(slot-map call-alloc-slot-map))
(define (lookup-maybe-slot var allocation)
(intmap-ref (allocation-slots allocation) var (lambda (_) #f)))
@ -121,9 +121,9 @@
(define (lookup-parallel-moves k allocation)
(intmap-ref (allocation-shuffles allocation) k))
(define (lookup-dead-slot-map k allocation)
(or (call-alloc-dead-slot-map (lookup-call-alloc k allocation))
(error "Call has no dead slot map" k)))
(define (lookup-slot-map k allocation)
(or (call-alloc-slot-map (lookup-call-alloc k allocation))
(error "Call has no slot map" k)))
(define (lookup-nlocals k allocation)
(intmap-ref (allocation-frame-sizes allocation) k))
@ -764,8 +764,52 @@ are comparable with eqv?. A tmp slot may be used."
(persistent-intmap
(intmap-fold-right allocate-lazy cps slots)))
(define (compute-var-representations cps)
(define (get-defs k)
(match (intmap-ref cps k)
(($ $kargs names vars) vars)
(_ '())))
(intmap-fold
(lambda (label cont representations)
(match cont
(($ $kargs _ _ ($ $continue k _ exp))
(match (get-defs k)
(() representations)
((var)
(match exp
(($ $values (arg))
(intmap-add representations var
(intmap-ref representations arg)))
;; FIXME: Placeholder for as-yet-unwritten primitive
;; operations that define unboxed f64 values.
(($ $primcall 'scm->f64)
(intmap-add representations var 'f64))
(_
(intmap-add representations var 'scm))))
(vars
(match exp
(($ $values args)
(fold (lambda (arg var representations)
(intmap-add representations var
(intmap-ref representations arg)))
representations args vars))))))
(($ $kfun src meta self)
(intmap-add representations self 'scm))
(($ $kclause arity body alt)
(fold1 (lambda (var representations)
(intmap-add representations var 'scm))
(get-defs body) representations))
(($ $kreceive arity kargs)
(fold1 (lambda (var representations)
(intmap-add representations var 'scm))
(get-defs kargs) representations))
(($ $ktail) representations)))
cps
empty-intmap))
(define (allocate-slots cps)
(let*-values (((defs uses) (compute-defs-and-uses cps))
((representations) (compute-var-representations cps))
((live-in live-out) (compute-live-variables cps defs uses))
((constants) (compute-constant-values cps))
((needs-slot) (compute-needs-slot cps defs uses))
@ -809,6 +853,23 @@ are comparable with eqv?. A tmp slot may be used."
(define (compute-live-out-slots slots label)
(compute-live-slots* slots label live-out))
(define slot-desc-dead 0)
(define slot-desc-live-raw 1)
(define slot-desc-live-scm 2)
(define slot-desc-unused 3)
(define (compute-slot-map slots live-vars nslots)
(intset-fold
(lambda (var slot-map)
(match (get-slot slots var)
(#f slot-map)
(slot
(let ((desc (match (intmap-ref representations var)
('f64 slot-desc-live-raw)
('scm slot-desc-live-scm))))
(logior slot-map (ash desc (* 2 slot)))))))
live-vars 0))
(define (allocate var hint slots live)
(cond
((not (intset-ref needs-slot var))
@ -874,9 +935,9 @@ are comparable with eqv?. A tmp slot may be used."
(let ((result-slots (integers (+ proc-slot 2)
(length results))))
(allocate* results result-slots slots post-live)))))
((dead-slot-map) (logand (1- (ash 1 (- proc-slot 2)))
(lognot post-live)))
((call) (make-call-alloc proc-slot dead-slot-map)))
((slot-map) (compute-slot-map slots (intmap-ref live-out label)
(- proc-slot 2)))
((call) (make-call-alloc proc-slot slot-map)))
(values slots
(intmap-add! call-allocs label call))))))
@ -909,8 +970,8 @@ are comparable with eqv?. A tmp slot may be used."
(let*-values
(((handler-live) (compute-live-in-slots slots handler))
((proc-slot) (compute-prompt-handler-proc-slot handler-live))
((dead-slot-map) (logand (1- (ash 1 (- proc-slot 2)))
(lognot handler-live)))
((slot-map) (compute-slot-map slots (intmap-ref live-in handler)
(- proc-slot 2)))
((result-vars) (match (get-cont kargs)
(($ $kargs names vars) vars)))
((value-slots) (integers (1+ proc-slot) (length result-vars)))
@ -918,7 +979,7 @@ are comparable with eqv?. A tmp slot may be used."
slots handler-live)))
(values slots
(intmap-add! call-allocs label
(make-call-alloc proc-slot dead-slot-map)))))))
(make-call-alloc proc-slot slot-map)))))))
(define (allocate-cont label cont slots call-allocs)
(match cont

View file

@ -326,7 +326,7 @@
constants inits
shstrtab next-section-number
meta sources
dead-slot-maps)
slot-maps)
asm?
;; We write bytecode into what is logically a growable vector,
@ -404,12 +404,11 @@
;;
(sources asm-sources set-asm-sources!)
;; A list of (pos . dead-slot-map) pairs, indicating dead slot maps.
;; POS is relative to the beginning of the text section.
;; DEAD-SLOT-MAP is a bitfield of slots that are dead at call sites,
;; as an integer.
;; A list of (pos . slot-map) pairs, indicating slot maps. POS is
;; relative to the beginning of the text section. SLOT-MAP is a
;; bitfield describing the stack at call sites, as an integer.
;;
(dead-slot-maps asm-dead-slot-maps set-asm-dead-slot-maps!))
(slot-maps asm-slot-maps set-asm-slot-maps!))
(define-inline (fresh-block)
(make-u32vector *block-size*))
@ -1187,12 +1186,11 @@ returned instead."
(cell-label (intern-cache-cell asm key sym)))
(emit-module-box asm dst cell-label mod-name-label sym-label bound?)))
(define-macro-assembler (dead-slot-map asm proc-slot dead-slot-map)
(unless (zero? dead-slot-map)
(set-asm-dead-slot-maps! asm
(cons
(cons* (asm-start asm) proc-slot dead-slot-map)
(asm-dead-slot-maps asm)))))
(define-macro-assembler (slot-map asm proc-slot slot-map)
(unless (zero? slot-map)
(set-asm-slot-maps! asm (cons
(cons* (asm-start asm) proc-slot slot-map)
(asm-slot-maps asm)))))
@ -1605,7 +1603,7 @@ needed."
(define (link-frame-maps asm)
(define (map-byte-length proc-slot)
(ceiling-quotient (- proc-slot 2) 8))
(ceiling-quotient (* 2 (- proc-slot 2)) 8))
(define (make-frame-maps maps count map-len)
(let* ((endianness (asm-endianness asm))
(header-pos frame-maps-prefix-len)
@ -1630,7 +1628,7 @@ needed."
(bytevector-u8-set! bv map-pos (logand map #xff))
(write-bytes (1+ map-pos) (ash map -8)
(1- byte-length))))))))))
(match (asm-dead-slot-maps asm)
(match (asm-slot-maps asm)
(() #f)
(in
(let lp ((in in) (out '()) (count 0) (map-len 0))