wingo pushed a commit to branch main
in repository guile.

commit 1f70d597dbc38585f0eeb6f5d8ca4ae62ed6ec3a
Author: Andy Wingo <wi...@pobox.com>
AuthorDate: Thu Aug 24 11:36:10 2023 +0200

    Allow functions to mark themselves as maybe-unused
    
    * module/language/tree-il/analyze.scm (<reference-graph>): Oh my
    goodness, constructor args were reversed relative to field order.
    Constructor use was consistent but it was terribly confusing; fixed and
    updated uses.
    (unused-toplevel-analysis): Add ability for functions to mark themselves
    as "maybe-unused"; such functions won't cause unused toplevel warnings.
    
    * module/language/tree-il/compile-bytecode.scm (sanitize-meta):
    (compile-closure):
    * module/language/tree-il/compile-cps.scm (sanitize-meta): Prevent
    maybe-unused from being needlessly written out to the binary.
---
 module/language/tree-il/analyze.scm          | 84 ++++++++++++++++++----------
 module/language/tree-il/compile-bytecode.scm | 11 +++-
 module/language/tree-il/compile-cps.scm      |  2 +-
 3 files changed, 66 insertions(+), 31 deletions(-)

diff --git a/module/language/tree-il/analyze.scm 
b/module/language/tree-il/analyze.scm
index c949b5f54..e9a803919 100644
--- a/module/language/tree-il/analyze.scm
+++ b/module/language/tree-il/analyze.scm
@@ -194,7 +194,7 @@ given `tree-il' element."
 ;; definition we're currently in).  The second part (`refs' below) is
 ;; effectively a graph from which we can determine unused top-level 
definitions.
 (define-record-type <reference-graph>
-  (make-reference-graph refs defs toplevel-context)
+  (make-reference-graph defs refs toplevel-context)
   reference-graph?
   (defs             reference-graph-defs) ;; ((NAME . LOC) ...)
   (refs             reference-graph-refs) ;; ((REF-CONTEXT REF ...) ...)
@@ -257,46 +257,66 @@ given `tree-il' element."
 
 (define unused-toplevel-analysis
   ;; Report unused top-level definitions that are not exported.
-  (let ((add-ref-from-context
-         (lambda (graph name)
-           ;; Add an edge CTX -> NAME in GRAPH.
-           (let* ((refs     (reference-graph-refs graph))
-                  (defs     (reference-graph-defs graph))
-                  (ctx      (reference-graph-toplevel-context graph))
-                  (ctx-refs (or (and=> (vhash-assq ctx refs) cdr) '())))
-             (make-reference-graph (vhash-consq ctx (cons name ctx-refs) refs)
-                                   defs ctx)))))
+  (let ()
+    (define initial-graph
+      (make-reference-graph vlist-null vlist-null #f))
+
+    (define (add-def graph name src)
+      (match graph
+        (($ <reference-graph> defs refs ctx)
+         (make-reference-graph (vhash-consq name src defs) refs name))))
+
+    (define (add-ref graph pred succ)
+      ;; Add a ref edge PRED -> SUCC in GRAPH.
+      (match graph
+        (($ <reference-graph> defs refs ctx)
+         (let* ((succs (match (vhash-assq pred refs)
+                         ((pred . succs) succs)
+                         (#f '())))
+                (refs (vhash-consq pred (cons succ succs) refs)))
+           (make-reference-graph defs refs ctx)))))
+
+    (define (add-ref-from-context graph name)
+      ;; Add a ref edge from the current context to NAME in GRAPH.
+      (add-ref graph (reference-graph-toplevel-context graph) name))
+
+    (define (add-root-ref graph name)
+      ;; Add a ref edge to NAME from the root, because its metadata is
+      ;; marked maybe-unused.
+      (add-ref graph #f name))
+
     (define (macro-variable? name env)
       (and (module? env)
            (let ((var (module-variable env name)))
              (and var (variable-bound? var)
                   (macro? (variable-ref var))))))
 
+    (define (maybe-unused? metadata)
+      (assq 'maybe-unused metadata))
+
     (make-tree-analysis
      (lambda (x graph env locs)
        ;; Going down into X.
-       (let ((ctx  (reference-graph-toplevel-context graph))
-             (refs (reference-graph-refs graph))
-             (defs (reference-graph-defs graph)))
-         (match x
-           (($ <toplevel-ref> src mod name)
-            (add-ref-from-context graph name))
-           (($ <toplevel-define> src mod name expr)
-            (let ((refs refs)
-                  (defs (vhash-consq name (or src (find pair? locs))
-                                     defs)))
-              (make-reference-graph refs defs name)))
-           (($ <toplevel-set> src mod name expr)
-            (add-ref-from-context graph name))
-           (_ graph))))
+       (match x
+         (($ <toplevel-ref> src mod name)
+          (add-ref-from-context graph name))
+         (($ <toplevel-define> src mod name expr)
+          (let ((graph (add-def graph name (or src (find pair? locs)))))
+            (match expr
+              (($ <lambda> src (? maybe-unused?) body)
+               (add-root-ref graph name))
+              (_ graph))))
+         (($ <toplevel-set> src mod name expr)
+          (add-ref-from-context graph name))
+         (_ graph)))
 
      (lambda (x graph env locs)
        ;; Leaving X's scope.
        (match x
          (($ <toplevel-define>)
-          (let ((refs (reference-graph-refs graph))
-                (defs (reference-graph-defs graph)))
-            (make-reference-graph refs defs #f)))
+          (match graph
+            (($ <reference-graph> defs refs ctx)
+             (make-reference-graph defs refs #f))))
          (_ graph)))
 
      (lambda (graph env)
@@ -308,9 +328,15 @@ given `tree-il' element."
        ;; private bindings.  FIXME: The `make-syntax-transformer' calls don't
        ;; contain any literal `toplevel-ref' of the global bindings they use so
        ;; this strategy fails.
+       (define exports (make-hash-table))
+       (when (module? env)
+         (module-for-each (lambda (name var) (hashq-set! exports var name))
+                          (module-public-interface env)))
        (define (exported? name)
          (if (module? env)
-             (module-variable (module-public-interface env) name)
+             (and=> (module-variable env name)
+                    (lambda (var)
+                      (hashq-ref exports var)))
              #t))
 
        (let-values (((public-defs private-defs)
@@ -332,7 +358,7 @@ given `tree-il' element."
                                    (warning 'unused-toplevel loc name))))
                            unused))))
 
-     (make-reference-graph vlist-null vlist-null #f))))
+     initial-graph)))
 
 
 ;;;
diff --git a/module/language/tree-il/compile-bytecode.scm 
b/module/language/tree-il/compile-bytecode.scm
index 71f22dde7..c4c9bf614 100644
--- a/module/language/tree-il/compile-bytecode.scm
+++ b/module/language/tree-il/compile-bytecode.scm
@@ -724,6 +724,15 @@ in the frame with for the lambda-case clause @var{clause}."
              (visit body))  ; Body.
         temporary-count)))) ; Temporaries.
 
+(define (sanitize-meta meta)
+  (match meta
+    (() '())
+    (((k . v) . meta)
+     (let ((meta (sanitize-meta meta)))
+       (case k
+         ((maybe-unused) meta)
+         (else (acons k v meta)))))))
+
 (define (compile-closure asm closure assigned? lookup-closure)
   (define-record-type <env>
     (make-env prev name id idx closure? boxed? next-local)
@@ -1375,7 +1384,7 @@ in the frame with for the lambda-case clause 
@var{clause}."
   (match closure
     (($ <closure> label ($ <lambda> src meta body) module-scope free)
      (when src (emit-source asm src))
-     (emit-begin-program asm label meta)
+     (emit-begin-program asm label (sanitize-meta meta))
      (emit-clause #f body module-scope free)
      (emit-end-program asm))))
 
diff --git a/module/language/tree-il/compile-cps.scm 
b/module/language/tree-il/compile-cps.scm
index ae5df10ed..ff22fa5ca 100644
--- a/module/language/tree-il/compile-cps.scm
+++ b/module/language/tree-il/compile-cps.scm
@@ -1315,7 +1315,7 @@ use as the proc slot."
     (((k . v) . meta)
      (let ((meta (sanitize-meta meta)))
        (case k
-         ((arg-representations noreturn return-type) meta)
+         ((arg-representations noreturn return-type maybe-unused) meta)
          (else (acons k v meta)))))))
 
 ;;; The conversion from Tree-IL to CPS essentially wraps every

Reply via email to