From 76cf25fc12ea4bf1ce9c9fdb450e74b96873082f Mon Sep 17 00:00:00 2001 From: Matthew Flatt Date: Tue, 19 Aug 2008 00:50:52 +0000 Subject: [PATCH] document 'mzc --decompile' svn: r11317 --- collects/compiler/decompile.ss | 60 +++++++++------- collects/compiler/zo-parse.ss | 2 +- collects/scribblings/mzc/decompile.scrbl | 81 ++++++++++++++++++++++ collects/scribblings/mzc/mzc.scrbl | 4 ++ collects/scribblings/reference/pairs.scrbl | 2 - 5 files changed, 121 insertions(+), 28 deletions(-) create mode 100644 collects/scribblings/mzc/decompile.scrbl diff --git a/collects/compiler/decompile.ss b/collects/compiler/decompile.ss index 95aab6ec6a..3beb726fbd 100644 --- a/collects/compiler/decompile.ss +++ b/collects/compiler/decompile.ss @@ -115,7 +115,7 @@ ,(let-values ([(globs defns) (decompile-prefix prefix)]) `(let () ,@defns - ,(decompile-expr rhs globs '(#%globals)))))] + ,(decompile-form rhs globs '(#%globals)))))] [(struct def-for-syntax (ids rhs prefix max-let-depth)) `(define-values-for-syntax ,ids ,(let-values ([(globs defns) (decompile-prefix prefix)]) @@ -188,24 +188,13 @@ (if clear? `(#%sfs-clear ,e) e)))] - [(struct lam (name flags num-params rest? closure-map max-let-depth body)) - (let ([vars (for/list ([i (in-range num-params)]) - (gensym (format "arg~a-" i)))] - [rest-vars (if rest? (list (gensym 'rest)) null)] - [captures (map (lambda (v) - (list-ref/protect stack v)) - (vector->list closure-map))]) - `(lambda (,@vars . ,(if rest? - (car rest-vars) - null)) - ,@(if name - `(',name) - null) - ,@(if (null? captures) - null - `('(captures: ,@captures))) - ,(decompile-expr body globs (append captures - (append vars rest-vars)))))] + [(? lam?) + `(lambda . ,(decompile-lam expr globs stack))] + [(struct case-lam (name lams)) + `(case-lambda + ,@(map (lambda (lam) + (decompile-lam lam globs stack)) + lams))] [(struct let-one (rhs body)) (let ([id (or (extract-id rhs) (gensym 'local))]) @@ -222,12 +211,12 @@ ,(decompile-expr body globs (append vars stack)))))] [(struct let-rec (procs body)) `(begin - (set!-rec-values ,(for/list ([p (in-list procs)] - [i (in-naturals)]) - (list-ref/protect stack i)) - ,@(map (lambda (proc) - (decompile-expr proc globs stack)) - procs)) + (#%set!-rec-values ,(for/list ([p (in-list procs)] + [i (in-naturals)]) + (list-ref/protect stack i)) + ,@(map (lambda (proc) + (decompile-expr proc globs stack)) + procs)) ,(decompile-expr body globs stack))] [(struct install-value (count pos boxes? rhs body)) `(begin @@ -274,6 +263,27 @@ '???)] [else `(quote ,expr)])) +(define (decompile-lam expr globs stack) + (match expr + [(struct lam (name flags num-params rest? closure-map max-let-depth body)) + (let ([vars (for/list ([i (in-range num-params)]) + (gensym (format "arg~a-" i)))] + [rest-vars (if rest? (list (gensym 'rest)) null)] + [captures (map (lambda (v) + (list-ref/protect stack v)) + (vector->list closure-map))]) + `((,@vars . ,(if rest? + (car rest-vars) + null)) + ,@(if (and name (not (null? name))) + `(',name) + null) + ,@(if (null? captures) + null + `('(captures: ,@captures))) + ,(decompile-expr body globs (append captures + (append vars rest-vars)))))])) + ;; ---------------------------------------- #; diff --git a/collects/compiler/zo-parse.ss b/collects/compiler/zo-parse.ss index 98528bbfa4..7bcefcbde9 100644 --- a/collects/compiler/zo-parse.ss +++ b/collects/compiler/zo-parse.ss @@ -235,7 +235,7 @@ [(0) (read-define-values v)] [(1) (read-define-syntax v)] [(2) (read-set! v)] - [(3) (read-case-lambda v)] + [(3) v] ; a case-lam already [(4) (read-begin0 v)] [(5) (read-boxenv v)] [(6) (read-module-wrap v)] diff --git a/collects/scribblings/mzc/decompile.scrbl b/collects/scribblings/mzc/decompile.scrbl new file mode 100644 index 0000000000..4d177abdcb --- /dev/null +++ b/collects/scribblings/mzc/decompile.scrbl @@ -0,0 +1,81 @@ +#lang scribble/doc +@(require scribble/manual + "common.ss" + (for-label scheme/base)) + +@title[#:tag "decompile"]{Decompiling Bytecode} + +The @DFlag{decompile} mode for @|mzc| takes a bytecode file (which + usually has the file extension @filepath{.zo}) and converts it back + to an approximation of Scheme code. Decompiled bytecode is mostly + useful for checking the compiler's transformation and optimization of + the source program. + +Many forms in the decompiled code, such as @scheme[module], + @scheme[define], and @scheme[lambda], have the same meanings as + always. Other forms and transformations are specific to the rendering + of bytecode, and they reflect a specific execution model: + +@itemize[ + + @item{Top-level variables, variables defined within the module, and + variables imported from other modules are prefixed with @litchar{_}, + which helps expose the difference between uses of local variables + versus other variables. Variables imported from other modules, + moreover, have a suffix that indicates the source module. + + Non-local variables are always accessed indirectly though an implicit + @schemeidfont{#%globals} or @schemeidfont{#%modvars} variable that + resides on the value stack (which otherwise contains local + variables). Variable accesses are further wrapped with + @schemeidfont{#%checked} when the compiler cannot prove that the + variable will be defined before the access. + + Uses of core primitives are shown without a leading @litchar{_}, and + they are never wrapped with @schemeidfont{#%checked}. Applications of + some primitives are inlined by the JIT compiler.} + + @item{Local-variable access may be wrapped with + @schemeidfont{#%sfs-clear}, which indicates that the variable-stack + location holding the variable will be cleared to prevent the + variable's value from being retained by the garbage collector. + + Mutable variables are converted to explicitly boxed values using + @schemeidfont{#%box}, @schemeidfont{#%unbox}, and + @schemeidfont{#%set-boxes!} (which works on multiple boxes at once). + A @schemeidfont{set!-rec-values} operation constructs + mutually-recursive closures and simultaneously updates the + corresponding variable-stack locations that bind the closures. A + @schemeidfont{set!}, @schemeidfont{set!-values}, or + @schemeidfont{set!-rec-values} form is always used on a local + variable before it is captured by a closure; that ordering reflects + how closures capture values in variable-stack locations, as opposed + to stack locations.} + + @item{In a @scheme[lambda] form, if the procedure produced by the + @scheme[lambda] has a name (accessible via @scheme[object-name]) + and/or source-location information, then it is shown as a quoted + constant at the start of the procedure's body. Afterward, if the + @scheme[lambda] form captures any bindings from its context, those + bindings are also shown in a quoted constant. Neither constant + corresponds to a computation when the closure is called, though the + list of captured bindings corresponds to a closure allocation when + the @scheme[lambda] form itself is evaluated. + + A @scheme[lambda] form that closes over no bindings is wrapped with + @schemeidfont{#%closed} plus an identifier that is bound to the + closure. The binding's scope covers the entire decompiled output, and + it may be referenced directly in other parts of the program; the + binding corresponds to a constant closure value that is shared, and + it may even contain cyclic references to itself or other constant + closures.} + + @item{A form @scheme[(#%apply-values _proc _expr)] is equivalent to + @scheme[(call-with-values (lambda () _expr) _proc)], but the run-time + system avoids allocating a closure for @scheme[_expr].} + + @item{A @schemeidfont{#%decode-syntax} form corresponds to a syntax + object. Future improvements to the decompiler will convert such + syntax objects to a readable form.} + +] diff --git a/collects/scribblings/mzc/mzc.scrbl b/collects/scribblings/mzc/mzc.scrbl index ca8c355de5..048534b7ad 100644 --- a/collects/scribblings/mzc/mzc.scrbl +++ b/collects/scribblings/mzc/mzc.scrbl @@ -47,6 +47,9 @@ command-line flags: @item{@as-index{@DFlag{expand}} : Pretty-prints the macro-expanded form of a Scheme program.} + @item{@as-index{@DFlag{decompile}} : Parses a bytecode file and + prints its content as quasi-Scheme. See @secref["decompile"].} + @item{@as-index{@DFlag{zo}}, @as-index{@Flag{z}}, or @as-index{@DFlag{collection-zo}} : Compiles Scheme code to bytecode, without following transitive imports. See @@ -64,6 +67,7 @@ command-line flags: @include-section["plt.scrbl"] @include-section["cc.scrbl"] @include-section["c-mods.scrbl"] +@include-section["decompile.scrbl"] @include-section["zo.scrbl"] @include-section["ext.scrbl"] @include-section["api.scrbl"] diff --git a/collects/scribblings/reference/pairs.scrbl b/collects/scribblings/reference/pairs.scrbl index 455fa17e5a..1fbc8192f0 100644 --- a/collects/scribblings/reference/pairs.scrbl +++ b/collects/scribblings/reference/pairs.scrbl @@ -46,8 +46,6 @@ @guideintro["pairs"]{pairs and lists} -@local-table-of-contents[] - A @deftech{pair} combines exactly two values. The first value is accessed with the @scheme[car] procedure, and the second value is accessed with the @scheme[cdr] procedure. Pairs are not mutable (but