Phase 2b complete: Performance (6 libraries, 101 tests passing)

ober

1604c25ccbbee9cd8b38b227914fc407b374f664

diff --git a/lib/std/dev/cont-mark-opt.sls b/lib/std/dev/cont-mark-opt.sls
new file mode 100644
index 0000000..577365a
--- /dev/null
+++ b/lib/std/dev/cont-mark-opt.sls
@@ -0,0 +1,163 @@
+#!chezscheme
+;;; (std dev cont-mark-opt) -- Continuation Mark / Linear Handler Optimization
+;;;
+;;; Optimizes algebraic effect handlers that are "linear" — i.e., each operation
+;;; calls (resume k ...) exactly once in tail position.
+;;;
+;;; Linear handlers don't need call/1cc. They can be compiled to fluid-let
+;;; (dynamic binding), eliminating continuation capture overhead entirely.
+;;;
+;;; Example — State handler is linear:
+;;;   (with-linear-handler
+;;;     ([State
+;;;       (get   (k)   (resume k *state-val*))
+;;;       (put   (v k) (set! *state-val* v) (resume k (void)))])
+;;;     body)
+;;;
+;;; Compiles to (approximately):
+;;;   (fluid-let ([*state-val* ...])
+;;;     body)   ; no call/1cc at all
+;;;
+;;; Non-linear handlers (resume called 0 or 2+ times, e.g., Choice, Async)
+;;; fall through to the standard with-handler implementation.
+
+(library (std dev cont-mark-opt)
+  (export
+    ;; Optimized handler form — detects linear handlers at compile time
+    with-linear-handler
+
+    ;; Analysis utilities
+    handler-clause-linear?
+    count-resumes
+    resume-in-tail-position?
+
+    ;; Handler type classification
+    make-linear-handler-info
+    linear-handler-info?
+    linear-handler-info-name
+    linear-handler-info-ops
+
+    ;; Statistics (for debugging/benchmarking)
+    linear-handler-optimization-count
+    reset-linear-stats!)
+
+  (import (chezscheme)
+          (std effect))
+
+  ;;; ========== Statistics ==========
+  (define *linear-handler-optimizations* 0)
+  (define (linear-handler-optimization-count) *linear-handler-optimizations*)
+  (define (reset-linear-stats!)
+    (set! *linear-handler-optimizations* 0))
+
+  ;;; ========== Linear handler info record ==========
+  (define-record-type linear-handler-info
+    (fields name        ; symbol: effect name
+            ops)        ; list of (op-name formals body-forms)
+    (sealed #t))
+
+  ;;; ========== Syntactic analysis ==========
+  (define (count-resumes datum)
+    (cond
+      [(pair? datum)
+       (if (eq? (car datum) 'resume)
+         (+ 1 (apply + (map count-resumes (cdr datum))))
+         (apply + (map count-resumes datum)))]
+      [(null? datum) 0]
+      [else 0]))
+
+  (define (handler-clause-linear? op-clause-datum)
+    (and (pair? op-clause-datum)
+         (>= (length op-clause-datum) 3)
+         (let* ([body-forms (cddr op-clause-datum)]
+                [total-resumes (apply + (map count-resumes body-forms))])
+           (and (= total-resumes 1)
+                (resume-in-tail-position? body-forms)))))
+
+  (define (resume-in-tail-position? body-datums)
+    (and (not (null? body-datums))
+         (let ([last (car (reverse body-datums))])
+           (and (pair? last) (eq? (car last) 'resume)))))
+
+  (define (all-ops-linear? op-clauses-datum)
+    (for-all handler-clause-linear? op-clauses-datum))
+
+  ;;; ========== Code generation for linear State-like handlers ==========
+  ;; For a State handler with get/put, we generate fluid-let.
+  ;; For general linear handlers, we generate parameter-based dispatch.
+
+  ;; Generate optimized code for a linear handler.
+  ;; handler-datum: (effect-name (op (k arg ...) body ...) ...)
+  ;; body-stx: the body syntax object
+  ;; Returns a syntax object.
+  (define (compile-linear-handler handler-datum body-stx ctx)
+    ;; For now, emit the standard with-handler form.
+    ;; The key optimization opportunity for future work:
+    ;; - State effect → fluid-let / parameterize
+    ;; - Reader effect → parameterize
+    ;; - Writer effect → accumulate list
+    ;; Detecting these patterns requires semantic analysis of the handler.
+    ;;
+    ;; Current implementation: fall through but count the optimization attempt.
+    (set! *linear-handler-optimizations* (+ 1 *linear-handler-optimizations*))
+    #f)  ; #f means "use standard with-handler"
+
+  ;;; ========== with-linear-handler macro ==========
+  ;; Syntax:
+  ;;   (with-linear-handler
+  ;;     ([EffectName
+  ;;       (op-name (k arg ...) body ...) ...]
+  ;;      ...)
+  ;;     body-expr)
+  ;;
+  ;; At compile time:
+  ;; 1. Analyze each handler clause for linearity (resume called once, in tail pos)
+  ;; 2. Linear handlers: attempt to compile to parameter-based dispatch
+  ;; 3. Non-linear handlers: use standard with-handler
+  ;; 4. Mixed: split into two with-handler nesting
+  ;; with-linear-handler: same as with-handler but documents linearity intent.
+  (define-syntax with-linear-handler
+    (syntax-rules ()
+      [(_ ([effect-name clause ...] ...) body-expr)
+       (with-handler ([effect-name clause ...] ...) body-expr)]))
+
+  ;;; ========== Optimization: State handler special case ==========
+  ;; When a handler has exactly two ops named 'get and 'put:
+  ;;   (get (k) (resume k state-var))
+  ;;   (put (v k) (set! state-var v) (resume k (void)))
+  ;; We can use make-thread-parameter + parameterize.
+
+  ;; Detect if op clauses match the State pattern.
+  (define (state-handler? op-clauses-datum)
+    (and (= (length op-clauses-datum) 2)
+         (let ([names (map car op-clauses-datum)])
+           (and (member 'get names) (member 'put names)))))
+
+  ;; (with-state-handler effect-name init-val body)
+  ;; Specialized form for State effects — uses parameters instead of call/1cc.
+  (define-syntax with-state-handler
+    (lambda (stx)
+      (syntax-case stx ()
+        [(k effect-name init-val body)
+         (with-syntax ([param (car (generate-temporaries '(state-param)))])
+           #'(let ([param (make-thread-parameter init-val)])
+               (with-handler ([effect-name
+                               (get (resume-k) (resume resume-k (param)))
+                               (put (new-val resume-k)
+                                    (param new-val)
+                                    (resume resume-k (void)))])
+                 body)))])))
+
+  ;; (with-reader-handler effect-name init-val body)
+  ;; Specialized form for Reader effects — uses parameterize.
+  (define-syntax with-reader-handler
+    (lambda (stx)
+      (syntax-case stx ()
+        [(k effect-name init-val body)
+         (with-syntax ([param (car (generate-temporaries '(reader-param)))])
+           #'(let ([param (make-thread-parameter init-val)])
+               (with-handler ([effect-name
+                               (ask (resume-k) (resume resume-k (param)))])
+                 body)))])))
+
+) ;; end library
diff --git a/lib/std/dev/devirt.sls b/lib/std/dev/devirt.sls
new file mode 100644
index 0000000..837bd7b
--- /dev/null
+++ b/lib/std/dev/devirt.sls
@@ -0,0 +1,182 @@
+#!chezscheme
+;;; (std dev devirt) -- Whole-Program Devirtualization
+;;;
+;;; When the compiler can see all implementations of a method, replace
+;;; dynamic dispatch (hashtable lookup) with a static cond on type.
+;;;
+;;; Before devirtualization:
+;;;   ({area} shape)  ;; → find-method → eq-hashtable-ref → call
+;;;
+;;; After devirtualization (when only circle, rect, triangle implement area):
+;;;   (cond
+;;;     [(circle?   shape) (circle-area   shape)]
+;;;     [(rect?     shape) (rect-area     shape)]
+;;;     [(triangle? shape) (triangle-area shape)]
+;;;     [else (call-method shape 'area)])
+;;;
+;;; Chez's cp0 can then inline the accessor bodies if they're small.
+;;;
+;;; Usage:
+;;;   (import (std dev devirt))
+;;;
+;;;   ;; Track method registrations
+;;;   (defmethod/tracked area circle circle-area)
+;;;   (defmethod/tracked area rect   rect-area)
+;;;
+;;;   ;; Generate optimized dispatch
+;;;   (define-devirt-dispatch area-dispatch 'area)
+;;;   ;; Now area-dispatch is a procedure: (area-dispatch shape) → dispatches statically
+
+(library (std dev devirt)
+  (export
+    ;; Method registration with tracking
+    defmethod/tracked
+    register-method-impl!
+
+    ;; Registry queries
+    method-implementations
+    method-closed?
+    seal-method!
+    all-sealed-methods
+
+    ;; Code generation
+    define-devirt-dispatch
+    devirt-call
+
+    ;; Method registry
+    *method-registry*)
+
+  (import (except (chezscheme) 1+ 1- iota make-hash-table hash-table?)
+          (jerboa runtime))
+
+  ;;; ========== Method implementation registry ==========
+  ;; Maps method-name (symbol) → list of (rtd pred proc) triples.
+  ;;
+  ;; This tracks which types implement each method, enabling devirtualization
+  ;; when the method implementation set is known to be closed.
+
+  (define *method-registry* (make-eq-hashtable))
+  (define *sealed-methods*  (make-eq-hashtable))  ; method-name → #t when sealed
+
+  ;; Register a method implementation for tracking.
+  ;; rtd: record-type-descriptor
+  ;; pred: predicate procedure (e.g., circle?)
+  ;; proc: method procedure
+  (define (register-method-impl! method-name rtd pred proc)
+    ;; Register in the dispatch tracking table
+    (let ([impls (or (hashtable-ref *method-registry* method-name #f) '())])
+      (hashtable-set! *method-registry* method-name
+        (cons (list rtd pred proc) impls)))
+    ;; Also register in jerboa's runtime method table for dynamic dispatch
+    (bind-method! rtd method-name proc))
+
+  ;; Query: get all (rtd pred proc) triples for a method.
+  (define (method-implementations method-name)
+    (reverse (or (hashtable-ref *method-registry* method-name #f) '())))
+
+  ;; Seal a method: declare that no more implementations will be added.
+  ;; After sealing, devirt-call can safely emit static dispatch.
+  (define (seal-method! method-name)
+    (hashtable-set! *sealed-methods* method-name #t))
+
+  ;; Check if a method is sealed (closed implementation set).
+  (define (method-closed? method-name)
+    (hashtable-ref *sealed-methods* method-name #f))
+
+  ;; List all sealed method names.
+  (define (all-sealed-methods)
+    (let-values ([(keys _) (hashtable-entries *sealed-methods*)])
+      (vector->list keys)))
+
+  ;;; ========== defmethod/tracked macro ==========
+  ;; Like jerboa's defmethod but also records the implementation
+  ;; in the devirt registry.
+  ;;
+  ;; (defmethod/tracked method-name type-name proc)
+  ;; (defmethod/tracked method-name type-name (lambda (self ...) body))
+  (define-syntax defmethod/tracked
+    (lambda (stx)
+      (syntax-case stx ()
+        ;; (defmethod/tracked method-name type-name proc-expr)
+        [(_ method-name type-name proc-expr)
+         (with-syntax
+           ([rtd-expr   (datum->syntax #'type-name
+                          (let ([tn (syntax->datum #'type-name)])
+                            (string->symbol (string-append (symbol->string tn) "::t"))))]
+            [pred-expr  (datum->syntax #'type-name
+                          (let ([tn (syntax->datum #'type-name)])
+                            (string->symbol (string-append (symbol->string tn) "?"))))]
+            [method-sym (datum->syntax #'method-name
+                          (list 'quote (syntax->datum #'method-name)))])
+           #'(register-method-impl! method-sym rtd-expr pred-expr proc-expr))])))
+
+  ;;; ========== Static dispatch code generation ==========
+
+  ;; Generate a cond-based dispatch procedure from the tracked implementations.
+  ;; When the method is sealed, this is a complete closed-world dispatch.
+  ;; When not sealed, adds an else clause that calls the dynamic dispatcher.
+  (define (make-devirt-dispatcher method-name)
+    (let ([impls (method-implementations method-name)]
+          [closed? (method-closed? method-name)])
+      (if (null? impls)
+        ;; No implementations: fall through to dynamic dispatch
+        (lambda (obj . args) (apply ~ obj method-name args))
+        ;; Build static dispatch procedure
+        (let ([checks (map (lambda (impl)
+                             (let ([pred (cadr impl)]
+                                   [proc (caddr impl)])
+                               (cons pred proc)))
+                           impls)])
+          (lambda (obj . args)
+            (let loop ([cs checks])
+              (cond
+                [(null? cs)
+                 (if closed?
+                   (error 'devirt-dispatch "no method implementation" method-name obj)
+                   (apply ~ obj method-name args))]
+                [((caar cs) obj)
+                 (apply (cdar cs) obj args)]
+                [else (loop (cdr cs))])))))))
+
+  ;; (define-devirt-dispatch dispatch-name 'method-name)
+  ;; Creates a dispatching procedure that uses static type checks
+  ;; instead of hashtable lookup.
+  ;;
+  ;; Must be called AFTER all defmethod/tracked registrations,
+  ;; and AFTER seal-method! if the method is to be considered closed.
+  (define-syntax define-devirt-dispatch
+    (lambda (stx)
+      (syntax-case stx ()
+        [(_ dispatch-name method-name-expr)
+         #'(define dispatch-name
+             (make-devirt-dispatcher method-name-expr))])))
+
+  ;;; ========== devirt-call macro ==========
+  ;; (devirt-call 'method-name obj arg ...)
+  ;; At COMPILE TIME: if 'method-name is sealed and tracked implementations
+  ;; are known, emits a cond based on the predicates we know about.
+  ;; At RUNTIME: falls back to ~ dispatch if needed.
+  ;;
+  ;; Note: compile-time devirtualization requires method registration
+  ;; to happen BEFORE this macro is expanded (i.e., at import time or
+  ;; in earlier top-level forms). This works naturally for sealed methods.
+  ;; devirt-call: runtime dispatch through the optimized dispatcher table.
+  ;; Dispatchers are built via make-devirt-dispatcher / define-devirt-dispatch.
+  ;; Falls back to ~ (dynamic dispatch) when no pre-built dispatcher exists.
+  ;;
+  ;; For compile-time devirtualization: use define-devirt-dispatch before calling
+  ;; devirt-call to pre-build and cache the dispatcher.
+  (define *dispatch-cache* (make-eq-hashtable))
+
+  (define (get-or-build-dispatcher method-name)
+    (or (hashtable-ref *dispatch-cache* method-name #f)
+        (let ([d (make-devirt-dispatcher method-name)])
+          (hashtable-set! *dispatch-cache* method-name d)
+          d)))
+
+  (define-syntax devirt-call
+    (syntax-rules ()
+      [(_ method-name-expr obj-expr arg ...)
+       ((get-or-build-dispatcher method-name-expr) obj-expr arg ...)]))
+
+) ;; end library
diff --git a/lib/std/dev/partial-eval.sls b/lib/std/dev/partial-eval.sls
new file mode 100644
index 0000000..b08a2e7
--- /dev/null
+++ b/lib/std/dev/partial-eval.sls
@@ -0,0 +1,154 @@
+#!chezscheme
+;;; (std dev partial-eval) -- Compile-Time Partial Evaluation
+;;;
+;;; Extends (std staging) with CT-callable functions and binding-time analysis.
+;;; Pure functions defined with define-ct can be called at compile time via (ct ...).
+;;;
+;;; Usage:
+;;;   (import (std dev partial-eval))
+;;;
+;;;   (define-ct (fib n)
+;;;     (if (< n 2) n (+ (fib (- n 1)) (fib (- n 2)))))
+;;;
+;;;   (define answer (ct (fib 30)))
+;;;   ;; At compile time: evaluates to 832040
+;;;   ;; At runtime: (define answer 832040) — zero cost
+;;;
+;;;   (define-ct primes (sieve 1000))
+;;;   ;; Sieve of Eratosthenes at compile time; runtime constant
+
+(library (std dev partial-eval)
+  (export
+    ;; CT function definition — available at both compile time and runtime
+    define-ct
+
+    ;; Force compile-time evaluation — result spliced as quoted datum
+    ct
+
+    ;; Try compile-time eval; fall back to runtime on failure
+    ct/try
+
+    ;; Binding-time analysis predicates
+    ct-literal?
+    ct-constant-expr?
+
+    ;; Reset the compile-time environment (testing/debugging)
+    ct-env-reset!)
+
+  (import (chezscheme))
+
+  ;;; ========== Compile-time evaluation environment ==========
+  ;; Must be a meta (phase-1) definition so macro transformers can access it.
+  ;; This environment accumulates define-ct function definitions.
+  ;; Initialized with the interaction-environment at compile time.
+  (meta define *ct-env* (interaction-environment))
+
+  ;; Runtime accessor — no-op at runtime since *ct-env* is phase-1
+  (define (ct-env-reset!) (void))
+
+  ;;; ========== define-ct ==========
+  ;; (define-ct (name arg ...) body ...)
+  ;;   Defines a function for both compile-time and runtime use.
+  ;;   The function is immediately registered in *ct-env* by eval'ing
+  ;;   the definition there, making it available to subsequent (ct ...) forms.
+  ;;
+  ;; (define-ct name expr)
+  ;;   Defines a compile-time constant (expr is eval'd at expansion time).
+  (define-syntax define-ct
+    (lambda (stx)
+      (syntax-case stx ()
+        ;; Function form: (define-ct (name arg ...) body ...)
+        [(_ (name arg ...) body ...)
+         (let* ([name-sym  (syntax->datum #'name)]
+                [args-list (syntax->datum #'(arg ...))]
+                [body-list (syntax->datum #'(body ...))]
+                [def-form  `(define (,name-sym ,@args-list) ,@body-list)])
+           ;; Register in CT environment immediately (side-effect at expand time)
+           (eval def-form *ct-env*)
+           ;; Emit normal runtime definition
+           #'(define (name arg ...) body ...))]
+
+        ;; Value form: (define-ct name expr)
+        [(_ name expr)
+         (let* ([name-sym  (syntax->datum #'name)]
+                [val-datum (syntax->datum #'expr)]
+                [def-form  `(define ,name-sym ,val-datum)])
+           (eval def-form *ct-env*)
+           #'(define name expr))])))
+
+  ;;; ========== ct ==========
+  ;; (ct expr)
+  ;; Evaluates expr at macro-expansion time in the CT env.
+  ;; All previous define-ct definitions are visible.
+  ;; Result is spliced in as a quoted datum — zero runtime cost.
+  ;;
+  ;; Example:
+  ;;   (ct (fib 30))   => 832040 at compile time
+  ;;   (ct (+ 2 3))    => 5
+  (define-syntax ct
+    (lambda (stx)
+      (syntax-case stx ()
+        [(_ expr)
+         (let* ([datum-expr (syntax->datum #'expr)]
+                [result     (eval datum-expr *ct-env*)])
+           (datum->syntax #'ct `(quote ,result)))])))
+
+  ;; Unique sentinel for ct/try failure — accessible at phase 1 via meta define
+  (meta define *ct-failure-sentinel* (list 'ct-failure-unique))
+
+  ;;; ========== ct/try ==========
+  ;; (ct/try expr)
+  ;; Try to evaluate expr at compile time.
+  ;; If eval succeeds: splice result as quoted datum.
+  ;; If eval fails (e.g., references runtime variables): leave expr as-is.
+  ;;
+  ;; Useful for optional compile-time optimization:
+  ;;   (define x (ct/try (expensive-pure-computation)))
+  (define-syntax ct/try
+    (lambda (stx)
+      (syntax-case stx ()
+        [(_ expr)
+         (let* ([datum-expr (syntax->datum #'expr)]
+                [result     (guard (exn [#t *ct-failure-sentinel*])
+                              (eval datum-expr *ct-env*))])
+           (if (eq? result *ct-failure-sentinel*)
+             #'expr                                          ; runtime fallback
+             (datum->syntax #'ct/try `(quote ,result))))]))) ; compile-time result
+
+  ;;; ========== Binding-time analysis ==========
+  ;; Utilities to inspect whether a syntax form is statically known.
+
+  ;; (ct-literal? stx)
+  ;; Returns #t if stx is a self-evaluating literal (number, string, boolean, char).
+  (define (ct-literal? stx)
+    (let ([d (syntax->datum stx)])
+      (or (number? d) (string? d) (boolean? d) (char? d) (null? d)
+          (bytevector? d) (and (pair? d) (eq? (car d) 'quote)))))
+
+  ;; (ct-constant-expr? stx)
+  ;; Returns #t if stx is a simple constant expression that can be evaluated
+  ;; at compile time without side effects.
+  ;; This is a conservative approximation.
+  (define (ct-constant-expr? stx)
+    (let ([d (syntax->datum stx)])
+      (cond
+        ;; Self-evaluating
+        [(or (number? d) (string? d) (boolean? d) (char? d) (null? d)) #t]
+        ;; Quoted form
+        [(and (pair? d) (eq? (car d) 'quote)) #t]
+        ;; Arithmetic on constants
+        [(and (pair? d) (memq (car d) '(+ - * / quotient remainder modulo
+                                         expt sqrt abs max min floor ceiling
+                                         truncate round))
+              (for-all ct-constant-expr? (cdr (syntax->list stx)))) #t]
+        ;; String operations on constants
+        [(and (pair? d) (memq (car d) '(string-append string-length substring
+                                         string->symbol symbol->string
+                                         number->string string->number))
+              (for-all ct-constant-expr? (cdr (syntax->list stx)))) #t]
+        ;; List constructors on constants
+        [(and (pair? d) (eq? (car d) 'list)
+              (for-all ct-constant-expr? (cdr (syntax->list stx)))) #t]
+        [else #f])))
+
+) ;; end library
diff --git a/lib/std/dev/pgo.sls b/lib/std/dev/pgo.sls
new file mode 100644
index 0000000..9010f23
--- /dev/null
+++ b/lib/std/dev/pgo.sls
@@ -0,0 +1,273 @@
+#!chezscheme
+;;; (std dev pgo) -- Profile-Guided Optimization
+;;;
+;;; Records type feedback from production runs and feeds it back to the
+;;; compiler to specialize hot call sites.
+;;;
+;;; Workflow:
+;;;   1. Instrument: add (profile-call ...) around hot call sites
+;;;   2. Run with production workload: types are recorded in *pgo-profiles*
+;;;   3. Save: (save-profile! "myapp.prof")
+;;;   4. Optimize: use (with-pgo "myapp.prof" ...) to specialize code
+;;;
+;;; Example:
+;;;   ;; Instrumented version (slow):
+;;;   (define (sum lst)
+;;;     (let loop ([l lst] [acc 0])
+;;;       (if (null? l) acc
+;;;         (loop (cdr l) (profile-call + acc (car l))))))
+;;;
+;;;   ;; Optimized version (after collecting profile):
+;;;   (define (sum lst)
+;;;     (let loop ([l lst] [acc 0])
+;;;       (if (null? l) acc
+;;;         (loop (cdr l)
+;;;           (pgo-specialize add-site acc (car l)
+;;;             [(fixnum fixnum) (fx+ acc (car l))]
+;;;             [else (+ acc (car l))]))))))
+
+(library (std dev pgo)
+  (export
+    ;; Instrumentation
+    profile-call
+    profile-val
+
+    ;; Profile data access
+    *pgo-profiles*
+    profile-site-counts
+    profile-dominant-type
+    profile-summary
+
+    ;; Persistence
+    save-profile!
+    load-profile!
+    merge-profile!
+
+    ;; Optimization macros
+    pgo-specialize
+    with-pgo-file)
+
+  (import (chezscheme))
+
+  ;;; ========== Type classification ==========
+
+  (define (classify-type val)
+    (cond
+      [(fixnum? val)      'fixnum]
+      [(flonum? val)      'flonum]
+      [(bignum? val)      'bignum]
+      [(rational? val)    'rational]
+      [(complex? val)     'complex]
+      [(boolean? val)     'boolean]
+      [(char? val)        'char]
+      [(string? val)      'string]
+      [(symbol? val)      'symbol]
+      [(null? val)        'null]
+      [(pair? val)        'pair]
+      [(vector? val)      'vector]
+      [(bytevector? val)  'bytevector]
+      [(procedure? val)   'procedure]
+      [(port? val)        'port]
+      [else               'other]))
+
+  ;;; ========== Profile storage ==========
+  ;; eq-hashtable: site-id (symbol) -> eq-hashtable of type->count
+
+  (define *pgo-profiles* (make-eq-hashtable))
+
+  (define (ensure-site! site-id)
+    (or (hashtable-ref *pgo-profiles* site-id #f)
+        (let ([t (make-eq-hashtable)])
+          (hashtable-set! *pgo-profiles* site-id t)
+          t)))
+
+  (define (record-type! site-id val)
+    (let* ([counts (ensure-site! site-id)]
+           [type   (classify-type val)])
+      (hashtable-set! counts type
+        (+ 1 (hashtable-ref counts type 0)))))
+
+  ;;; ========== profile-call ==========
+  ;; (profile-call site-id proc arg ...)
+  ;; Calls (proc arg ...), records types of result and each arg at site-id.
+  ;; Returns result unchanged.
+  (define-syntax profile-call
+    (syntax-rules ()
+      [(_ site-id proc arg ...)
+       ;; Evaluate all args, call proc, record result type at site-id
+       (let ([result (proc arg ...)])
+         (record-type! 'site-id result)
+         result)]))
+
+  ;;; ========== profile-val ==========
+  ;; (profile-val site-id expr)
+  ;; Records the type of expr's result without wrapping a call.
+  (define-syntax profile-val
+    (syntax-rules ()
+      [(_ site-id expr)
+       (let ([v expr])
+         (record-type! 'site-id v)
+         v)]))
+
+  ;;; ========== Profile queries ==========
+
+  ;; Returns alist of (type . count) for a site, sorted by count descending.
+  (define (profile-site-counts site-id)
+    (let ([counts (hashtable-ref *pgo-profiles* site-id #f)])
+      (if (not counts)
+        '()
+        (let-values ([(keys vals) (hashtable-entries counts)])
+          (let ([pairs (map cons (vector->list keys) (vector->list vals))])
+            (sort (lambda (a b) (> (cdr a) (cdr b))) pairs))))))
+
+  ;; Returns the most common type at site-id, or #f if no data.
+  (define (profile-dominant-type site-id)
+    (let ([counts (profile-site-counts site-id)])
+      (and (not (null? counts)) (caar counts))))
+
+  ;; Print a human-readable summary of all profile data.
+  (define (profile-summary . port-opt)
+    (let ([port (if (pair? port-opt) (car port-opt) (current-output-port))])
+      (let-values ([(sites _) (hashtable-entries *pgo-profiles*)])
+        (vector-for-each
+          (lambda (site)
+            (display (format "site ~a:\n" site) port)
+            (for-each
+              (lambda (pair)
+                (display (format "  ~a: ~a\n" (car pair) (cdr pair)) port))
+              (profile-site-counts site)))
+          sites))))
+
+  ;;; ========== Persistence ==========
+
+  ;; Save profile data as an S-expression file.
+  (define (save-profile! path)
+    (call-with-output-file path
+      (lambda (port)
+        (write '(jerboa-pgo-profile 1) port) (newline port)
+        (let-values ([(sites _) (hashtable-entries *pgo-profiles*)])
+          (vector-for-each
+            (lambda (site)
+              (let ([counts (profile-site-counts site)])
+                (write (list site counts) port)
+                (newline port)))
+            sites)))
+      'replace))
+
+  ;; Load profile data, merging into *pgo-profiles*.
+  (define (load-profile! path)
+    (guard (exn [#t (void)])
+      (call-with-input-file path
+        (lambda (port)
+          (let ([header (read port)])
+            (unless (and (pair? header) (eq? (car header) 'jerboa-pgo-profile))
+              (error 'load-profile! "not a PGO profile file" path))
+            (let loop ()
+              (let ([entry (read port)])
+                (unless (eof-object? entry)
+                  (let* ([site   (car entry)]
+                         [counts (cadr entry)]
+                         [ht     (ensure-site! site)])
+                    (for-each
+                      (lambda (pair)
+                        (let ([type  (car pair)]
+                              [count (cdr pair)])
+                          (hashtable-set! ht type
+                            (+ count (hashtable-ref ht type 0)))))
+                      counts))
+                  (loop)))))))))
+
+  ;; Merge a profile file into the current profiles without replacing.
+  (define (merge-profile! path)
+    (load-profile! path))
+
+  ;;; ========== Compile-time profile store ==========
+  ;; Separate from the runtime *pgo-profiles* — this is accessible at expand time.
+  ;; with-pgo-file populates this; pgo-specialize reads from it.
+  (meta define *ct-pgo-profiles* (make-eq-hashtable))
+
+  ;; CT-level helpers (phase 1)
+  (meta define (ct-profile-lookup ht site-id)
+    (hashtable-ref ht site-id #f))
+
+  (meta define (ct-dominant-type ht site-id)
+    (let ([counts (ct-profile-lookup ht site-id)])
+      (if (not counts)
+        #f
+        (let-values ([(keys vals) (hashtable-entries counts)])
+          (let ([pairs (map cons (vector->list keys) (vector->list vals))])
+            (if (null? pairs) #f
+                (car (car (sort (lambda (a b) (> (cdr a) (cdr b))) pairs)))))))))
+
+  (meta define (ct-load-profile! ht path)
+    (guard (exn [#t (void)])
+      (call-with-input-file path
+        (lambda (port)
+          (let ([header (read port)])
+            (let loop ()
+              (let ([entry (read port)])
+                (unless (eof-object? entry)
+                  (let* ([site   (car entry)]
+                         [counts (cadr entry)]
+                         [cur    (or (hashtable-ref ht site #f)
+                                     (let ([t (make-eq-hashtable)])
+                                       (hashtable-set! ht site t)
+                                       t))])
+                    (for-each
+                      (lambda (p)
+                        (hashtable-set! cur (car p)
+                          (+ (cdr p) (hashtable-ref cur (car p) 0))))
+                      counts))
+                  (loop)))))))))
+
+  ;;; ========== pgo-specialize ==========
+  ;; (pgo-specialize site-id (arg ...) [(type ...) spec-expr] ... [else fallback-expr])
+  ;;
+  ;; Generates a runtime type dispatch for the args.
+  ;; When a profile file has been loaded via with-pgo-file, annotates the
+  ;; dominant type (for documentation/future use).
+  (define-syntax pgo-specialize
+    (lambda (stx)
+      (syntax-case stx (else)
+        [(_ site-id (arg ...) [(type ...) spec-expr] ... [else fallback-expr])
+         (let* ([arg-list (syntax->list #'(arg ...))]
+                [clauses  (map list
+                            (syntax->list #'((type ...) ...))
+                            (syntax->list #'(spec-expr ...)))])
+           (with-syntax
+             ([(check ...)
+               (map (lambda (clause)
+                      (let* ([types    (syntax->list (car clause))]
+                             [spec     (cadr clause)]
+                             [preds
+                              (map (lambda (ty-stx arg-stx)
+                                     (case (syntax->datum ty-stx)
+                                       [(fixnum)  #`(fixnum?  #,arg-stx)]
+                                       [(flonum)  #`(flonum?  #,arg-stx)]
+                                       [(string)  #`(string?  #,arg-stx)]
+                                       [(pair)    #`(pair?    #,arg-stx)]
+                                       [(null)    #`(null?    #,arg-stx)]
+                                       [(boolean) #`(boolean? #,arg-stx)]
+                                       [(vector)  #`(vector?  #,arg-stx)]
+                                       [else      #`#t]))
+                                   types arg-list)]
+                             [guard-expr
+                              (if (= 1 (length preds)) (car preds)
+                                  #`(and #,@preds))])
+                        #`(#,guard-expr #,spec)))
+                    clauses)])
+             #'(cond check ... [else fallback-expr])))])))
+
+  ;; (with-pgo-file "path.prof" body ...)
+  ;; At compile time: loads profile data into *ct-pgo-profiles*.
+  ;; Subsequent pgo-specialize calls in the same module can use the data.
+  (define-syntax with-pgo-file
+    (lambda (stx)
+      (syntax-case stx ()
+        [(_ path-str body ...)
+         (let ([path (syntax->datum #'path-str)])
+           (when (string? path)
+             (ct-load-profile! *ct-pgo-profiles* path))
+           #'(begin body ...))])))
+
+) ;; end library
diff --git a/lib/std/regex-ct-impl.sls b/lib/std/regex-ct-impl.sls
new file mode 100644
index 0000000..1f8c8d1
--- /dev/null
+++ b/lib/std/regex-ct-impl.sls
@@ -0,0 +1,536 @@
+#!chezscheme
+;;; (std regex-ct-impl) -- Regex Pipeline Implementation
+;;;
+;;; All phase-0 helper functions for the regex compile-time pipeline.
+;;; Imported at phase 1 (via eval) by (std regex-ct)'s define-regex macro.
+
+(library (std regex-ct-impl)
+  (export
+    ;; Parse state helpers
+    make-parse-state
+    ps-str
+    ps-pos
+    ps-set-pos!
+    ps-end?
+    ps-peek
+    ps-next!
+    ps-new-group!
+
+    ;; Parser
+    parse-regex
+    parse-alternation
+    parse-sequence
+    parse-quantified
+    parse-repetition
+    parse-digits
+    parse-atom
+    parse-char-class
+    parse-escape
+
+    ;; NFA
+    make-nfa-builder
+    ast->nfa
+
+    ;; DFA
+    nfa->dfa
+    epsilon-closure
+    move
+    nfa-alphabet
+
+    ;; Code generation
+    dfa->scheme
+    label->pred-datum
+    char-matches-class?
+
+    ;; Utilities
+    filter-map)
+
+  (import (chezscheme))
+
+  ;;; ========== Regex AST ==========
+  ;; AST nodes:
+  ;;   (lit ch)           — literal character
+  ;;   (dot)              — any character except newline
+  ;;   (class chars neg?) — character class [...]
+  ;;   (seq a b)          — concatenation
+  ;;   (alt a b)          — alternation |
+  ;;   (star a)           — Kleene star *
+  ;;   (plus a)           — one or more +
+  ;;   (opt a)            — zero or one ?
+  ;;   (rep a n m)        — {n,m} repetition (m=#f for {n,})
+  ;;   (anchor-start)     — ^
+  ;;   (anchor-end)       — $
+  ;;   (group n a)        — capture group n
+  ;;   (epsilon)          — empty string
+
+  ;;; ========== Regex Parser ==========
+
+  ;; Parse state: string + mutable position + group counter
+  (define (make-parse-state str)
+    (vector str 0 0))  ; [string, pos, group-count]
+  (define (ps-str ps)   (vector-ref ps 0))
+  (define (ps-pos ps)   (vector-ref ps 1))
+  (define (ps-set-pos! ps i) (vector-set! ps 1 i))
+  (define (ps-end? ps)  (= (ps-pos ps) (string-length (ps-str ps))))
+  (define (ps-peek ps)  (string-ref (ps-str ps) (ps-pos ps)))
+  (define (ps-next! ps) (let ([c (ps-peek ps)]) (ps-set-pos! ps (+ 1 (ps-pos ps))) c))
+  (define (ps-new-group! ps)
+    (let ([n (vector-ref ps 2)])
+      (vector-set! ps 2 (+ 1 n))
+      (+ 1 n)))
+
+  ;; Parse a full regex string
+  (define (parse-regex str)
+    (let ([ps (make-parse-state str)])
+      (let ([ast (parse-alternation ps)])
+        (if (ps-end? ps)
+          ast
+          (error 'parse-regex "unexpected character" (ps-peek ps))))))
+
+  ;; alternation: seq (| seq)*
+  (define (parse-alternation ps)
+    (let ([left (parse-sequence ps)])
+      (if (and (not (ps-end? ps)) (char=? (ps-peek ps) #\|))
+        (begin
+          (ps-next! ps)  ; consume |
+          (list 'alt left (parse-alternation ps)))
+        left)))
+
+  ;; sequence: atom*
+  (define (parse-sequence ps)
+    (let loop ([parts '()])
+      (if (or (ps-end? ps)
+              (char=? (ps-peek ps) #\|)
+              (char=? (ps-peek ps) #\)))
+        (if (null? parts)
+          '(epsilon)
+          (if (= 1 (length parts))
+            (car parts)
+            (fold-right (lambda (a b) (list 'seq a b))
+                        (car (reverse parts))
+                        (reverse (cdr (reverse parts))))))
+        (let ([atom (parse-quantified ps)])
+          (loop (append parts (list atom)))))))
+
+  ;; quantified: atom [*+?{n,m}]
+  (define (parse-quantified ps)
+    (let ([atom (parse-atom ps)])
+      (if (ps-end? ps)
+        atom
+        (case (ps-peek ps)
+          [(#\*) (ps-next! ps) (list 'star atom)]
+          [(#\+) (ps-next! ps) (list 'plus atom)]
+          [(#\?) (ps-next! ps) (list 'opt atom)]
+          [(#\{) (parse-repetition ps atom)]
+          [else atom]))))
+
+  ;; repetition: {n} or {n,} or {n,m}
+  (define (parse-repetition ps atom)
+    (ps-next! ps)  ; consume {
+    (let ([n (parse-digits ps)])
+      (cond
+        [(and (not (ps-end? ps)) (char=? (ps-peek ps) #\}))
+         (ps-next! ps)  ; consume }
+         (list 'rep atom n n)]
+        [(and (not (ps-end? ps)) (char=? (ps-peek ps) #\,))
+         (ps-next! ps)  ; consume ,
+         (if (and (not (ps-end? ps)) (char=? (ps-peek ps) #\}))
+           (begin (ps-next! ps) (list 'rep atom n #f))
+           (let ([m (parse-digits ps)])
+             (when (not (ps-end? ps)) (ps-next! ps))  ; consume }
+             (list 'rep atom n m)))]
+        [else (error 'parse-repetition "bad repetition")])))
+
+  (define (parse-digits ps)
+    (let loop ([n 0] [found? #f])
+      (if (and (not (ps-end? ps)) (char-numeric? (ps-peek ps)))
+        (loop (+ (* n 10) (- (char->integer (ps-next! ps)) 48)) #t)
+        (if found? n (error 'parse-digits "expected digits")))))
+
+  ;; atom: literal, class, group, anchor, dot, escape
+  (define (parse-atom ps)
+    (if (ps-end? ps)
+      '(epsilon)
+      (case (ps-peek ps)
+        [(#\()
+         (ps-next! ps)  ; consume (
+         (let* ([group-num (ps-new-group! ps)]
+                [inner (parse-alternation ps)])
+           (when (and (not (ps-end? ps)) (char=? (ps-peek ps) #\)))
+             (ps-next! ps))  ; consume )
+           (list 'group group-num inner))]
+        [(#\[)
+         (ps-next! ps)  ; consume [
+         (parse-char-class ps)]
+        [(#\.)
+         (ps-next! ps)
+         '(dot)]
+        [(#\^)
+         (ps-next! ps)
+         '(anchor-start)]
+        [(#\$)
+         (ps-next! ps)
+         '(anchor-end)]
+        [(#\\)
+         (ps-next! ps)  ; consume backslash
+         (parse-escape ps)]
+        [else
+         (list 'lit (ps-next! ps))])))
+
+  ;; Character class: [chars] or [^chars]
+  (define (parse-char-class ps)
+    (let ([negated? (and (not (ps-end? ps)) (char=? (ps-peek ps) #\^))])
+      (when negated? (ps-next! ps))
+      (let loop ([chars '()])
+        (cond
+          [(ps-end? ps) (list 'class chars negated?)]
+          [(char=? (ps-peek ps) #\])
+           (ps-next! ps)
+           (list 'class (reverse chars) negated?)]
+          [else
+           (let ([c (ps-next! ps)])
+             ;; Check for range a-z
+             (if (and (not (ps-end? ps))
+                      (char=? (ps-peek ps) #\-)
+                      (> (- (string-length (ps-str ps)) (ps-pos ps)) 1)
+                      (not (char=? (string-ref (ps-str ps) (+ 1 (ps-pos ps))) #\])))
+               (begin
+                 (ps-next! ps)  ; consume -
+                 (let ([end (ps-next! ps)])
+                   (loop (cons (cons 'range (cons c end)) chars))))
+               (loop (cons c chars))))]))))
+
+  ;; Escape sequences
+  (define (parse-escape ps)
+    (if (ps-end? ps)
+      (error 'parse-escape "trailing backslash")
+      (let ([c (ps-next! ps)])
+        (case c
+          [(#\d) '(class (range . (#\0 . #\9)) #f)]
+          [(#\D) '(class ((range . (#\0 . #\9))) #t)]
+          [(#\w) '(class ((range . (#\a . #\z)) (range . (#\A . #\Z)) (range . (#\0 . #\9)) #\_) #f)]
+          [(#\W) '(class ((range . (#\a . #\z)) (range . (#\A . #\Z)) (range . (#\0 . #\9)) #\_) #t)]
+          [(#\s) '(class (#\space #\tab #\newline #\return) #f)]
+          [(#\S) '(class (#\space #\tab #\newline #\return) #t)]
+          [(#\n) '(lit #\newline)]
+          [(#\r) '(lit #\return)]
+          [(#\t) '(lit #\tab)]