1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-05-22 04:30:19 +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
@itemx .guile.docstrs.strtab @itemx .guile.docstrs.strtab
Side tables of procedure properties, arities, and docstrings. 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 @item .debug_info
@itemx .debug_abbrev @itemx .debug_abbrev
@itemx .debug_str @itemx .debug_str

View file

@ -1,5 +1,5 @@
/* Copyright (C) 2001, 2009, 2010, 2011, 2012 /* 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 * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License * 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); verify (sizeof (struct frame_map_header) == 8);
const scm_t_uint8 * 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; struct mapped_elf_image *image;
char *base; 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 * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License * 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_API SCM scm_load_thunk_from_memory (SCM bv);
SCM_INTERNAL const scm_t_uint8 * 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_bootstrap_loader (void);
SCM_INTERNAL void scm_init_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 #endif
} }
#define DEAD_SLOT_MAP_CACHE_SIZE 32U #define SLOT_MAP_CACHE_SIZE 32U
struct dead_slot_map_cache_entry struct slot_map_cache_entry
{ {
scm_t_uint32 *ip; scm_t_uint32 *ip;
const scm_t_uint8 *map; 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 * 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 /* The lower two bits should be zero. FIXME: Use a better hash
function; we don't expose scm_raw_hashq currently. */ 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; const scm_t_uint8 *map;
if (cache->entries[slot].ip == ip) if (cache->entries[slot].ip == ip)
map = cache->entries[slot].map; map = cache->entries[slot].map;
else else
{ {
map = scm_find_dead_slot_map_unlocked (ip); map = scm_find_slot_map_unlocked (ip);
cache->entries[slot].ip = ip; cache->entries[slot].ip = ip;
cache->entries[slot].map = map; 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; 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. */ /* Mark the active VM stack region. */
struct GC_ms_entry * struct GC_ms_entry *
scm_i_vm_mark_stack (struct scm_vm *vp, struct GC_ms_entry *mark_stack_ptr, scm_i_vm_mark_stack (struct scm_vm *vp, struct GC_ms_entry *mark_stack_ptr,
struct GC_ms_entry *mark_stack_limit) struct GC_ms_entry *mark_stack_limit)
{ {
union scm_vm_stack_element *sp, *fp; union scm_vm_stack_element *sp, *fp;
/* The first frame will be marked conservatively (without a dead /* The first frame will be marked conservatively (without a slot map).
slot map). This is because GC can happen at any point within the This is because GC can happen at any point within the hottest
hottest activation, due to multiple threads or per-instruction activation, due to multiple threads or per-instruction hooks, and
hooks, and providing dead slot maps for all points in a program providing slot maps for all points in a program would take a
would take a prohibitive amount of space. */ prohibitive amount of space. */
const scm_t_uint8 *dead_slots = NULL; const scm_t_uint8 *slot_map = NULL;
void *upper = (void *) GC_greatest_plausible_heap_addr; void *upper = (void *) GC_greatest_plausible_heap_addr;
void *lower = (void *) GC_least_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)); 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; size_t slot = nlocals - 1;
for (slot = nlocals - 1; sp < fp; sp++, slot--) 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) && if (SCM_NIMP (sp->as_scm) &&
sp->as_ptr >= lower && sp->as_ptr <= upper) 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 = GC_mark_and_push (sp->as_ptr,
mark_stack_ptr, mark_stack_ptr,
mark_stack_limit, mark_stack_limit,
NULL); 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); 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 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 map, e.g. if all of the frame's slots below the callee frame
are live. */ 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); return_unused_stack_to_os (vp);

View file

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

View file

@ -40,7 +40,7 @@
lookup-nlocals lookup-nlocals
lookup-call-proc-slot lookup-call-proc-slot
lookup-parallel-moves lookup-parallel-moves
lookup-dead-slot-map)) lookup-slot-map))
(define-record-type $allocation (define-record-type $allocation
(make-allocation slots constant-values call-allocs shuffles frame-sizes) (make-allocation slots constant-values call-allocs shuffles frame-sizes)
@ -84,10 +84,10 @@
(frame-sizes allocation-frame-sizes)) (frame-sizes allocation-frame-sizes))
(define-record-type $call-alloc (define-record-type $call-alloc
(make-call-alloc proc-slot dead-slot-map) (make-call-alloc proc-slot slot-map)
call-alloc? call-alloc?
(proc-slot call-alloc-proc-slot) (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) (define (lookup-maybe-slot var allocation)
(intmap-ref (allocation-slots allocation) var (lambda (_) #f))) (intmap-ref (allocation-slots allocation) var (lambda (_) #f)))
@ -121,9 +121,9 @@
(define (lookup-parallel-moves k allocation) (define (lookup-parallel-moves k allocation)
(intmap-ref (allocation-shuffles allocation) k)) (intmap-ref (allocation-shuffles allocation) k))
(define (lookup-dead-slot-map k allocation) (define (lookup-slot-map k allocation)
(or (call-alloc-dead-slot-map (lookup-call-alloc k allocation)) (or (call-alloc-slot-map (lookup-call-alloc k allocation))
(error "Call has no dead slot map" k))) (error "Call has no slot map" k)))
(define (lookup-nlocals k allocation) (define (lookup-nlocals k allocation)
(intmap-ref (allocation-frame-sizes allocation) k)) (intmap-ref (allocation-frame-sizes allocation) k))
@ -764,8 +764,52 @@ are comparable with eqv?. A tmp slot may be used."
(persistent-intmap (persistent-intmap
(intmap-fold-right allocate-lazy cps slots))) (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) (define (allocate-slots cps)
(let*-values (((defs uses) (compute-defs-and-uses 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)) ((live-in live-out) (compute-live-variables cps defs uses))
((constants) (compute-constant-values cps)) ((constants) (compute-constant-values cps))
((needs-slot) (compute-needs-slot cps defs uses)) ((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) (define (compute-live-out-slots slots label)
(compute-live-slots* slots label live-out)) (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) (define (allocate var hint slots live)
(cond (cond
((not (intset-ref needs-slot var)) ((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) (let ((result-slots (integers (+ proc-slot 2)
(length results)))) (length results))))
(allocate* results result-slots slots post-live))))) (allocate* results result-slots slots post-live)))))
((dead-slot-map) (logand (1- (ash 1 (- proc-slot 2))) ((slot-map) (compute-slot-map slots (intmap-ref live-out label)
(lognot post-live))) (- proc-slot 2)))
((call) (make-call-alloc proc-slot dead-slot-map))) ((call) (make-call-alloc proc-slot slot-map)))
(values slots (values slots
(intmap-add! call-allocs label call)))))) (intmap-add! call-allocs label call))))))
@ -909,8 +970,8 @@ are comparable with eqv?. A tmp slot may be used."
(let*-values (let*-values
(((handler-live) (compute-live-in-slots slots handler)) (((handler-live) (compute-live-in-slots slots handler))
((proc-slot) (compute-prompt-handler-proc-slot handler-live)) ((proc-slot) (compute-prompt-handler-proc-slot handler-live))
((dead-slot-map) (logand (1- (ash 1 (- proc-slot 2))) ((slot-map) (compute-slot-map slots (intmap-ref live-in handler)
(lognot handler-live))) (- proc-slot 2)))
((result-vars) (match (get-cont kargs) ((result-vars) (match (get-cont kargs)
(($ $kargs names vars) vars))) (($ $kargs names vars) vars)))
((value-slots) (integers (1+ proc-slot) (length result-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))) slots handler-live)))
(values slots (values slots
(intmap-add! call-allocs label (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) (define (allocate-cont label cont slots call-allocs)
(match cont (match cont

View file

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