constant-interning fix
[bpt/guile.git] / module / system / vm / assembler.scm
index 01fac65..bb4ddf7 100644 (file)
@@ -1,6 +1,6 @@
-;;; Guile RTL assembler
+;;; Guile bytecode assembler
 
-;;; Copyright (C) 2001, 2009, 2010, 2012, 2013 Free Software Foundation, Inc.
+;;; Copyright (C) 2001, 2009, 2010, 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
 ;;; Commentary:
 ;;;
 ;;; This module implements an assembler that creates an ELF image from
-;;; RTL assembly and macro-assembly.  The input can be given in
+;;; bytecode assembly and macro-assembly.  The input can be given in
 ;;; s-expression form, like ((OP ARG ...) ...).  Internally there is a
 ;;; procedural interface, the emit-OP procedures, but that is not
 ;;; currently exported.
 ;;;
-;;; "Primitive instructions" correspond to RTL VM operations.
-;;; Assemblers for primitive instructions are generated programmatically
-;;; from (rtl-instruction-list), which itself is derived from the VM
-;;; sources.  There are also "macro-instructions" like "label" or
-;;; "load-constant" that expand to 0 or more primitive instructions.
+;;; "Primitive instructions" correspond to VM operations.  Assemblers
+;;; for primitive instructions are generated programmatically from
+;;; (instruction-list), which itself is derived from the VM sources.
+;;; There are also "macro-instructions" like "label" or "load-constant"
+;;; that expand to 0 or more primitive instructions.
 ;;;
 ;;; The assembler also handles some higher-level tasks, like creating
 ;;; the symbol table, other metadata sections, creating a constant table
 
 (define-module (system vm assembler)
   #:use-module (system base target)
-  #:use-module (system vm instruction)
   #:use-module (system vm dwarf)
   #:use-module (system vm elf)
   #:use-module (system vm linker)
+  #:use-module (language bytecode)
   #:use-module (rnrs bytevectors)
   #:use-module (ice-9 binary-ports)
   #:use-module (ice-9 vlist)
   #:use-module (srfi srfi-9)
   #:use-module (srfi srfi-11)
   #:export (make-assembler
+
+            emit-call
+            emit-call-label
+            emit-tail-call
+            emit-tail-call-label
+            (emit-receive* . emit-receive)
+            emit-receive-values
+            emit-return
+            emit-return-values
+            emit-call/cc
+            emit-abort
+            (emit-builtin-ref* . emit-builtin-ref)
+            emit-br-if-nargs-ne
+            emit-br-if-nargs-lt
+            emit-br-if-nargs-gt
+            emit-assert-nargs-ee
+            emit-assert-nargs-ge
+            emit-assert-nargs-le
+            emit-alloc-frame
+            emit-reset-frame
+            emit-assert-nargs-ee/locals
+            emit-br-if-npos-gt
+            emit-bind-kwargs
+            emit-bind-rest
+            emit-br
+            emit-br-if-true
+            emit-br-if-null
+            emit-br-if-nil
+            emit-br-if-pair
+            emit-br-if-struct
+            emit-br-if-char
+            emit-br-if-tc7
+            (emit-br-if-eq* . emit-br-if-eq)
+            (emit-br-if-eqv* . emit-br-if-eqv)
+            (emit-br-if-equal* . emit-br-if-equal)
+            (emit-br-if-=* . emit-br-if-=)
+            (emit-br-if-<* . emit-br-if-<)
+            (emit-br-if-<=* . emit-br-if-<=)
+            (emit-br-if-logtest* . emit-br-if-logtest)
+            (emit-mov* . emit-mov)
+            (emit-box* . emit-box)
+            (emit-box-ref* . emit-box-ref)
+            (emit-box-set!* . emit-box-set!)
+            emit-make-closure
+            (emit-free-ref* . emit-free-ref)
+            (emit-free-set!* . emit-free-set!)
+            emit-current-module
+            emit-resolve
+            (emit-define!* . emit-define!)
+            emit-toplevel-box
+            emit-module-box
+            emit-prompt
+            (emit-wind* . emit-wind)
+            emit-unwind
+            (emit-push-fluid* . emit-push-fluid)
+            emit-pop-fluid
+            (emit-fluid-ref* . emit-fluid-ref)
+            (emit-fluid-set* . emit-fluid-set)
+            (emit-string-length* . emit-string-length)
+            (emit-string-ref* . emit-string-ref)
+            (emit-string->number* . emit-string->number)
+            (emit-string->symbol* . emit-string->symbol)
+            (emit-symbol->keyword* . emit-symbol->keyword)
+            (emit-cons* . emit-cons)
+            (emit-car* . emit-car)
+            (emit-cdr* . emit-cdr)
+            (emit-set-car!* . emit-set-car!)
+            (emit-set-cdr!* . emit-set-cdr!)
+            (emit-add* . emit-add)
+            (emit-add1* . emit-add1)
+            (emit-sub* . emit-sub)
+            (emit-sub1* . emit-sub1)
+            (emit-mul* . emit-mul)
+            (emit-div* . emit-div)
+            (emit-quo* . emit-quo)
+            (emit-rem* . emit-rem)
+            (emit-mod* . emit-mod)
+            (emit-ash* . emit-ash)
+            (emit-logand* . emit-logand)
+            (emit-logior* . emit-logior)
+            (emit-logxor* . emit-logxor)
+            (emit-make-vector* . emit-make-vector)
+            (emit-make-vector/immediate* . emit-make-vector/immediate)
+            (emit-vector-length* . emit-vector-length)
+            (emit-vector-ref* . emit-vector-ref)
+            (emit-vector-ref/immediate* . emit-vector-ref/immediate)
+            (emit-vector-set!* . emit-vector-set!)
+            (emit-vector-set!/immediate* . emit-vector-set!/immediate)
+            (emit-struct-vtable* . emit-struct-vtable)
+            (emit-allocate-struct/immediate* . emit-allocate-struct/immediate)
+            (emit-struct-ref/immediate* . emit-struct-ref/immediate)
+            (emit-struct-set!/immediate* . emit-struct-set!/immediate)
+            (emit-allocate-struct* . emit-allocate-struct)
+            (emit-struct-ref* . emit-struct-ref)
+            (emit-struct-set!* . emit-struct-set!)
+            (emit-class-of* . emit-class-of)
+            (emit-make-array* . emit-make-array)
+            (emit-bv-u8-ref* . emit-bv-u8-ref)
+            (emit-bv-s8-ref* . emit-bv-s8-ref)
+            (emit-bv-u16-ref* . emit-bv-u16-ref)
+            (emit-bv-s16-ref* . emit-bv-s16-ref)
+            (emit-bv-u32-ref* . emit-bv-u32-ref)
+            (emit-bv-s32-ref* . emit-bv-s32-ref)
+            (emit-bv-u64-ref* . emit-bv-u64-ref)
+            (emit-bv-s64-ref* . emit-bv-s64-ref)
+            (emit-bv-f32-ref* . emit-bv-f32-ref)
+            (emit-bv-f64-ref* . emit-bv-f64-ref)
+            (emit-bv-u8-set!* . emit-bv-u8-set!)
+            (emit-bv-s8-set!* . emit-bv-s8-set!)
+            (emit-bv-u16-set!* . emit-bv-u16-set!)
+            (emit-bv-s16-set!* . emit-bv-s16-set!)
+            (emit-bv-u32-set!* . emit-bv-u32-set!)
+            (emit-bv-s32-set!* . emit-bv-s32-set!)
+            (emit-bv-u64-set!* . emit-bv-u64-set!)
+            (emit-bv-s64-set!* . emit-bv-s64-set!)
+            (emit-bv-f32-set!* . emit-bv-f32-set!)
+            (emit-bv-f64-set!* . emit-bv-f64-set!)
+
             emit-text
             link-assembly))
 
 
 \f
 
-;;; RTL code consists of 32-bit units, often subdivided in some way.
+;; Like define-inlinable, but only for first-order uses of the defined
+;; routine.  Should residualize less code.
+(eval-when (expand)
+  (define-syntax define-inline
+    (lambda (x)
+      (syntax-case x ()
+        ((_ (name arg ...) body ...)
+         (with-syntax (((temp ...) (generate-temporaries #'(arg ...))))
+           #`(eval-when (expand)
+               (define-syntax-rule (name temp ...)
+                 (let ((arg temp) ...)
+                   body ...)))))))))
+
+;;; Bytecode consists of 32-bit units, often subdivided in some way.
 ;;; These helpers create one 32-bit unit from multiple components.
 
-(define-inlinable (pack-u8-u24 x y)
+(define-inline (pack-u8-u24 x y)
   (unless (<= 0 x 255)
     (error "out of range" x))
   (logior x (ash y 8)))
 
-(define-inlinable (pack-u8-s24 x y)
+(define-inline (pack-u8-s24 x y)
   (unless (<= 0 x 255)
     (error "out of range" x))
   (logior x (ash (cond
                   (else (error "out of range" y)))
                  8)))
 
-(define-inlinable (pack-u1-u7-u24 x y z)
+(define-inline (pack-u1-u7-u24 x y z)
   (unless (<= 0 x 1)
     (error "out of range" x))
   (unless (<= 0 y 127)
     (error "out of range" y))
   (logior x (ash y 1) (ash z 8)))
 
-(define-inlinable (pack-u8-u12-u12 x y z)
+(define-inline (pack-u8-u12-u12 x y z)
   (unless (<= 0 x 255)
     (error "out of range" x))
   (unless (<= 0 y 4095)
     (error "out of range" y))
   (logior x (ash y 8) (ash z 20)))
 
-(define-inlinable (pack-u8-u8-u16 x y z)
+(define-inline (pack-u8-u8-u16 x y z)
   (unless (<= 0 x 255)
     (error "out of range" x))
   (unless (<= 0 y 255)
     (error "out of range" y))
   (logior x (ash y 8) (ash z 16)))
 
-(define-inlinable (pack-u8-u8-u8-u8 x y z w)
+(define-inline (pack-u8-u8-u8-u8 x y z w)
   (unless (<= 0 x 255)
     (error "out of range" x))
   (unless (<= 0 y 255)
     (error "out of range" z))
   (logior x (ash y 8) (ash z 16) (ash w 24)))
 
-(define-syntax pack-flags
-  (syntax-rules ()
-    ;; Add clauses as needed.
-    ((pack-flags f1 f2) (logior (if f1 (ash 1 0) 0)
-                                (if f2 (ash 2 0) 0)))))
+(eval-when (expand)
+  (define-syntax pack-flags
+    (syntax-rules ()
+      ;; Add clauses as needed.
+      ((pack-flags f1 f2) (logior (if f1 (ash 1 0) 0)
+                                  (if f2 (ash 2 0) 0))))))
 
 ;;; Helpers to read and write 32-bit units in a buffer.
 
-(define-syntax-rule (u32-ref buf n)
+(define-inline (u32-ref buf n)
   (bytevector-u32-native-ref buf (* n 4)))
 
-(define-syntax-rule (u32-set! buf n val)
+(define-inline (u32-set! buf n val)
   (bytevector-u32-native-set! buf (* n 4) val))
 
-(define-syntax-rule (s32-ref buf n)
+(define-inline (s32-ref buf n)
   (bytevector-s32-native-ref buf (* n 4)))
 
-(define-syntax-rule (s32-set! buf n val)
+(define-inline (s32-set! buf n val)
   (bytevector-s32-native-set! buf (* n 4) val))
 
 
 \f
 
 ;;; A <meta> entry collects metadata for one procedure.  Procedures are
-;;; written as contiguous ranges of RTL code.
+;;; written as contiguous ranges of bytecode.
 ;;;
-(define-syntax-rule (assert-match arg pattern kind)
-  (let ((x arg))
-    (unless (match x (pattern #t) (_ #f))
-      (error (string-append "expected " kind) x))))
+(eval-when (expand)
+  (define-syntax-rule (assert-match arg pattern kind)
+    (let ((x arg))
+      (unless (match x (pattern #t) (_ #f))
+        (error (string-append "expected " kind) x)))))
 
 (define-record-type <meta>
   (%make-meta label properties low-pc high-pc arities)
   (arities meta-arities set-meta-arities!))
 
 (define (make-meta label properties low-pc)
-  (assert-match label (? symbol?) "symbol")
+  (assert-match label (or (? exact-integer?) (? symbol?)) "symbol")
   (assert-match properties (((? symbol?) . _) ...) "alist with symbolic keys")
   (%make-meta label properties low-pc #f '()))
 
 ;; Metadata for one <lambda-case>.
 (define-record-type <arity>
   (make-arity req opt rest kw-indices allow-other-keys?
-              low-pc high-pc)
+              low-pc high-pc definitions)
   arity?
   (req arity-req)
   (opt arity-opt)
   (kw-indices arity-kw-indices)
   (allow-other-keys? arity-allow-other-keys?)
   (low-pc arity-low-pc)
-  (high-pc arity-high-pc set-arity-high-pc!))
+  (high-pc arity-high-pc set-arity-high-pc!)
+  (definitions arity-definitions set-arity-definitions!))
 
-(define-syntax *block-size* (identifier-syntax 32))
+(eval-when (expand)
+  (define-syntax *block-size* (identifier-syntax 32)))
 
 ;;; An assembler collects all of the words emitted during assembly, and
 ;;; also maintains ancillary information such as the constant table, a
 ;;; relocation list, and so on.
 ;;;
-;;; RTL code consists of 32-bit units.  We emit RTL code using native
+;;; Bytecode consists of 32-bit units.  We emit bytecode using native
 ;;; endianness.  If we're targeting a foreign endianness, we byte-swap
 ;;; the bytevector as a whole instead of conditionalizing each access.
 ;;;
             word-size endianness
             constants inits
             shstrtab next-section-number
-            meta sources)
+            meta sources
+            dead-slot-maps
+            to-file?)
   asm?
 
-  ;; We write RTL code into what is logically a growable vector,
+  ;; We write bytecode into what is logically a growable vector,
   ;; implemented as a list of blocks.  asm-cur is the current block, and
   ;; asm-idx is the current index into that block, in 32-bit units.
   ;;
   ;; beginning of an instruction (in u32 units).  It is updated after
   ;; writing all the words for one primitive instruction.  It models the
   ;; position of the instruction pointer during execution, given that
-  ;; the RTL VM updates the IP only at the end of executing the
-  ;; instruction, and is thus useful for computing offsets between two
-  ;; points in a program.
+  ;; the VM updates the IP only at the end of executing the instruction,
+  ;; and is thus useful for computing offsets between two points in a
+  ;; program.
   ;;
   (start asm-start set-asm-start!)
 
   ;;
   (constants asm-constants set-asm-constants!)
 
-  ;; A list of RTL instructions needed to initialize the constants.
-  ;; Will run in a thunk with 2 local variables.
+  ;; A list of instructions needed to initialize the constants.  Will
+  ;; run in a thunk with 2 local variables.
   ;;
   (inits asm-inits set-asm-inits!)
 
   ;; is relative to the beginning of the text section, and SOURCE is in
   ;; the same format that source-properties returns.
   ;;
-  (sources asm-sources set-asm-sources!))
+  (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.
+  ;;
+  (dead-slot-maps asm-dead-slot-maps set-asm-dead-slot-maps!)
+  (to-file? asm-to-file?))
 
-(define-inlinable (fresh-block)
+(define-inline (fresh-block)
   (make-u32vector *block-size*))
 
 (define* (make-assembler #:key (word-size (target-word-size))
-                         (endianness (target-endianness)))
+                         (endianness (target-endianness))
+                         (to-file? #t))
   "Create an assembler for a given target @var{word-size} and
 @var{endianness}, falling back to appropriate values for the configured
 target."
@@ -280,13 +426,13 @@ target."
             word-size endianness
             vlist-null '()
             (make-string-table) 1
-            '() '()))
+            '() '() '() to-file?))
 
 (define (intern-section-name! asm string)
   "Add a string to the section name table (shstrtab)."
   (string-table-intern! (asm-shstrtab asm) string))
 
-(define-inlinable (asm-pos asm)
+(define-inline (asm-pos asm)
   "The offset of the next word to be written into the code buffer, in
 32-bit units."
   (+ (asm-idx asm) (asm-written asm)))
@@ -300,7 +446,7 @@ written to a fresh block."
     (set-asm-cur! asm new)
     (set-asm-idx! asm 0)))
 
-(define-inlinable (emit asm u32)
+(define-inline (emit asm u32)
   "Emit one 32-bit word into the instruction stream.  Assumes that there
 is space for the word, and ensures that there is space for the next
 word."
@@ -309,7 +455,7 @@ word."
   (if (= (asm-idx asm) *block-size*)
       (allocate-new-block asm)))
 
-(define-inlinable (make-reloc type label base word)
+(define-inline (make-reloc type label base word)
   "Make an internal relocation of type @var{type} referencing symbol
 @var{label}, @var{word} words after position @var{start}.  @var{type}
 may be x8-s24, indicating a 24-bit relative label reference that can be
@@ -317,7 +463,7 @@ fixed up by the assembler, or s32, indicating a 32-bit relative
 reference that needs to be fixed up by the linker."
   (list type label base word))
 
-(define-inlinable (reset-asm-start! asm)
+(define-inline (reset-asm-start! asm)
   "Reset the asm-start after writing the words for one instruction."
   (set-asm-start! asm (asm-pos asm)))
 
@@ -342,151 +488,301 @@ later by the linker."
 
 ;;;
 ;;; Primitive assemblers are defined by expanding `assembler' for each
-;;; opcode in `(rtl-instruction-list)'.
+;;; opcode in `(instruction-list)'.
 ;;;
 
-(eval-when (expand compile load eval)
+(eval-when (expand)
   (define (id-append ctx a b)
-    (datum->syntax ctx (symbol-append (syntax->datum a) (syntax->datum b)))))
-
-(define-syntax assembler
-  (lambda (x)
-    (define-syntax op-case
-      (lambda (x)
-        (syntax-case x ()
-          ((_ asm name ((type arg ...) code ...) clause ...)
-           #`(if (eq? name 'type)
-                 (with-syntax (((arg ...) (generate-temporaries #'(arg ...))))
-                   #'((arg ...)
-                      code ...))
-                 (op-case asm name clause ...)))
-          ((_ asm name)
-           #'(error "unmatched name" name)))))
-
-    (define (pack-first-word asm opcode type)
-      (with-syntax ((opcode opcode))
+    (datum->syntax ctx (symbol-append (syntax->datum a) (syntax->datum b))))
+
+  (define-syntax assembler
+    (lambda (x)
+      (define-syntax op-case
+        (lambda (x)
+          (syntax-case x ()
+            ((_ asm name ((type arg ...) code ...) clause ...)
+             #`(if (eq? name 'type)
+                   (with-syntax (((arg ...) (generate-temporaries #'(arg ...))))
+                     #'((arg ...)
+                        code ...))
+                   (op-case asm name clause ...)))
+            ((_ asm name)
+             #'(error "unmatched name" name)))))
+
+      (define (pack-first-word asm opcode type)
+        (with-syntax ((opcode opcode))
+          (op-case
+           asm type
+           ((U8_X24)
+            (emit asm opcode))
+           ((U8_U24 arg)
+            (emit asm (pack-u8-u24 opcode arg)))
+           ((U8_L24 label)
+            (record-label-reference asm label)
+            (emit asm opcode))
+           ((U8_U8_I16 a imm)
+            (emit asm (pack-u8-u8-u16 opcode a (object-address imm))))
+           ((U8_U12_U12 a b)
+            (emit asm (pack-u8-u12-u12 opcode a b)))
+           ((U8_U8_U8_U8 a b c)
+            (emit asm (pack-u8-u8-u8-u8 opcode a b c))))))
+
+      (define (pack-tail-word asm type)
         (op-case
          asm type
-         ((U8_X24)
-          (emit asm opcode))
-         ((U8_U24 arg)
-          (emit asm (pack-u8-u24 opcode arg)))
-         ((U8_L24 label)
+         ((U8_U24 a b)
+          (emit asm (pack-u8-u24 a b)))
+         ((U8_L24 a label)
           (record-label-reference asm label)
-          (emit asm opcode))
-         ((U8_U8_I16 a imm)
-          (emit asm (pack-u8-u8-u16 opcode a (object-address imm))))
-         ((U8_U12_U12 a b)
-          (emit asm (pack-u8-u12-u12 opcode a b)))
-         ((U8_U8_U8_U8 a b c)
-          (emit asm (pack-u8-u8-u8-u8 opcode a b c))))))
-
-    (define (pack-tail-word asm type)
-      (op-case
-       asm type
-       ((U8_U24 a b)
-        (emit asm (pack-u8-u24 a b)))
-       ((U8_L24 a label)
-        (record-label-reference asm label)
-        (emit asm a))
-       ((U8_U8_I16 a b imm)
-        (emit asm (pack-u8-u8-u16 a b (object-address imm))))
-       ((U8_U12_U12 a b)
-        (emit asm (pack-u8-u12-u12 a b c)))
-       ((U8_U8_U8_U8 a b c d)
-        (emit asm (pack-u8-u8-u8-u8 a b c d)))
-       ((U32 a)
-        (emit asm a))
-       ((I32 imm)
-        (let ((val (object-address imm)))
-          (unless (zero? (ash val -32))
-            (error "FIXME: enable truncation of negative fixnums when cross-compiling"))
-          (emit asm val)))
-       ((A32 imm)
-        (unless (= (asm-word-size asm) 8)
-          (error "make-long-immediate unavailable for this target"))
-        (emit asm (ash (object-address imm) -32))
-        (emit asm (logand (object-address imm) (1- (ash 1 32)))))
-       ((B32))
-       ((N32 label)
-        (record-far-label-reference asm label)
-        (emit asm 0))
-       ((S32 label)
-        (record-far-label-reference asm label)
-        (emit asm 0))
-       ((L32 label)
-        (record-far-label-reference asm label)
-        (emit asm 0))
-       ((LO32 label offset)
-        (record-far-label-reference asm label
-                                    (* offset (/ (asm-word-size asm) 4)))
-        (emit asm 0))
-       ((X8_U24 a)
-        (emit asm (pack-u8-u24 0 a)))
-       ((X8_U12_U12 a b)
-        (emit asm (pack-u8-u12-u12 0 a b)))
-       ((X8_L24 label)
-        (record-label-reference asm label)
-        (emit asm 0))
-       ((B1_X7_L24 a label)
-        (record-label-reference asm label)
-        (emit asm (pack-u1-u7-u24 (if a 1 0) 0 0)))
-       ((B1_U7_L24 a b label)
-        (record-label-reference asm label)
-        (emit asm (pack-u1-u7-u24 (if a 1 0) b 0)))
-       ((B1_X31 a)
-        (emit asm (pack-u1-u7-u24 (if a 1 0) 0 0)))
-       ((B1_X7_U24 a b)
-        (emit asm (pack-u1-u7-u24 (if a 1 0) 0 b)))))
-
-    (syntax-case x ()
-      ((_ name opcode word0 word* ...)
-       (with-syntax ((((formal0 ...)
-                       code0 ...)
-                      (pack-first-word #'asm
-                                       (syntax->datum #'opcode)
-                                       (syntax->datum #'word0)))
-                     ((((formal* ...)
-                        code* ...) ...)
-                      (map (lambda (word) (pack-tail-word #'asm word))
-                           (syntax->datum #'(word* ...)))))
-         #'(lambda (asm formal0 ... formal* ... ...)
-             (unless (asm? asm) (error "not an asm"))
-             code0 ...
-             code* ... ...
-             (reset-asm-start! asm)))))))
+          (emit asm a))
+         ((U32 a)
+          (emit asm a))
+         ((I32 imm)
+          (let ((val (object-address imm)))
+            (unless (zero? (ash val -32))
+              (error "FIXME: enable truncation of negative fixnums when cross-compiling"))
+            (emit asm val)))
+         ((A32 imm)
+          (unless (= (asm-word-size asm) 8)
+            (error "make-long-immediate unavailable for this target"))
+          (emit asm (ash (object-address imm) -32))
+          (emit asm (logand (object-address imm) (1- (ash 1 32)))))
+         ((B32))
+         ((N32 label)
+          (record-far-label-reference asm label)
+          (emit asm 0))
+         ((S32 label)
+          (record-far-label-reference asm label)
+          (emit asm 0))
+         ((L32 label)
+          (record-far-label-reference asm label)
+          (emit asm 0))
+         ((LO32 label offset)
+          (record-far-label-reference asm label
+                                      (* offset (/ (asm-word-size asm) 4)))
+          (emit asm 0))
+         ((X8_U24 a)
+          (emit asm (pack-u8-u24 0 a)))
+         ((X8_L24 label)
+          (record-label-reference asm label)
+          (emit asm 0))
+         ((B1_X7_L24 a label)
+          (record-label-reference asm label)
+          (emit asm (pack-u1-u7-u24 (if a 1 0) 0 0)))
+         ((B1_U7_L24 a b label)
+          (record-label-reference asm label)
+          (emit asm (pack-u1-u7-u24 (if a 1 0) b 0)))
+         ((B1_X31 a)
+          (emit asm (pack-u1-u7-u24 (if a 1 0) 0 0)))
+         ((B1_X7_U24 a b)
+          (emit asm (pack-u1-u7-u24 (if a 1 0) 0 b)))))
+
+      (syntax-case x ()
+        ((_ name opcode word0 word* ...)
+         (with-syntax ((((formal0 ...)
+                         code0 ...)
+                        (pack-first-word #'asm
+                                         (syntax->datum #'opcode)
+                                         (syntax->datum #'word0)))
+                       ((((formal* ...)
+                          code* ...) ...)
+                        (map (lambda (word) (pack-tail-word #'asm word))
+                             (syntax->datum #'(word* ...)))))
+           #'(lambda (asm formal0 ... formal* ... ...)
+               (unless (asm? asm) (error "not an asm"))
+               code0 ...
+               code* ... ...
+               (reset-asm-start! asm))))))))
 
 (define assemblers (make-hash-table))
 
-(define-syntax define-assembler
-  (lambda (x)
-    (syntax-case x ()
-      ((_ name opcode kind arg ...)
-       (with-syntax ((emit (id-append #'name #'emit- #'name)))
-         #'(begin
-             (define emit
+(eval-when (expand)
+  (define-syntax define-assembler
+    (lambda (x)
+      (syntax-case x ()
+        ((_ name opcode kind arg ...)
+         (with-syntax ((emit (id-append #'name #'emit- #'name)))
+           #'(define emit
                (let ((emit (assembler name opcode arg ...)))
                  (hashq-set! assemblers 'name emit)
-                 emit))
-             (export emit)))))))
-
-(define-syntax visit-opcodes
-  (lambda (x)
-    (syntax-case x ()
-      ((visit-opcodes macro arg ...)
-       (with-syntax (((inst ...)
-                      (map (lambda (x) (datum->syntax #'macro x))
-                           (rtl-instruction-list))))
-         #'(begin
-             (macro arg ... . inst)
-             ...))))))
+                 emit)))))))
+
+  (define-syntax visit-opcodes
+    (lambda (x)
+      (syntax-case x ()
+        ((visit-opcodes macro arg ...)
+         (with-syntax (((inst ...)
+                        (map (lambda (x) (datum->syntax #'macro x))
+                             (instruction-list))))
+           #'(begin
+               (macro arg ... . inst)
+               ...)))))))
 
 (visit-opcodes define-assembler)
 
+(eval-when (expand)
+
+  ;; Some operands are encoded using a restricted subset of the full
+  ;; 24-bit local address space, in order to make the bytecode more
+  ;; dense in the usual case that there are few live locals.  Here we
+  ;; define wrapper emitters that shuffle out-of-range operands into and
+  ;; out of the reserved range of locals [233,255].  This range is
+  ;; sufficient because these restricted operands are only present in
+  ;; the first word of an instruction.  Since 8 bits is the smallest
+  ;; slot-addressing operand size, that means we can fit 3 operands in
+  ;; the 24 bits of payload of the first word (the lower 8 bits being
+  ;; taken by the opcode).
+  ;;
+  ;; The result are wrapper emitters with the same arity,
+  ;; e.g. emit-cons* that wraps emit-cons.  We expose these wrappers as
+  ;; the public interface for emitting `cons' instructions.  That way we
+  ;; solve the problem fully and in just one place.  The only manual
+  ;; care that need be taken is in the exports list at the top of the
+  ;; file -- to be sure that we export the wrapper and not the wrapped
+  ;; emitter.
+
+  (define (shuffling-assembler name kind word0 word*)
+    (define (analyze-first-word)
+      (define-syntax op-case
+        (syntax-rules ()
+          ((_ type ((%type %kind arg ...) values) clause ...)
+           (if (and (eq? type '%type) (eq? kind '%kind))
+               (with-syntax (((arg ...) (generate-temporaries #'(arg ...))))
+                 #'((arg ...) values))
+               (op-case type clause ...)))
+          ((_ type)
+           #f)))
+      (op-case
+       word0
+       ((U8_U8_I16 ! a imm)
+        (values (if (< a (ash 1 8))  a (begin (emit-mov* asm 253 a) 253))
+                imm))
+       ((U8_U8_I16 <- a imm)
+        (values (if (< a (ash 1 8))  a 253)
+                imm))
+       ((U8_U12_U12 ! a b)
+        (values (if (< a (ash 1 12)) a (begin (emit-mov* asm 253 a) 253))
+                (if (< b (ash 1 12)) b (begin (emit-mov* asm 254 b) 254))))
+       ((U8_U12_U12 <- a b)
+        (values (if (< a (ash 1 12)) a 253)
+                (if (< b (ash 1 12)) b (begin (emit-mov* asm 254 b) 254))))
+       ((U8_U8_U8_U8 ! a b c)
+        (values (if (< a (ash 1 8))  a (begin (emit-mov* asm 253 a) 253))
+                (if (< b (ash 1 8))  b (begin (emit-mov* asm 254 b) 254))
+                (if (< c (ash 1 8))  c (begin (emit-mov* asm 255 c) 255))))
+       ((U8_U8_U8_U8 <- a b c)
+        (values (if (< a (ash 1 8))  a 253)
+                (if (< b (ash 1 8))  b (begin (emit-mov* asm 254 b) 254))
+                (if (< c (ash 1 8))  c (begin (emit-mov* asm 255 c) 255))))))
+
+    (define (tail-formals type)
+      (define-syntax op-case
+        (syntax-rules ()
+          ((op-case type (%type arg ...) clause ...)
+           (if (eq? type '%type)
+               (generate-temporaries #'(arg ...))
+               (op-case type clause ...)))
+          ((op-case type)
+           (error "unmatched type" type))))
+      (op-case type
+               (U8_U24 a b)
+               (U8_L24 a label)
+               (U32 a)
+               (I32 imm)
+               (A32 imm)
+               (B32)
+               (N32 label)
+               (S32 label)
+               (L32 label)
+               (LO32 label offset)
+               (X8_U24 a)
+               (X8_L24 label)
+               (B1_X7_L24 a label)
+               (B1_U7_L24 a b label)
+               (B1_X31 a)
+               (B1_X7_U24 a b)))
+
+    (define (shuffle-up dst)
+      (define-syntax op-case
+        (syntax-rules ()
+          ((_ type ((%type ...) exp) clause ...)
+           (if (memq type '(%type ...))
+               #'exp
+               (op-case type clause ...)))
+          ((_ type)
+           (error "unexpected type" type))))
+      (with-syntax ((dst dst))
+        (op-case
+         word0
+         ((U8_U8_I16 U8_U8_U8_U8)
+          (unless (< dst (ash 1 8))
+            (emit-mov* asm dst 253)))
+         ((U8_U12_U12)
+          (unless (< dst (ash 1 12))
+            (emit-mov* asm dst 253))))))
+
+    (and=>
+     (analyze-first-word)
+     (lambda (formals+shuffle)
+       (with-syntax ((emit-name (id-append name #'emit- name))
+                     (((formal0 ...) shuffle) formals+shuffle)
+                     (((formal* ...) ...) (map tail-formals word*)))
+         (with-syntax (((shuffle-up-dst ...)
+                        (if (eq? kind '<-)
+                            (syntax-case #'(formal0 ...) ()
+                              ((dst . _)
+                               (list (shuffle-up #'dst))))
+                            '())))
+           #'(lambda (asm formal0 ... formal* ... ...)
+               (call-with-values (lambda () shuffle)
+                 (lambda (formal0 ...)
+                   (emit-name asm formal0 ... formal* ... ...)))
+               shuffle-up-dst ...))))))
+
+  (define-syntax define-shuffling-assembler
+    (lambda (stx)
+      (syntax-case stx ()
+        ((_ #:except (except ...) name opcode kind word0 word* ...)
+         (cond
+          ((or-map (lambda (op) (eq? (syntax->datum #'name) op))
+                   (map syntax->datum #'(except ...)))
+           #'(begin))
+          ((shuffling-assembler #'name (syntax->datum #'kind)
+                                (syntax->datum #'word0)
+                                (map syntax->datum #'(word* ...)))
+           => (lambda (proc)
+                (with-syntax ((emit (id-append #'name
+                                               (id-append #'name #'emit- #'name)
+                                               #'*))
+                              (proc proc))
+                  #'(define emit
+                      (let ((emit proc))
+                        (hashq-set! assemblers 'name emit)
+                        emit)))))
+          (else #'(begin))))))))
+
+(visit-opcodes define-shuffling-assembler #:except (receive mov))
+
+;; Mov and receive are two special cases that can work without wrappers.
+;; Indeed it is important that they do so.
+
+(define (emit-mov* asm dst src)
+  (if (and (< dst (ash 1 12)) (< src (ash 1 12)))
+      (emit-mov asm dst src)
+      (emit-long-mov asm dst src)))
+
+(define (emit-receive* asm dst proc nlocals)
+  (if (and (< dst (ash 1 12)) (< proc (ash 1 12)))
+      (emit-receive asm dst proc nlocals)
+      (begin
+        (emit-receive-values asm proc #t 1)
+        (emit-mov* asm dst (1+ proc))
+        (emit-reset-frame asm nlocals))))
+
 (define (emit-text asm instructions)
   "Assemble @var{instructions} using the assembler @var{asm}.
-@var{instructions} is a sequence of RTL instructions, expressed as a
-list of lists.  This procedure can be called many times before calling
+@var{instructions} is a sequence of instructions, expressed as a list of
+lists.  This procedure can be called many times before calling
 @code{link-assembly}."
   (for-each (lambda (inst)
               (apply (or (hashq-ref assemblers (car inst))
@@ -509,7 +805,7 @@ list of lists.  This procedure can be called many times before calling
 ;;; to the table.
 ;;;
 
-(define-inlinable (immediate? x)
+(define-inline (immediate? x)
   "Return @code{#t} if @var{x} is immediate, and @code{#f} otherwise."
   (not (zero? (logand (object-address x) 6))))
 
@@ -580,7 +876,7 @@ table, its existing label is used directly."
      ((static-procedure? obj)
       `((static-patch! ,label 1 ,(static-procedure-code obj))))
      ((cache-cell? obj) '())
-     ((symbol? obj)
+     ((and (symbol? obj) (symbol-interned? obj))
       `((make-non-immediate 1 ,(recur (symbol->string obj)))
         (string->symbol 1 1)
         (static-set! 1 ,label 0)))
@@ -596,22 +892,33 @@ table, its existing label is used directly."
         (static-set! 1 ,label 0)))
      ((uniform-vector-backing-store? obj) '())
      ((simple-uniform-vector? obj)
-      `((static-patch! ,label 2
-                       ,(recur (make-uniform-vector-backing-store
-                                (uniform-array->bytevector obj)
-                                (if (bitvector? obj)
-                                    ;; Bitvectors are addressed in
-                                    ;; 32-bit units.
-                                    4
-                                    (uniform-vector-element-size obj)))))))
+      (let ((width (case (array-type obj)
+                     ((vu8 u8 s8) 1)
+                     ((u16 s16) 2)
+                     ;; Bitvectors are addressed in 32-bit units.
+                     ;; Although a complex number is 8 or 16 bytes wide,
+                     ;; it should be byteswapped in 4 or 8 byte units.
+                     ((u32 s32 f32 c32 b) 4)
+                     ((u64 s64 f64 c64) 8)
+                     (else
+                      (error "unhandled array type" obj)))))
+        `((static-patch! ,label 2
+                         ,(recur (make-uniform-vector-backing-store
+                                  (uniform-array->bytevector obj)
+                                  width))))))
+     ((array? obj)
+      `((static-patch! ,label 1 ,(recur (shared-array-root obj)))))
      (else
-      (error "don't know how to intern" obj))))
+      (if (asm-to-file? asm)
+          (error "don't know how to intern" obj)
+          `((make-short-immediate 1 ,(vlist-length (asm-constants asm)))
+            (vector-ref 1 0 1)
+            (static-set! 1 ,label 0))))))
   (cond
    ((immediate? obj) #f)
    ((vhash-assoc obj (asm-constants asm)) => cdr)
    (else
-    ;; Note that calling intern may mutate asm-constants and
-    ;; asm-constant-inits.
+    ;; Note that calling intern may mutate asm-constants and asm-inits.
     (let* ((label (gensym "constant"))
            (inits (intern obj label)))
       (set-asm-constants! asm (vhash-cons obj label (asm-constants asm)))
@@ -644,17 +951,18 @@ returned instead."
 ;;; some higher-level operations.
 ;;;
 
-(define-syntax define-macro-assembler
-  (lambda (x)
-    (syntax-case x ()
-      ((_ (name arg ...) body body* ...)
-       (with-syntax ((emit (id-append #'name #'emit- #'name)))
-         #'(begin
-             (define emit
-               (let ((emit (lambda (arg ...) body body* ...)))
-                 (hashq-set! assemblers 'name emit)
-                 emit))
-             (export emit)))))))
+(eval-when (expand)
+  (define-syntax define-macro-assembler
+    (lambda (x)
+      (syntax-case x ()
+        ((_ (name arg ...) body body* ...)
+         (with-syntax ((emit (id-append #'name #'emit- #'name)))
+           #'(begin
+               (define emit
+                 (let ((emit (lambda (arg ...) body body* ...)))
+                   (hashq-set! assemblers 'name emit)
+                   emit))
+               (export emit))))))))
 
 (define-macro-assembler (load-constant asm dst obj)
   (cond
@@ -701,6 +1009,7 @@ returned instead."
 ;(define-tc7-macro-assembler br-if-fluid 37)
 ;(define-tc7-macro-assembler br-if-dynamic-state 45)
 ;(define-tc7-macro-assembler br-if-frame 47)
+(define-tc7-macro-assembler br-if-keyword 53)
 ;(define-tc7-macro-assembler br-if-vm 55)
 ;(define-tc7-macro-assembler br-if-vm-cont 71)
 ;(define-tc7-macro-assembler br-if-rtl-program 69)
@@ -736,10 +1045,10 @@ returned instead."
                 "alist of keyword -> integer")
   (assert-match allow-other-keys? (? boolean?) "boolean")
   (assert-match nlocals (? integer?) "integer")
-  (assert-match alternate (or #f (? symbol?)) "#f or symbol")
+  (assert-match alternate (or #f (? exact-integer?) (? symbol?)) "#f or symbol")
   (let* ((meta (car (asm-meta asm)))
          (arity (make-arity req opt rest kw-indices allow-other-keys?
-                            (asm-start asm) #f))
+                            (asm-start asm) #f '()))
          ;; The procedure itself is in slot 0, in the standard calling
          ;; convention.  For procedure prologues, nreq includes the
          ;; procedure, so here we add 1.
@@ -758,8 +1067,22 @@ returned instead."
 
 (define-macro-assembler (end-arity asm)
   (let ((arity (car (meta-arities (car (asm-meta asm))))))
+    (set-arity-definitions! arity (reverse (arity-definitions arity)))
     (set-arity-high-pc! arity (asm-start asm))))
 
+;; As noted above, we reserve locals 253 through 255 for shuffling large
+;; operands.  However the calling convention has all arguments passed in
+;; a contiguous block.  This helper, called after the clause has been
+;; chosen and the keyword/optional/rest arguments have been processed,
+;; shuffles up arguments from slot 253 and higher into their final
+;; allocations.
+;;
+(define (shuffle-up-args asm nargs)
+  (when (> nargs 253)
+    (let ((slot (1- nargs)))
+      (emit-mov asm (+ slot 3) slot)
+      (shuffle-up-args asm (1- nargs)))))
+
 (define-macro-assembler (standard-prelude asm nreq nlocals alternate)
   (cond
    (alternate
@@ -769,7 +1092,8 @@ returned instead."
     (emit-assert-nargs-ee/locals asm nreq (- nlocals nreq)))
    (else
     (emit-assert-nargs-ee asm nreq)
-    (emit-alloc-frame asm nlocals))))
+    (emit-alloc-frame asm nlocals)))
+  (shuffle-up-args asm nreq))
 
 (define-macro-assembler (opt-prelude asm nreq nopt rest? nlocals alternate)
   (if alternate
@@ -782,7 +1106,8 @@ returned instead."
     (emit-br-if-nargs-gt asm (+ nreq nopt) alternate))
    (else
     (emit-assert-nargs-le asm (+ nreq nopt))))
-  (emit-alloc-frame asm nlocals))
+  (emit-alloc-frame asm nlocals)
+  (shuffle-up-args asm (+ nreq nopt (if rest? 1 0))))
 
 (define-macro-assembler (kw-prelude asm nreq nopt rest? kw-indices
                                     allow-other-keys? nlocals alternate)
@@ -803,7 +1128,8 @@ returned instead."
                       (+ nreq nopt)
                       ntotal
                       (intern-constant asm kw-indices))
-    (emit-alloc-frame asm nlocals)))
+    (emit-alloc-frame asm nlocals)
+    (shuffle-up-args asm ntotal)))
 
 (define-macro-assembler (label asm sym)
   (hashq-set! (asm-labels asm) sym (asm-start asm)))
@@ -811,6 +1137,13 @@ returned instead."
 (define-macro-assembler (source asm source)
   (set-asm-sources! asm (acons (asm-start asm) source (asm-sources asm))))
 
+(define-macro-assembler (definition asm name slot)
+  (let* ((arity (car (meta-arities (car (asm-meta asm)))))
+         (def (vector name
+                      slot
+                      (* (- (asm-start asm) (arity-low-pc arity)) 4))))
+    (set-arity-definitions! arity (cons def (arity-definitions arity)))))
+
 (define-macro-assembler (cache-current-module! asm module scope)
   (let ((mod-label (intern-module-cache-cell asm scope)))
     (emit-static-set! asm module mod-label 0)))
@@ -828,6 +1161,12 @@ 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)))))
 
 \f
 
@@ -876,7 +1215,10 @@ a procedure to do that and return its label.  Otherwise return
          (let ((label (gensym "init-constants")))
            (emit-text asm
                       `((begin-program ,label ())
-                        (assert-nargs-ee/locals 1 1)
+                        ,@(if (asm-to-file? asm)
+                              '((assert-nargs-ee/locals 1 1))
+                              '((assert-nargs-ee/locals 2 0)
+                                (mov 0 1)))
                         ,@(reverse inits)
                         (load-constant 1 ,*unspecified*)
                         (return 1)
@@ -900,9 +1242,10 @@ should be .data or .rodata), and return the resulting linker object.
   (define tc7-wide-stringbuf
     (+ tc7-stringbuf stringbuf-shared-flag stringbuf-wide-flag))
   (define tc7-ro-string (+ 21 #x200))
-  (define tc7-rtl-program 69)
+  (define tc7-program 69)
   (define tc7-bytevector 77)
   (define tc7-bitvector 95)
+  (define tc7-array 93)
 
   (let ((word-size (asm-word-size asm))
         (endianness (asm-endianness asm)))
@@ -927,6 +1270,8 @@ should be .data or .rodata), and return the resulting linker object.
         (* 4 word-size))
        ((uniform-vector-backing-store? x)
         (bytevector-length (uniform-vector-backing-store-bytes x)))
+       ((array? x)
+        (* word-size (+ 3 (* 3 (array-rank x)))))
        (else
         word-size)))
 
@@ -972,10 +1317,10 @@ should be .data or .rodata), and return the resulting linker object.
        ((static-procedure? obj)
         (case word-size
           ((4)
-           (bytevector-u32-set! buf pos tc7-rtl-program endianness)
+           (bytevector-u32-set! buf pos tc7-program endianness)
            (bytevector-u32-set! buf (+ pos 4) 0 endianness))
           ((8)
-           (bytevector-u64-set! buf pos tc7-rtl-program endianness)
+           (bytevector-u64-set! buf pos tc7-program endianness)
            (bytevector-u64-set! buf (+ pos 8) 0 endianness))
           (else (error "bad word size"))))
 
@@ -983,7 +1328,7 @@ should be .data or .rodata), and return the resulting linker object.
         (write-immediate asm buf pos #f))
 
        ((string? obj)
-        (let ((tag (logior tc7-ro-string (ash (string-length obj) 8))))
+        (let ((tag (logior tc7-ro-string (ash (string-length obj) 8)))) ; FIXME: unused?
           (case word-size
             ((4)
              (bytevector-u32-set! buf pos tc7-ro-string endianness)
@@ -1015,7 +1360,7 @@ should be .data or .rodata), and return the resulting linker object.
                 (write-constant-reference buf pos elt)
                 (lp (1+ i)))))))
 
-       ((symbol? obj)
+       ((and (symbol? obj) (symbol-interned? obj))
         (write-immediate asm buf pos #f))
 
        ((keyword? obj)
@@ -1027,7 +1372,7 @@ should be .data or .rodata), and return the resulting linker object.
        ((simple-uniform-vector? obj)
         (let ((tag (if (bitvector? obj)
                        tc7-bitvector
-                       (let ((type-code (uniform-vector-element-type-code obj)))
+                       (let ((type-code (array-type-code obj)))
                          (logior tc7-bytevector (ash type-code 7))))))
           (case word-size
             ((4)
@@ -1058,8 +1403,31 @@ should be .data or .rodata), and return the resulting linker object.
             ;; Need to swap units of element-size bytes
             (error "FIXME: Implement byte order swap"))))
 
+       ((array? obj)
+        (let-values
+            ;; array tag + rank + contp flag: see libguile/arrays.h .
+            (((tag) (logior tc7-array (ash (array-rank obj) 17) (ash 1 16)))
+             ((bv-set! bvs-set!)
+              (case word-size
+                ((4) (values bytevector-u32-set! bytevector-s32-set!))
+                ((8) (values bytevector-u64-set! bytevector-s64-set!))
+                (else (error "bad word size")))))
+          (bv-set! buf pos tag endianness)
+          (write-immediate asm buf (+ pos word-size) #f) ; root vector (fixed later)
+          (bv-set! buf (+ pos (* word-size 2)) 0 endianness) ; base
+          (let lp ((pos (+ pos (* word-size 3)))
+                   (bounds (array-shape obj))
+                   (incs (shared-array-increments obj)))
+            (when (pair? bounds)
+              (bvs-set! buf pos (first (first bounds)) endianness)
+              (bvs-set! buf (+ pos word-size) (second (first bounds)) endianness)
+              (bvs-set! buf (+ pos (* word-size 2)) (first incs) endianness)
+              (lp (+ pos (* 3 word-size)) (cdr bounds) (cdr incs))))))
+
        (else
-        (error "unrecognized object" obj))))
+        (if (asm-to-file? asm)
+            (error "unrecognized object" obj)
+            (write-constant-reference buf pos obj)))))
 
     (cond
      ((vlist-null? data) #f)
@@ -1193,6 +1561,67 @@ needed."
 
 \f
 
+;;;
+;;; Create the frame maps.  These maps are used by GC to identify dead
+;;; slots in pending call frames, to avoid marking them.  We only do
+;;; this when frame makes a non-tail call, as that is the common case.
+;;; Only the topmost frame will see a GC at any other point, but we mark
+;;; top frames conservatively as serializing live slot maps at every
+;;; instruction would take up too much space in the object file.
+;;;
+
+;; The .guile.frame-maps section starts with two packed u32 values: one
+;; indicating the offset of the first byte of the .rtl-text section, and
+;; another indicating the relative offset in bytes of the slots data.
+(define frame-maps-prefix-len 8)
+
+;; Each header is 8 bytes: 4 for the offset from .rtl_text, and 4 for
+;; the offset of the slot map from the beginning of the
+;; .guile.frame-maps section.  The length of a frame map depends on the
+;; frame size at the call site, and is not encoded into this section as
+;; it is available at run-time.
+(define frame-map-header-len 8)
+
+(define (link-frame-maps asm)
+  (define (map-byte-length proc-slot)
+    (ceiling-quotient (- proc-slot 2) 8))
+  (define (make-frame-maps maps count map-len)
+    (let* ((endianness (asm-endianness asm))
+           (header-pos frame-maps-prefix-len)
+           (map-pos (+ header-pos (* count frame-map-header-len)))
+           (bv (make-bytevector (+ map-pos map-len) 0)))
+      (bytevector-u32-set! bv 4 map-pos endianness)
+      (let lp ((maps maps) (header-pos header-pos) (map-pos map-pos))
+        (match maps
+          (()
+           (make-object asm '.guile.frame-maps bv
+                        (list (make-linker-reloc 'abs32/1 0 0 '.rtl-text))
+                        '() #:type SHT_PROGBITS #:flags SHF_ALLOC))
+          (((pos proc-slot . map) . maps)
+           (bytevector-u32-set! bv header-pos (* pos 4) endianness)
+           (bytevector-u32-set! bv (+ header-pos 4) map-pos endianness)
+           (let write-bytes ((map-pos map-pos)
+                             (map map)
+                             (byte-length (map-byte-length proc-slot)))
+             (if (zero? byte-length)
+                 (lp maps (+ header-pos frame-map-header-len) map-pos)
+                 (begin
+                   (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)
+    (() #f)
+    (in
+     (let lp ((in in) (out '()) (count 0) (map-len 0))
+       (match in
+         (() (make-frame-maps out count map-len))
+         (((and head (pos proc-slot . map)) . in)
+          (lp in (cons head out)
+              (1+ count)
+              (+ (map-byte-length proc-slot) map-len))))))))
+
+\f
+
 ;;;
 ;;; Linking other sections of the ELF file, like the dynamic segment,
 ;;; the symbol table, etc.
@@ -1200,16 +1629,20 @@ needed."
 
 ;; FIXME: Define these somewhere central, shared with C.
 (define *bytecode-major-version* #x0202)
-(define *bytecode-minor-version* 3)
+(define *bytecode-minor-version* 6)
 
-(define (link-dynamic-section asm text rw rw-init)
-  "Link the dynamic section for an ELF image with RTL text, given the
-writable data section @var{rw} needing fixup from the procedure with
-label @var{rw-init}.  @var{rw-init} may be false.  If @var{rw} is true,
-it will be added to the GC roots at runtime."
+(define (link-dynamic-section asm text rw rw-init frame-maps)
+  "Link the dynamic section for an ELF image with bytecode @var{text},
+given the writable data section @var{rw} needing fixup from the
+procedure with label @var{rw-init}.  @var{rw-init} may be false.  If
+@var{rw} is true, it will be added to the GC roots at runtime."
   (define-syntax-rule (emit-dynamic-section word-size %set-uword! reloc-type)
     (let* ((endianness (asm-endianness asm))
-           (bv (make-bytevector (* word-size (if rw (if rw-init 12 10) 6)) 0))
+           (words 6)
+           (words (if rw (+ words 4) words))
+           (words (if rw-init (+ words 2) words))
+           (words (if frame-maps (+ words 2) words))
+           (bv (make-bytevector (* word-size words) 0))
            (set-uword!
             (lambda (i uword)
               (%set-uword! bv (* i word-size) uword endianness)))
@@ -1220,30 +1653,25 @@ it will be added to the GC roots at runtime."
                                                     (* i word-size) 0 label)
                                  relocs))
               (%set-uword! bv (* i word-size) 0 endianness))))
-      (set-uword! 0 DT_GUILE_RTL_VERSION)
+      (set-uword! 0 DT_GUILE_VM_VERSION)
       (set-uword! 1 (logior (ash *bytecode-major-version* 16)
                             *bytecode-minor-version*))
       (set-uword! 2 DT_GUILE_ENTRY)
       (set-label! 3 '.rtl-text)
-      (cond
-       (rw
+      (when rw
         ;; Add roots to GC.
         (set-uword! 4 DT_GUILE_GC_ROOT)
         (set-label! 5 '.data)
         (set-uword! 6 DT_GUILE_GC_ROOT_SZ)
         (set-uword! 7 (bytevector-length (linker-object-bv rw)))
-        (cond
-         (rw-init
+        (when rw-init
           (set-uword! 8 DT_INIT)        ; constants
-          (set-label! 9 rw-init)
-          (set-uword! 10 DT_NULL)
-          (set-uword! 11 0))
-         (else
-          (set-uword! 8 DT_NULL)
-          (set-uword! 9 0))))
-       (else
-        (set-uword! 4 DT_NULL)
-        (set-uword! 5 0)))
+          (set-label! 9 rw-init)))
+      (when frame-maps
+        (set-uword! (- words 4) DT_GUILE_FRAME_MAPS)
+        (set-label! (- words 3) '.guile.frame-maps))
+      (set-uword! (- words 2) DT_NULL)
+      (set-uword! (- words 1) 0)
       (make-object asm '.dynamic bv relocs '()
                    #:type SHT_DYNAMIC #:flags SHF_ALLOC)))
   (case (asm-word-size asm)
@@ -1312,12 +1740,13 @@ it will be added to the GC roots at runtime."
 ;;;     uint32_t flags;
 ;;;     uint32_t nreq;
 ;;;     uint32_t nopt;
+;;;     uint32_t nlocals;
 ;;;   }
 ;;;
 ;;; All of the offsets and addresses are 32 bits.  We can expand in the
 ;;; future to use 64-bit offsets if appropriate, but there are other
-;;; aspects of RTL that constrain us to a total image that fits in 32
-;;; bits, so for the moment we'll simplify the problem space.
+;;; aspects of bytecode that constrain us to a total image that fits in
+;;; 32 bits, so for the moment we'll simplify the problem space.
 ;;;
 ;;; The following flags values are defined:
 ;;;
@@ -1332,12 +1761,15 @@ it will be added to the GC roots at runtime."
 ;;; is-case-lambda? flag set.  Their "offset" member links to an array
 ;;; of pointers into the associated .guile.arities.strtab string table,
 ;;; identifying the argument names.  This offset is relative to the
-;;; start of the .guile.arities section.  Links for required arguments
-;;; are first, in order, as uint32 values.  Next follow the optionals,
-;;; then the rest link if has-rest? is set, then a link to the "keyword
-;;; indices" literal if has-keyword-args? is set.  Unlike the other
-;;; links, the kw-indices link points into the data section, and is
-;;; relative to the ELF image as a whole.
+;;; start of the .guile.arities section.
+;;;
+;;; If the arity has keyword arguments -- if has-keyword-args? is set in
+;;; the flags -- the first uint32 pointed to by offset encodes a link to
+;;; the "keyword indices" literal, in the data section.  Then follow the
+;;; names for all locals, in order, as uleb128 values.  The required
+;;; arguments will be the first locals, followed by the optionals,
+;;; followed by the rest argument if if has-rest? is set.  The names
+;;; point into the associated string table section.
 ;;;
 ;;; Functions with no arities have no arities information present in the
 ;;; .guile.arities section.
@@ -1354,141 +1786,157 @@ it will be added to the GC roots at runtime."
 (define arities-prefix-len 4)
 
 ;; Length of an arity header, in bytes.
-(define arity-header-len (* 6 4))
-
-;; The offset of "offset" within arity header, in bytes.
-(define arity-header-offset-offset (* 2 4))
-
-(define-syntax-rule (pack-arity-flags has-rest? allow-other-keys?
-                                      has-keyword-args? is-case-lambda?
-                                      is-in-case-lambda?)
+(define arity-header-len (* 7 4))
+
+;; Some helpers.
+(define (put-uleb128 port val)
+  (let lp ((val val))
+    (let ((next (ash val -7)))
+      (if (zero? next)
+          (put-u8 port val)
+          (begin
+            (put-u8 port (logior #x80 (logand val #x7f)))
+            (lp next))))))
+
+(define (put-sleb128 port val)
+  (let lp ((val val))
+    (if (<= 0 (+ val 64) 127)
+        (put-u8 port (logand val #x7f))
+        (begin
+          (put-u8 port (logior #x80 (logand val #x7f)))
+          (lp (ash val -7))))))
+
+(define (port-position port)
+  (seek port 0 SEEK_CUR))
+
+(define-inline (pack-arity-flags has-rest? allow-other-keys?
+                                 has-keyword-args? is-case-lambda?
+                                 is-in-case-lambda?)
   (logior (if has-rest? (ash 1 0) 0)
           (if allow-other-keys? (ash 1 1) 0)
           (if has-keyword-args? (ash 1 2) 0)
           (if is-case-lambda? (ash 1 3) 0)
           (if is-in-case-lambda? (ash 1 4) 0)))
 
-(define (meta-arities-size meta)
-  (define (lambda-size arity)
-    (+ arity-header-len
-       (* 4    ;; name pointers
-          (+ (length (arity-req arity))
-             (length (arity-opt arity))
-             (if (arity-rest arity) 1 0)
-             (if (pair? (arity-kw-indices arity)) 1 0)))))
-  (define (case-lambda-size arities)
-    (fold +
-          arity-header-len ;; case-lambda header
-          (map lambda-size arities))) ;; the cases
-  (match (meta-arities meta)
-    (() 0)
-    ((arity) (lambda-size arity))
-    (arities (case-lambda-size arities))))
-
-(define (write-arity-headers metas bv endianness)
-  (define (write-arity-header* pos low-pc high-pc flags nreq nopt)
-    (bytevector-u32-set! bv pos (* low-pc 4) endianness)
-    (bytevector-u32-set! bv (+ pos 4) (* high-pc 4) endianness)
-    (bytevector-u32-set! bv (+ pos 8) 0 endianness) ; offset
-    (bytevector-u32-set! bv (+ pos 12) flags endianness)
-    (bytevector-u32-set! bv (+ pos 16) nreq endianness)
-    (bytevector-u32-set! bv (+ pos 20) nopt endianness))
-  (define (write-arity-header pos arity in-case-lambda?)
-    (write-arity-header* pos (arity-low-pc arity)
-                         (arity-high-pc arity)
-                         (pack-arity-flags (arity-rest arity)
-                                           (arity-allow-other-keys? arity)
-                                           (pair? (arity-kw-indices arity))
-                                           #f
-                                           in-case-lambda?)
-                         (length (arity-req arity))
-                         (length (arity-opt arity))))
-  (let lp ((metas metas) (pos arities-prefix-len) (offsets '()))
+(define (write-arities asm metas headers names-port strtab)
+  (define (write-header pos low-pc high-pc offset flags nreq nopt nlocals)
+    (unless (<= (+ nreq nopt) nlocals)
+      (error "forgot to emit definition instructions?"))
+    (bytevector-u32-set! headers pos (* low-pc 4) (asm-endianness asm))
+    (bytevector-u32-set! headers (+ pos 4) (* high-pc 4) (asm-endianness asm))
+    (bytevector-u32-set! headers (+ pos 8) offset (asm-endianness asm))
+    (bytevector-u32-set! headers (+ pos 12) flags (asm-endianness asm))
+    (bytevector-u32-set! headers (+ pos 16) nreq (asm-endianness asm))
+    (bytevector-u32-set! headers (+ pos 20) nopt (asm-endianness asm))
+    (bytevector-u32-set! headers (+ pos 24) nlocals (asm-endianness asm)))
+  (define (write-kw-indices kw-indices relocs)
+    ;; FIXME: Assert that kw-indices is already interned.
+    (if (pair? kw-indices)
+        (let ((pos (+ (bytevector-length headers)
+                      (port-position names-port)))
+              (label (intern-constant asm kw-indices)))
+          (put-bytevector names-port #vu8(0 0 0 0))
+          (cons (make-linker-reloc 'abs32/1 pos 0 label) relocs))
+        relocs))
+  (define (write-arity pos arity in-case-lambda? relocs)
+    (write-header pos (arity-low-pc arity)
+                  (arity-high-pc arity)
+                  ;; FIXME: Seems silly to add on bytevector-length of
+                  ;; headers, given the arities-prefix.
+                  (+ (bytevector-length headers) (port-position names-port))
+                  (pack-arity-flags (arity-rest arity)
+                                    (arity-allow-other-keys? arity)
+                                    (pair? (arity-kw-indices arity))
+                                    #f
+                                    in-case-lambda?)
+                  (length (arity-req arity))
+                  (length (arity-opt arity))
+                  (length (arity-definitions arity)))
+    (let ((relocs (write-kw-indices (arity-kw-indices arity) relocs)))
+      ;; Write local names.
+      (let lp ((definitions (arity-definitions arity)))
+        (match definitions
+          (() relocs)
+          ((#(name slot def) . definitions)
+           (let ((sym (if (symbol? name)
+                          (string-table-intern! strtab (symbol->string name))
+                          0)))
+             (put-uleb128 names-port sym)
+             (lp definitions)))))
+      ;; Now write their definitions.
+      (let lp ((definitions (arity-definitions arity)))
+        (match definitions
+          (() relocs)
+          ((#(name slot def) . definitions)
+           (put-uleb128 names-port def)
+           (put-uleb128 names-port slot)
+           (lp definitions))))))
+  (let lp ((metas metas) (pos arities-prefix-len) (relocs '()))
     (match metas
       (()
-       ;; Fill in the prefix.
-       (bytevector-u32-set! bv 0 pos endianness)
-       (values pos (reverse offsets)))
+       (unless (= pos (bytevector-length headers))
+         (error "expected to fully fill the bytevector"
+                pos (bytevector-length headers)))
+       relocs)
       ((meta . metas)
        (match (meta-arities meta)
-         (() (lp metas pos offsets))
+         (() (lp metas pos relocs))
          ((arity)
-          (write-arity-header pos arity #f)
           (lp metas
               (+ pos arity-header-len)
-              (acons arity (+ pos arity-header-offset-offset) offsets)))
+              (write-arity pos arity #f relocs)))
          (arities
           ;; Write a case-lambda header, then individual arities.
           ;; The case-lambda header's offset link is 0.
-          (write-arity-header* pos (meta-low-pc meta) (meta-high-pc meta)
-                               (pack-arity-flags #f #f #f #t #f) 0 0)
+          (write-header pos (meta-low-pc meta) (meta-high-pc meta) 0
+                        (pack-arity-flags #f #f #f #t #f) 0 0 0)
           (let lp* ((arities arities) (pos (+ pos arity-header-len))
-                    (offsets offsets))
+                    (relocs relocs))
             (match arities
-              (() (lp metas pos offsets))
+              (() (lp metas pos relocs))
               ((arity . arities)
-               (write-arity-header pos arity #t)
                (lp* arities
                     (+ pos arity-header-len)
-                    (acons arity
-                           (+ pos arity-header-offset-offset)
-                           offsets)))))))))))
-
-(define (write-arity-links asm bv pos arity-offset-pairs strtab)
-  (define (write-symbol sym pos)
-    (bytevector-u32-set! bv pos
-                         (string-table-intern! strtab (symbol->string sym))
-                         (asm-endianness asm))
-    (+ pos 4))
-  (define (write-kw-indices pos kw-indices)
-    ;; FIXME: Assert that kw-indices is already interned.
-    (make-linker-reloc 'abs32/1 pos 0
-                       (intern-constant asm kw-indices)))
-  (let lp ((pos pos) (pairs arity-offset-pairs) (relocs '()))
-    (match pairs
-      (()
-       (unless (= pos (bytevector-length bv))
-         (error "expected to fully fill the bytevector"
-                pos (bytevector-length bv)))
-       relocs)
-      (((arity . offset) . pairs)
-       (bytevector-u32-set! bv offset pos (asm-endianness asm))
-       (let ((pos (fold write-symbol
-                        pos
-                        (append (arity-req arity)
-                                (arity-opt arity)
-                                (cond
-                                 ((arity-rest arity) => list)
-                                 (else '()))))))
-         (match (arity-kw-indices arity)
-           (() (lp pos pairs relocs))
-           (kw-indices
-            (lp (+ pos 4)
-                pairs
-                (cons (write-kw-indices pos kw-indices) relocs)))))))))
+                    (write-arity pos arity #t relocs)))))))))))
 
 (define (link-arities asm)
+  (define (meta-arities-header-size meta)
+    (define (lambda-size arity)
+      arity-header-len)
+    (define (case-lambda-size arities)
+      (fold +
+            arity-header-len            ;; case-lambda header
+            (map lambda-size arities))) ;; the cases
+    (match (meta-arities meta)
+      (() 0)
+      ((arity) (lambda-size arity))
+      (arities (case-lambda-size arities))))
+
+  (define (bytevector-append a b)
+    (let ((out (make-bytevector (+ (bytevector-length a)
+                                   (bytevector-length b)))))
+      (bytevector-copy! a 0 out 0 (bytevector-length a))
+      (bytevector-copy! b 0 out (bytevector-length a) (bytevector-length b))
+      out))
+
   (let* ((endianness (asm-endianness asm))
          (metas (reverse (asm-meta asm)))
-         (size (fold (lambda (meta size)
-                       (+ size (meta-arities-size meta)))
-                     arities-prefix-len
-                     metas))
+         (header-size (fold (lambda (meta size)
+                              (+ size (meta-arities-header-size meta)))
+                            arities-prefix-len
+                            metas))
          (strtab (make-string-table))
-         (bv (make-bytevector size 0)))
-    (let ((kw-indices-relocs
-           (call-with-values
-               (lambda ()
-                 (write-arity-headers metas bv endianness))
-             (lambda (pos arity-offset-pairs)
-               (write-arity-links asm bv pos arity-offset-pairs strtab)))))
-      (let ((strtab (make-object asm '.guile.arities.strtab
-                                 (link-string-table! strtab)
-                                 '() '()
-                                 #:type SHT_STRTAB #:flags 0)))
+         (headers (make-bytevector header-size 0)))
+    (bytevector-u32-set! headers 0 (bytevector-length headers) endianness)
+    (let-values (((names-port get-name-bv) (open-bytevector-output-port)))
+      (let* ((relocs (write-arities asm metas headers names-port strtab))
+             (strtab (make-object asm '.guile.arities.strtab
+                                  (link-string-table! strtab)
+                                  '() '()
+                                  #:type SHT_STRTAB #:flags 0)))
         (values (make-object asm '.guile.arities
-                             bv
-                             kw-indices-relocs '()
+                             (bytevector-append headers (get-name-bv))
+                             relocs '()
                              #:type SHT_PROGBITS #:flags 0
                              #:link (elf-section-index
                                      (linker-object-section strtab)))
@@ -1635,26 +2083,6 @@ it will be added to the GC roots at runtime."
       (bytevector-u64-set! bv 0 val (asm-endianness asm))
       (put-bytevector port bv)))
 
-  (define (put-uleb128 port val)
-    (let lp ((val val))
-      (let ((next (ash val -7)))
-        (if (zero? next)
-            (put-u8 port val)
-            (begin
-              (put-u8 port (logior #x80 (logand val #x7f)))
-              (lp next))))))
-
-  (define (put-sleb128 port val)
-    (let lp ((val val))
-      (if (<= 0 (+ val 64) 127)
-          (put-u8 port (logand val #x7f))
-          (begin
-            (put-u8 port (logior #x80 (logand val #x7f)))
-            (lp (ash val -7))))))
-
-  (define (port-position port)
-    (seek port 0 SEEK_CUR))
-
   (define (meta->subprogram-die meta)
     `(subprogram
       (@ ,@(cond
@@ -1728,11 +2156,13 @@ it will be added to the GC roots at runtime."
              (lp sources
                  ;; Guile line and column numbers are 0-indexed, but
                  ;; they are 1-indexed for DWARF.
-                 (cons (list pc
-                             (if file (intern-file file) 0)
-                             (if line (1+ line))
-                             (if col (1+ col)))
-                       out))))
+                 (if (and line col)
+                     (cons (list pc
+                                 (if (string? file) (intern-file file) 0)
+                                 (1+ line)
+                                 (1+ col))
+                           out)
+                     out))))
           (()
            ;; Compilation unit header for .debug_line.  We write in
            ;; DWARF 2 format because more tools understand it than DWARF
@@ -1874,13 +2304,11 @@ it will be added to the GC roots at runtime."
         ('language (language-name->code val))
         ('stmt-list val)))
 
-    (define (exact-integer? val)
-      (and (number? val) (integer? val) (exact? val)))
-
     (define (choose-form attr val code)
       (cond
        ((string? val) 'strp)
        ((eq? attr 'stmt-list) 'sec-offset)
+       ((eq? attr 'low-pc) 'addr)
        ((exact-integer? code)
         (cond
          ((< code 0) 'sleb128)
@@ -1889,7 +2317,6 @@ it will be added to the GC roots at runtime."
          ((<= code #xffffffff) 'data4)
          ((<= code #xffffffffffffffff) 'data8)
          (else 'uleb128)))
-       ((symbol? val) 'addr)
        (else (error "unhandled case" attr val code))))
 
     (define (add-die-relocation! kind sym)
@@ -1969,7 +2396,8 @@ it will be added to the GC roots at runtime."
                 ;; Link text object after constants, so that the
                 ;; constants initializer gets included.
                 ((text) (link-text-object asm))
-                ((dt) (link-dynamic-section asm text rw rw-init))
+                ((frame-maps) (link-frame-maps asm))
+                ((dt) (link-dynamic-section asm text rw rw-init frame-maps))
                 ((symtab strtab) (link-symtab (linker-object-section text) asm))
                 ((arities arities-strtab) (link-arities asm))
                 ((docstrs docstrs-strtab) (link-docstrs asm))
@@ -1978,7 +2406,8 @@ it will be added to the GC roots at runtime."
                 ;; sections adds entries to the string table.
                 ((shstrtab) (link-shstrtab asm)))
     (filter identity
-            (list text ro rw dt symtab strtab arities arities-strtab
+            (list text ro frame-maps rw dt symtab strtab
+                  arities arities-strtab
                   docstrs docstrs-strtab procprops
                   dinfo dabbrev dstrtab dloc dline
                   shstrtab))))
@@ -1995,4 +2424,7 @@ it will be added to the GC roots at runtime."
 The result is a bytevector, by default linked so that read-only and
 writable data are on separate pages.  Pass @code{#:page-aligned? #f} to
 disable this behavior."
-  (link-elf (link-objects asm) #:page-aligned? page-aligned?))
+  (define (asm-constant-vector asm)
+    (list->vector (reverse (map car (vlist->list (asm-constants asm))))))
+  (let ((bv (link-elf (link-objects asm) #:page-aligned? page-aligned?)))
+    (cons bv (if (asm-to-file? asm) #f (asm-constant-vector asm)))))