#lang typed/racket/base (require "expression-structs.rkt" "lexical-structs.rkt" "il-structs.rkt" "kernel-primitives.rkt" "optimize-il.rkt" racket/bool racket/list) (provide (rename-out [-compile compile]) compile-general-procedure-call append-instruction-sequences adjust-target-depth) (: -compile (Expression Target Linkage -> (Listof Statement))) ;; Generates the instruction-sequence stream. ;; Note: the toplevel generates the lambda body streams at the head, and then the ;; rest of the instruction stream. (define (-compile exp target linkage) (let* ([after-lam-bodies (make-label 'afterLamBodies)] [before-pop-prompt-multiple (make-label 'beforePopPromptMultiple)] [before-pop-prompt (make-LinkedLabel (make-label 'beforePopPrompt) before-pop-prompt-multiple)]) (optimize-il (statements (append-instruction-sequences ;; Layout the lambda bodies... (make-instruction-sequence `(,(make-GotoStatement (make-Label after-lam-bodies)))) (compile-lambda-bodies (collect-all-lams exp)) after-lam-bodies ;; Begin a prompted evaluation: (make-instruction-sequence `(,(make-PushControlFrame/Prompt default-continuation-prompt-tag before-pop-prompt))) (compile exp '() target return-linkage/nontail) before-pop-prompt-multiple before-pop-prompt))))) (define-struct: lam+cenv ([lam : Lam] [cenv : CompileTimeEnvironment])) (: collect-all-lams (Expression -> (Listof lam+cenv))) ;; Finds all the lambdas in the expression. (define (collect-all-lams exp) (let: loop : (Listof lam+cenv) ([exp : Expression exp] [cenv : CompileTimeEnvironment '()]) (cond [(Top? exp) (loop (Top-code exp) (cons (Top-prefix exp) cenv))] [(Constant? exp) '()] [(LocalRef? exp) '()] [(ToplevelRef? exp) '()] [(ToplevelSet? exp) (loop (ToplevelSet-value exp) cenv)] [(Branch? exp) (append (loop (Branch-predicate exp) cenv) (loop (Branch-consequent exp) cenv) (loop (Branch-alternative exp) cenv))] [(Lam? exp) (cons (make-lam+cenv exp cenv) (loop (Lam-body exp) (extract-lambda-cenv exp cenv)))] [(Seq? exp) (apply append (map (lambda: ([e : Expression]) (loop e cenv)) (Seq-actions exp)))] [(Splice? exp) (apply append (map (lambda: ([e : Expression]) (loop e cenv)) (Splice-actions exp)))] [(App? exp) (let ([new-cenv (append (build-list (length (App-operands exp)) (lambda: ([i : Natural]) '?)) cenv)]) (append (loop (App-operator exp) new-cenv) (apply append (map (lambda: ([e : Expression]) (loop e new-cenv)) (App-operands exp)))))] [(Let1? exp) (append (loop (Let1-rhs exp) (cons '? cenv)) (loop (Let1-body exp) (cons (extract-static-knowledge (Let1-rhs exp) (cons '? cenv)) cenv)))] [(LetVoid? exp) (loop (LetVoid-body exp) (append (build-list (LetVoid-count exp) (lambda: ([i : Natural]) '?)) cenv))] [(InstallValue? exp) (loop (InstallValue-body exp) cenv)] [(BoxEnv? exp) (loop (BoxEnv-body exp) cenv)] [(LetRec? exp) (let ([new-cenv (append (map (lambda: ([p : Lam]) (extract-static-knowledge p (append (build-list (length (LetRec-procs exp)) (lambda: ([i : Natural]) '?)) cenv))) (reverse (LetRec-procs exp))) cenv)]) (append (apply append (map (lambda: ([lam : Lam]) (loop lam new-cenv)) (LetRec-procs exp))) (loop (LetRec-body exp) new-cenv)))] [(WithContMark? exp) (append (loop (WithContMark-key exp) cenv) (loop (WithContMark-value exp) cenv) (loop (WithContMark-body exp) cenv))]))) (: extract-lambda-cenv (Lam CompileTimeEnvironment -> CompileTimeEnvironment)) (define (extract-lambda-cenv lam cenv) (append (map (lambda: ([d : Natural]) (list-ref cenv d)) (Lam-closure-map lam)) (build-list (if (Lam-rest? lam) (add1 (Lam-num-parameters lam)) (Lam-num-parameters lam)) (lambda: ([i : Natural]) '?)))) (: compile (Expression CompileTimeEnvironment Target Linkage -> InstructionSequence)) ;; Compiles an expression into an instruction sequence. (define (compile exp cenv target linkage) (cond [(Top? exp) (compile-top exp cenv target linkage)] [(Constant? exp) (compile-constant exp cenv target linkage)] [(LocalRef? exp) (compile-local-reference exp cenv target linkage)] [(ToplevelRef? exp) (compile-toplevel-reference exp cenv target linkage)] [(ToplevelSet? exp) (compile-toplevel-set exp cenv target linkage)] [(Branch? exp) (compile-branch exp cenv target linkage)] [(Lam? exp) (compile-lambda exp cenv target linkage)] [(Seq? exp) (compile-sequence (Seq-actions exp) cenv target linkage)] [(Splice? exp) (compile-splice (Splice-actions exp) cenv target linkage)] [(App? exp) (compile-application exp cenv target linkage)] [(Let1? exp) (compile-let1 exp cenv target linkage)] [(LetVoid? exp) (compile-let-void exp cenv target linkage)] [(InstallValue? exp) (compile-install-value exp cenv target linkage)] [(BoxEnv? exp) (compile-box-environment-value exp cenv target linkage)] [(LetRec? exp) (compile-let-rec exp cenv target linkage)] [(WithContMark? exp) (compile-with-cont-mark exp cenv target linkage)])) (: compile-top (Top CompileTimeEnvironment Target Linkage -> InstructionSequence)) (define (compile-top top cenv target linkage) (let*: ([names : (Listof (U Symbol ModuleVariable False)) (Prefix-names (Top-prefix top))]) (end-with-linkage linkage cenv (append-instruction-sequences (make-instruction-sequence `(,(make-PerformStatement (make-ExtendEnvironment/Prefix! names)))) (compile (Top-code top) (cons (Top-prefix top) cenv) target next-linkage) (make-instruction-sequence `(,(make-PopEnvironment (make-Const 1) (make-Const 0)))))))) ;; Add linkage for expressions. (: end-with-linkage (Linkage CompileTimeEnvironment InstructionSequence -> InstructionSequence)) (define (end-with-linkage linkage cenv instruction-sequence) (append-instruction-sequences instruction-sequence (compile-linkage cenv linkage))) (: compile-linkage (CompileTimeEnvironment Linkage -> InstructionSequence)) (define (compile-linkage cenv linkage) (cond [(ReturnLinkage? linkage) (make-instruction-sequence `(,(make-AssignImmediateStatement 'proc (make-ControlStackLabel)) ,(make-PopEnvironment (make-Const (length cenv)) (make-Const 0)) ,(make-PopControlFrame) ,(make-GotoStatement (make-Reg 'proc))))] [(ReturnLinkage/NonTail? linkage) (make-instruction-sequence `(,(make-AssignImmediateStatement 'proc (make-ControlStackLabel)) ,(make-PopControlFrame) ,(make-GotoStatement (make-Reg 'proc))))] [(NextLinkage? linkage) empty-instruction-sequence] [(NextLinkage/Expects? linkage) empty-instruction-sequence] [(LabelLinkage? linkage) (make-instruction-sequence `(,(make-GotoStatement (make-Label (LabelLinkage-label linkage)))))] [(LabelLinkage/Expects? linkage) (make-instruction-sequence `(,(make-GotoStatement (make-Label (LabelLinkage/Expects-label linkage)))))])) (: compile-singular-context-check (Linkage -> InstructionSequence)) ;; Emits code to raise a runtime error if the linkage expects a singular ;; value, but the code being generated is not producing it. (define (compile-singular-context-check linkage) (cond [(NextLinkage? linkage) empty-instruction-sequence] [(LabelLinkage? linkage) empty-instruction-sequence] [(ReturnLinkage? linkage) empty-instruction-sequence] [(ReturnLinkage/NonTail? linkage) empty-instruction-sequence] [(NextLinkage/Expects? linkage) (if (= (NextLinkage/Expects-expects linkage) 1) empty-instruction-sequence (make-instruction-sequence `(,(make-PerformStatement (make-RaiseContextExpectedValuesError! 1)))))] [(LabelLinkage/Expects? linkage) (if (= (LabelLinkage/Expects-expects linkage) 1) empty-instruction-sequence (make-instruction-sequence `(,(make-PerformStatement (make-RaiseContextExpectedValuesError! 1)))))])) (: compile-constant (Constant CompileTimeEnvironment Target Linkage -> InstructionSequence)) (define (compile-constant exp cenv target linkage) (let ([singular-context-check (compile-singular-context-check linkage)]) ;; Compiles constant values. (end-with-linkage linkage cenv (append-instruction-sequences (make-instruction-sequence `(,(make-AssignImmediateStatement target (make-Const (Constant-v exp))))) singular-context-check)))) (: compile-local-reference (LocalRef CompileTimeEnvironment Target Linkage -> InstructionSequence)) (define (compile-local-reference exp cenv target linkage) (let ([singular-context-check (compile-singular-context-check linkage)]) (end-with-linkage linkage cenv (append-instruction-sequences (make-instruction-sequence `(,(make-AssignImmediateStatement target (make-EnvLexicalReference (LocalRef-depth exp) (LocalRef-unbox? exp))))) singular-context-check)))) (: compile-toplevel-reference (ToplevelRef CompileTimeEnvironment Target Linkage -> InstructionSequence)) ;; Compiles toplevel references. (define (compile-toplevel-reference exp cenv target linkage) (let ([singular-context-check (compile-singular-context-check linkage)]) (end-with-linkage linkage cenv (append-instruction-sequences (make-instruction-sequence `(,(make-PerformStatement (make-CheckToplevelBound! (ToplevelRef-depth exp) (ToplevelRef-pos exp))) ,(make-AssignImmediateStatement target (make-EnvPrefixReference (ToplevelRef-depth exp) (ToplevelRef-pos exp))))) singular-context-check)))) (: compile-toplevel-set (ToplevelSet CompileTimeEnvironment Target Linkage -> InstructionSequence)) ;; Compiles toplevel definition. (define (compile-toplevel-set exp cenv target linkage) (let* ([var (ToplevelSet-name exp)] [lexical-pos (make-EnvPrefixReference (ToplevelSet-depth exp) (ToplevelSet-pos exp))]) (let ([get-value-code (compile (ToplevelSet-value exp) cenv lexical-pos next-linkage-expects-single)] [singular-context-check (compile-singular-context-check linkage)]) (end-with-linkage linkage cenv (append-instruction-sequences get-value-code (make-instruction-sequence `(,(make-AssignImmediateStatement target (make-Const (void))))) singular-context-check))))) (: compile-branch (Branch CompileTimeEnvironment Target Linkage -> InstructionSequence)) ;; Compiles a conditional branch. (define (compile-branch exp cenv target linkage) (let: ([t-branch : Symbol (make-label 'trueBranch)] [f-branch : Symbol (make-label 'falseBranch)] [after-if : Symbol (make-label 'afterIf)]) (let ([consequent-linkage (cond [(NextLinkage? linkage) (make-LabelLinkage after-if)] [(NextLinkage/Expects? linkage) (make-LabelLinkage/Expects after-if (NextLinkage/Expects-expects linkage))] [(ReturnLinkage? linkage) linkage] [(ReturnLinkage/NonTail? linkage) linkage] [(LabelLinkage? linkage) linkage] [(LabelLinkage/Expects? linkage) linkage])]) (let ([p-code (compile (Branch-predicate exp) cenv 'val next-linkage-expects-single)] [c-code (compile (Branch-consequent exp) cenv target consequent-linkage)] [a-code (compile (Branch-alternative exp) cenv target linkage)]) (append-instruction-sequences p-code (append-instruction-sequences (make-instruction-sequence `(,(make-TestAndBranchStatement 'false? 'val f-branch))) t-branch c-code f-branch a-code after-if)))))) (: compile-sequence ((Listof Expression) CompileTimeEnvironment Target Linkage -> InstructionSequence)) ;; Compiles a sequence of expressions. The last expression will be compiled in the provided linkage. (define (compile-sequence seq cenv target linkage) ;; All but the last will use next-linkage linkage. (if (last-exp? seq) (compile (first-exp seq) cenv target linkage) (append-instruction-sequences (compile (first-exp seq) cenv target next-linkage) (compile-sequence (rest-exps seq) cenv target linkage)))) (: compile-splice ((Listof Expression) CompileTimeEnvironment Target Linkage -> InstructionSequence)) ;; Compiles a sequence of expressions. A continuation prompt wraps around each of the expressions ;; to delimit any continuation captures. (define (compile-splice seq cenv target linkage) (cond [(last-exp? seq) (let* ([before-pop-prompt-multiple (make-label 'beforePromptPopMultiple)] [before-pop-prompt (make-LinkedLabel (make-label 'beforePromptPop) before-pop-prompt-multiple)]) (end-with-linkage linkage cenv (append-instruction-sequences (make-instruction-sequence `(,(make-PushControlFrame/Prompt default-continuation-prompt-tag before-pop-prompt))) (compile (first-exp seq) cenv target return-linkage/nontail) before-pop-prompt-multiple (make-instruction-sequence `(,(make-PopEnvironment (make-Reg 'argcount) (make-Const 0)))) before-pop-prompt)))] [else (let* ([before-pop-prompt-multiple (make-label 'beforePromptPopMultiple)] [before-pop-prompt (make-LinkedLabel (make-label 'beforePromptPop) before-pop-prompt-multiple)]) (append-instruction-sequences (make-instruction-sequence `(,(make-PushControlFrame/Prompt (make-DefaultContinuationPromptTag) before-pop-prompt))) (compile (first-exp seq) cenv target return-linkage/nontail) before-pop-prompt-multiple (make-instruction-sequence `(,(make-PopEnvironment (make-Reg 'argcount) (make-Const 0)))) before-pop-prompt (compile-splice (rest-exps seq) cenv target linkage)))])) (: compile-lambda (Lam CompileTimeEnvironment Target Linkage -> InstructionSequence)) ;; Write out code for lambda expressions. ;; The lambda will close over the free variables. ;; Assumption: all of the lambda bodies have already been written out at the top, in -compile. (define (compile-lambda exp cenv target linkage) (let ([singular-context-check (compile-singular-context-check linkage)]) (end-with-linkage linkage cenv (append-instruction-sequences (make-instruction-sequence `(,(make-AssignPrimOpStatement target (make-MakeCompiledProcedure (Lam-entry-label exp) (if (Lam-rest? exp) (make-ArityAtLeast (Lam-num-parameters exp)) (Lam-num-parameters exp)) (Lam-closure-map exp) (Lam-name exp))))) singular-context-check)))) (: compile-lambda-shell (Lam CompileTimeEnvironment Target Linkage -> InstructionSequence)) ;; Write out code for lambda expressions, minus the closure map. ;; Assumption: all of the lambda bodies have already been written out at the top, in -compile. (define (compile-lambda-shell exp cenv target linkage) (let ([singular-context-check (compile-singular-context-check linkage)]) (end-with-linkage linkage cenv (append-instruction-sequences (make-instruction-sequence `(,(make-AssignPrimOpStatement target (make-MakeCompiledProcedureShell (Lam-entry-label exp) (if (Lam-rest? exp) (make-ArityAtLeast (Lam-num-parameters exp)) (Lam-num-parameters exp)) (Lam-name exp))))) singular-context-check)))) (: compile-lambda-body (Lam CompileTimeEnvironment -> InstructionSequence)) ;; Compiles the body of the lambda in the appropriate environment. ;; Closures will target their value to the 'val register, and use return linkage. (define (compile-lambda-body exp cenv) (let: ([maybe-unsplice-rest-argument : InstructionSequence (if (Lam-rest? exp) (make-instruction-sequence `(,(make-PerformStatement (make-UnspliceRestFromStack! (make-Const (Lam-num-parameters exp)) (make-SubtractArg (make-Reg 'argcount) (make-Const (Lam-num-parameters exp))))))) empty-instruction-sequence)] [maybe-install-closure-values : InstructionSequence (if (not (empty? (Lam-closure-map exp))) (make-instruction-sequence `(,(make-PerformStatement (make-InstallClosureValues!)))) empty-instruction-sequence)] [lam-body-code : InstructionSequence (compile (Lam-body exp) (extract-lambda-cenv exp cenv) 'val return-linkage)]) (append-instruction-sequences (make-instruction-sequence `(,(Lam-entry-label exp))) maybe-unsplice-rest-argument maybe-install-closure-values lam-body-code))) (: compile-lambda-bodies ((Listof lam+cenv) -> InstructionSequence)) ;; Compile several lambda bodies, back to back. (define (compile-lambda-bodies exps) (cond [(empty? exps) (make-instruction-sequence '())] [else (append-instruction-sequences (compile-lambda-body (lam+cenv-lam (first exps)) (lam+cenv-cenv (first exps))) (compile-lambda-bodies (rest exps)))])) (: extend-compile-time-environment/scratch-space (CompileTimeEnvironment Natural -> CompileTimeEnvironment)) (define (extend-compile-time-environment/scratch-space cenv n) (append (build-list n (lambda: ([i : Natural]) '?)) cenv)) (: compile-application (App CompileTimeEnvironment Target Linkage -> InstructionSequence)) ;; Compiles procedure application ;; Special cases: if we know something about the operator, the compiler will special case. ;; This includes: ;; Known closure ;; Known kernel primitive ;; In the general case, we do general procedure application. (define (compile-application exp cenv target linkage) (let ([extended-cenv (extend-compile-time-environment/scratch-space cenv (length (App-operands exp)))]) (define (default) (compile-general-application exp cenv target linkage)) (let: ([op-knowledge : CompileTimeEnvironmentEntry (extract-static-knowledge (App-operator exp) extended-cenv)]) (cond [(eq? op-knowledge '?) (default)] [(ModuleVariable? op-knowledge) (cond [(symbol=? (ModuleVariable-module-path op-knowledge) '#%kernel) (let ([op (ModuleVariable-name op-knowledge)]) (cond [(KernelPrimitiveName? op) (compile-kernel-primitive-application op exp cenv target linkage)] [else (default)]))] [else (default)])] [(StaticallyKnownLam? op-knowledge) (compile-statically-known-lam-application op-knowledge exp cenv target linkage)] [(Prefix? op-knowledge) (error 'impossible)] [(Const? op-knowledge) (make-instruction-sequence `(,(make-AssignImmediateStatement 'proc op-knowledge) ,(make-PerformStatement (make-RaiseOperatorApplicationError! (make-Reg 'proc)))))])))) (: compile-general-application (App CompileTimeEnvironment Target Linkage -> InstructionSequence)) (define (compile-general-application exp cenv target linkage) (let* ([extended-cenv (extend-compile-time-environment/scratch-space cenv (length (App-operands exp)))] [proc-code (compile (App-operator exp) extended-cenv (if (empty? (App-operands exp)) 'proc (make-EnvLexicalReference (ensure-natural (sub1 (length (App-operands exp)))) #f)) next-linkage-expects-single)] [operand-codes (map (lambda: ([operand : Expression] [target : Target]) (compile operand extended-cenv target next-linkage-expects-single)) (App-operands exp) (build-list (length (App-operands exp)) (lambda: ([i : Natural]) (if (< i (sub1 (length (App-operands exp)))) (make-EnvLexicalReference i #f) 'val))))]) (append-instruction-sequences (if (not (empty? (App-operands exp))) (make-instruction-sequence `(,(make-PushEnvironment (length (App-operands exp)) #f))) empty-instruction-sequence) proc-code (juggle-operands operand-codes) (make-instruction-sequence `(,(make-AssignImmediateStatement 'argcount (make-Const (length (App-operands exp)))))) (compile-general-procedure-call cenv (cond [(= (length extended-cenv) (length (App-operands exp))) (make-Reg 'argcount)] [else (make-Const (length extended-cenv))]) target linkage)))) (: compile-kernel-primitive-application (KernelPrimitiveName App CompileTimeEnvironment Target Linkage -> InstructionSequence)) ;; This is a special case of application, where the operator is statically ;; known to be in the set of hardcoded primitives. ;; ;; There's a special case optimization we can perform: we can avoid touching ;; the stack for constant arguments; rather than allocate (length (App-operands exp)) ;; stack slots, we can do less than that. ;; ;; We have to be sensitive to mutation. (define (compile-kernel-primitive-application kernel-op exp cenv target linkage) (cond ;; If all the arguments are primitive enough (all constants, localrefs, or toplevelrefs), ;; then application requires no stack space at all, and application is especially simple. [(andmap (lambda (op) ;; TODO: as long as the operand contains no applications? (or (Constant? op) (ToplevelRef? op) (LocalRef? op))) (App-operands exp)) (let* ([n (length (App-operands exp))] [operand-knowledge (map (lambda: ([arg : Expression]) (extract-static-knowledge arg (extend-compile-time-environment/scratch-space cenv n))) (App-operands exp))] [typechecks? (map (lambda: ([dom : OperandDomain] [known : CompileTimeEnvironmentEntry]) (not (redundant-check? dom known))) (kernel-primitive-expected-operand-types kernel-op n) operand-knowledge)] [expected-operand-types (kernel-primitive-expected-operand-types kernel-op n)] [operand-poss (simple-operands->opargs (map (lambda: ([op : Expression]) (adjust-expression-depth op n n)) (App-operands exp)))]) (end-with-linkage linkage cenv (make-instruction-sequence `(,(make-AssignPrimOpStatement target (make-CallKernelPrimitiveProcedure kernel-op operand-poss expected-operand-types typechecks?))))))] [else ;; Otherwise, we can split the operands into two categories: constants, and the rest. (let*-values ([(n) (length (App-operands exp))] [(expected-operand-types) (kernel-primitive-expected-operand-types kernel-op n)] [(constant-operands rest-operands) (split-operands-by-constants (App-operands exp))] ;; here, we rewrite the stack references so they assume no scratch space ;; used by the constant operands. [(extended-cenv constant-operands rest-operands) (values (extend-compile-time-environment/scratch-space cenv (length rest-operands)) (map (lambda: ([constant-operand : Expression]) (ensure-simple-expression (adjust-expression-depth constant-operand (length constant-operands) n))) constant-operands) (map (lambda: ([rest-operand : Expression]) (adjust-expression-depth rest-operand (length constant-operands) n)) rest-operands))] [(operand-knowledge) (append (map (lambda: ([arg : Expression]) (extract-static-knowledge arg extended-cenv)) constant-operands) (map (lambda: ([arg : Expression]) (extract-static-knowledge arg extended-cenv)) rest-operands))] [(typechecks?) (map (lambda: ([dom : OperandDomain] [known : CompileTimeEnvironmentEntry]) (not (redundant-check? dom known))) (kernel-primitive-expected-operand-types kernel-op n) operand-knowledge)] [(stack-pushing-code) (if (empty? rest-operands) empty-instruction-sequence (make-instruction-sequence `(,(make-PushEnvironment (length rest-operands) #f))))] [(stack-popping-code) (if (empty? rest-operands) empty-instruction-sequence (make-instruction-sequence `(,(make-PopEnvironment (make-Const (length rest-operands)) (make-Const 0)))))] [(constant-operand-poss) (simple-operands->opargs constant-operands)] [(rest-operand-poss) (build-list (length rest-operands) (lambda: ([i : Natural]) (make-EnvLexicalReference i #f)))] [(rest-operand-code) (apply append-instruction-sequences (map (lambda: ([operand : Expression] [target : Target]) (compile operand extended-cenv target next-linkage-expects-single)) rest-operands rest-operand-poss))]) (end-with-linkage linkage cenv (append-instruction-sequences stack-pushing-code rest-operand-code (make-instruction-sequence `(,(make-AssignPrimOpStatement (adjust-target-depth target (length rest-operands)) (make-CallKernelPrimitiveProcedure kernel-op (append constant-operand-poss rest-operand-poss) expected-operand-types typechecks?)))) stack-popping-code)))])) (: ensure-simple-expression (Expression -> (U Constant ToplevelRef LocalRef))) (define (ensure-simple-expression e) (if (or (Constant? e) (LocalRef? e) (ToplevelRef? e)) e (error 'ensure-simple-expression))) (: simple-operands->opargs ((Listof Expression) -> (Listof OpArg))) ;; Produces a list of OpArgs if all the operands are particularly simple, and false therwise. (define (simple-operands->opargs rands) (map (lambda: ([e : Expression]) (cond [(Constant? e) (make-Const (Constant-v e))] [(LocalRef? e) (make-EnvLexicalReference (LocalRef-depth e) (LocalRef-unbox? e))] [(ToplevelRef? e) (make-EnvPrefixReference (ToplevelRef-depth e) (ToplevelRef-pos e))] [else (error 'all-operands-are-constant "Impossible")])) rands)) (: redundant-check? (OperandDomain CompileTimeEnvironmentEntry -> Boolean)) ;; Produces true if we know the knowledge implies the domain-type. (define (redundant-check? domain-type knowledge) (cond [(eq? domain-type 'any) #t] [else (cond [(Const? knowledge) (case domain-type [(number) (number? (Const-const knowledge))] [(string) (string? (Const-const knowledge))] [(box) (box? (Const-const knowledge))] [(list) (list? (Const-const knowledge))] [(pair) (pair? (Const-const knowledge))])] [else #f])])) (: split-operands-by-constants ((Listof Expression) -> (values (Listof (U Constant LocalRef ToplevelRef)) (Listof Expression)))) ;; Splits off the list of operations into two: a prefix of constant ;; or simple expressions, and the remainder. ;; TODO: if we can statically determine what arguments are immutable, regardless of ;; side effects, we can do a much better job here... (define (split-operands-by-constants rands) (let: loop : (values (Listof (U Constant LocalRef ToplevelRef)) (Listof Expression)) ([rands : (Listof Expression) rands] [constants : (Listof (U Constant LocalRef ToplevelRef)) empty]) (cond [(empty? rands) (values (reverse constants) empty)] [else (let ([e (first rands)]) (if (or (Constant? e) ;; These two are commented out because it's not sound otherwise. #;(and (LocalRef? e) (not (LocalRef-unbox? e))) #;(and (ToplevelRef? e) (let ([prefix (ensure-prefix (list-ref cenv (ToplevelRef-depth e)))]) (ModuleVariable? (list-ref prefix (ToplevelRef-pos e)))))) (loop (rest rands) (cons e constants)) (values (reverse constants) rands)))]))) (define-predicate natural? Natural) (define-predicate atomic-arity-list? (Listof (U Natural ArityAtLeast))) (: arity-matches? (Arity Natural -> Boolean)) (define (arity-matches? an-arity n) (cond [(natural? an-arity) (= an-arity n)] [(ArityAtLeast? an-arity) (>= n (ArityAtLeast-value an-arity))] [(atomic-arity-list? an-arity) (ormap (lambda: ([an-arity : (U Natural ArityAtLeast)]) (cond [(natural? an-arity) (= an-arity n)] [(ArityAtLeast? an-arity) (>= n (ArityAtLeast-value an-arity))])) an-arity)])) (: compile-statically-known-lam-application (StaticallyKnownLam App CompileTimeEnvironment Target Linkage -> InstructionSequence)) (define (compile-statically-known-lam-application static-knowledge exp cenv target linkage) (let ([arity-check (cond [(arity-matches? (StaticallyKnownLam-arity static-knowledge) (length (App-operands exp))) empty-instruction-sequence] [else (make-instruction-sequence `(,(make-PerformStatement (make-RaiseArityMismatchError! (StaticallyKnownLam-arity static-knowledge) (make-Const (length (App-operands exp)))))))])]) (let* ([extended-cenv (extend-compile-time-environment/scratch-space cenv (length (App-operands exp)))] [proc-code (compile (App-operator exp) extended-cenv (if (empty? (App-operands exp)) 'proc (make-EnvLexicalReference (ensure-natural (sub1 (length (App-operands exp)))) #f)) next-linkage-expects-single)] [operand-codes (map (lambda: ([operand : Expression] [target : Target]) (compile operand extended-cenv target next-linkage-expects-single)) (App-operands exp) (build-list (length (App-operands exp)) (lambda: ([i : Natural]) (if (< i (sub1 (length (App-operands exp)))) (make-EnvLexicalReference i #f) 'val))))]) (append-instruction-sequences (if (not (empty? (App-operands exp))) (make-instruction-sequence `(,(make-PushEnvironment (length (App-operands exp)) #f))) empty-instruction-sequence) proc-code (juggle-operands operand-codes) arity-check (compile-procedure-call/statically-known-lam static-knowledge cenv extended-cenv (length (App-operands exp)) target linkage))))) (: juggle-operands ((Listof InstructionSequence) -> InstructionSequence)) ;; Installs the operators. At the end of this, ;; the procedure lives in 'proc, and the operands on the environment stack. (define (juggle-operands operand-codes) (let: loop : InstructionSequence ([ops : (Listof InstructionSequence) operand-codes]) (cond ;; If there are no operands, no need to juggle. [(null? ops) (make-instruction-sequence empty)] [(null? (rest ops)) (let: ([n : Natural (ensure-natural (sub1 (length operand-codes)))]) ;; The last operand needs to be handled specially: it currently lives in ;; val. We move the procedure at env[n] over to proc, and move the ;; last operand at 'val into env[n]. (append-instruction-sequences (car ops) (make-instruction-sequence `(,(make-AssignImmediateStatement 'proc (make-EnvLexicalReference n #f)) ,(make-AssignImmediateStatement (make-EnvLexicalReference n #f) (make-Reg 'val))))))] [else ;; Otherwise, add instructions to juggle the operator and operands in the stack. (append-instruction-sequences (car ops) (loop (rest ops)))]))) (: compile-general-procedure-call (CompileTimeEnvironment OpArg Target Linkage -> InstructionSequence)) ;; Assumes the following: ;; 1. the procedure value has been loaded into the proc register. ;; 2. the n values passed in has been written into argcount register. ;; 3. environment stack contains the n operand values. ;; ;; n is the number of arguments passed in. ;; cenv is the compile-time enviroment before arguments have been shifted in. ;; extended-cenv is the compile-time environment after arguments have been shifted in. (define (compile-general-procedure-call cenv extended-cenv-length target linkage) (let: ([primitive-branch : LabelLinkage (make-LabelLinkage (make-label 'primitiveBranch))] [compiled-branch : LabelLinkage (make-LabelLinkage (make-label 'compiledBranch))] [after-call : LabelLinkage (make-LabelLinkage (make-label 'afterCall))]) (let: ([compiled-linkage : Linkage (if (ReturnLinkage? linkage) linkage after-call)]) (append-instruction-sequences (make-instruction-sequence `(,(make-TestAndBranchStatement 'primitive-procedure? 'proc (LabelLinkage-label primitive-branch)))) ;; Compiled branch (LabelLinkage-label compiled-branch) (make-instruction-sequence `(,(make-PerformStatement (make-CheckClosureArity! (make-Reg 'argcount))))) (compile-compiled-procedure-application extended-cenv-length 'val target compiled-linkage) ;; Primitive branch (LabelLinkage-label primitive-branch) (end-with-linkage linkage cenv (append-instruction-sequences (make-instruction-sequence `(,(make-PerformStatement (make-CheckPrimitiveArity! (make-Reg 'argcount))) ,(make-AssignPrimOpStatement 'val (make-ApplyPrimitiveProcedure)) ,(make-PopEnvironment (make-Reg 'argcount) (make-Const 0)) ,(make-AssignImmediateStatement target (make-Reg 'val)))) (LabelLinkage-label after-call))))))) (: compile-procedure-call/statically-known-lam (StaticallyKnownLam CompileTimeEnvironment CompileTimeEnvironment Natural Target Linkage -> InstructionSequence)) (define (compile-procedure-call/statically-known-lam static-knowledge cenv extended-cenv n target linkage) (let*: ([after-call : LabelLinkage (make-LabelLinkage (make-label 'afterCall))] [compiled-linkage : Linkage (if (ReturnLinkage? linkage) linkage after-call)]) (append-instruction-sequences (make-instruction-sequence `(,(make-AssignImmediateStatement 'argcount (make-Const n)))) (compile-compiled-procedure-application (cond [(= (length extended-cenv) n) (make-Reg 'argcount)] [else (make-Const (length extended-cenv))]) (make-Label (StaticallyKnownLam-entry-point static-knowledge)) target compiled-linkage) (end-with-linkage linkage cenv (LabelLinkage-label after-call))))) (: compile-compiled-procedure-application (OpArg (U Label 'val) Target Linkage -> InstructionSequence)) ;; Three fundamental cases for general compiled-procedure application. ;; 1. Tail calls. ;; 2. Non-tail calls (next/label linkage) that write to val ;; 3. Calls in argument position (next/label linkage) that write to the stack. (define (compile-compiled-procedure-application cenv-length-with-args entry-point target linkage) (let ([maybe-install-jump-address ;; Optimization: if the entry-point is supposed to be val, then it needs to hold ;; the procedure entry here. Otherwise, it doesn't. (cond [(Label? entry-point) empty-instruction-sequence] [(eq? entry-point 'val) (make-instruction-sequence `(,(make-AssignPrimOpStatement 'val (make-GetCompiledProcedureEntry))))])] [entry-point-target (cond [(Label? entry-point) entry-point] [(eq? entry-point 'val) (make-Reg 'val)])]) (cond [(ReturnLinkage? linkage) (cond [(eq? target 'val) ;; This case happens when we're in tail position. ;; We clean up the stack right before the jump, and do not add ;; to the control stack. (append-instruction-sequences maybe-install-jump-address (cond [(equal? cenv-length-with-args (make-Reg 'argcount)) empty-instruction-sequence] [else (make-instruction-sequence `(,(make-PopEnvironment (make-SubtractArg cenv-length-with-args (make-Reg 'argcount)) (make-Reg 'argcount))))]) (make-instruction-sequence `(;; Assign the proc value of the existing call frame ,(make-PerformStatement (make-SetFrameCallee! (make-Reg 'proc))) ,(make-GotoStatement entry-point-target))))] [else ;; This case should be impossible: return linkage should only ;; occur when we're in tail position, and we should be in tail position ;; only when the target is the val register. (error 'compile "return linkage, target not val: ~s" target)])] [(ReturnLinkage/NonTail? linkage) (cond [(eq? target 'val) ;; This case happens for a function call that isn't in ;; tail position. (let* ([proc-return-multiple (make-label 'procReturnMultiple)] [proc-return (make-LinkedLabel (make-label 'procReturn) proc-return-multiple)]) (append-instruction-sequences (make-instruction-sequence `(,(make-PushControlFrame/Call proc-return))) maybe-install-jump-address (make-instruction-sequence `(,(make-GotoStatement entry-point-target))) proc-return-multiple (make-instruction-sequence `(,(make-PopEnvironment (make-Reg 'argcount) (make-Const 0)))) proc-return))] [else ;; This case happens for evaluating arguments, since the ;; arguments are being installed into the scratch space. (let* ([proc-return-multiple (make-label 'procReturnMultiple)] [proc-return (make-LinkedLabel (make-label 'procReturn) proc-return-multiple)]) (append-instruction-sequences (make-instruction-sequence `(,(make-PushControlFrame/Call proc-return))) maybe-install-jump-address (make-instruction-sequence `(,(make-GotoStatement entry-point-target))) proc-return-multiple (make-instruction-sequence `(,(make-PopEnvironment (make-Reg 'argcount) (make-Const 0)))) proc-return (make-instruction-sequence `(,(make-AssignImmediateStatement target (make-Reg 'val))))))])] [(NextLinkage? linkage) (cond [(eq? target 'val) ;; This case happens for a function call that isn't in ;; tail position. (let* ([proc-return-multiple (make-label 'procReturnMultiple)] [proc-return (make-LinkedLabel (make-label 'procReturn) proc-return-multiple)]) (append-instruction-sequences (make-instruction-sequence `(,(make-PushControlFrame/Call proc-return))) maybe-install-jump-address (make-instruction-sequence `(,(make-GotoStatement entry-point-target))) proc-return-multiple (make-instruction-sequence `(,(make-PopEnvironment (make-Reg 'argcount) (make-Const 0)))) proc-return))] [else ;; This case happens for evaluating arguments, since the ;; arguments are being installed into the scratch space. (let* ([proc-return-multiple (make-label 'procReturnMultiple)] [proc-return (make-LinkedLabel (make-label 'procReturn) proc-return-multiple)]) (append-instruction-sequences (make-instruction-sequence `(,(make-PushControlFrame/Call proc-return))) maybe-install-jump-address (make-instruction-sequence `(,(make-GotoStatement entry-point-target))) proc-return-multiple ;; FIMXE: this needs to raise a runtime error! (make-instruction-sequence `(,(make-PopEnvironment (make-Reg 'argcount) (make-Const 0)))) proc-return (make-instruction-sequence `(,(make-AssignImmediateStatement target (make-Reg 'val))))))])] ;; FIXME: this isn't doing the proper checks!!! [(NextLinkage/Expects? linkage) (cond [(eq? target 'val) ;; This case happens for a function call that isn't in ;; tail position. (let* ([proc-return-multiple (make-label 'procReturnMultiple)] [proc-return (make-LinkedLabel (make-label 'procReturn) proc-return-multiple)]) (append-instruction-sequences (make-instruction-sequence `(,(make-PushControlFrame/Call proc-return))) maybe-install-jump-address (make-instruction-sequence `(,(make-GotoStatement entry-point-target))) proc-return-multiple (make-instruction-sequence `(,(make-PopEnvironment (make-Reg 'argcount) (make-Const 0)))) proc-return))] [else ;; This case happens for evaluating arguments, since the ;; arguments are being installed into the scratch space. (let* ([proc-return-multiple (make-label 'procReturnMultiple)] [proc-return (make-LinkedLabel (make-label 'procReturn) proc-return-multiple)]) (append-instruction-sequences (make-instruction-sequence `(,(make-PushControlFrame/Call proc-return))) maybe-install-jump-address (make-instruction-sequence `(,(make-GotoStatement entry-point-target))) proc-return-multiple ;; FIMXE: this needs to raise a runtime error! (make-instruction-sequence `(,(make-PopEnvironment (make-Reg 'argcount) (make-Const 0)))) proc-return (make-instruction-sequence `(,(make-AssignImmediateStatement target (make-Reg 'val))))))])] [(LabelLinkage? linkage) (cond [(eq? target 'val) ;; This case happens for a function call that isn't in ;; tail position. (let* ([proc-return-multiple (make-label 'procReturnMultiple)] [proc-return (make-LinkedLabel (make-label 'procReturn) proc-return-multiple)]) (append-instruction-sequences (make-instruction-sequence `(,(make-PushControlFrame/Call proc-return))) maybe-install-jump-address (make-instruction-sequence `(,(make-GotoStatement entry-point-target))) proc-return-multiple (make-instruction-sequence `(,(make-PopEnvironment (make-Reg 'argcount) (make-Const 0)))) proc-return (make-instruction-sequence `(,(make-GotoStatement (make-Label (LabelLinkage-label linkage)))))))] [else ;; This case happens for evaluating arguments, since the ;; arguments are being installed into the scratch space. (let* ([proc-return-multiple (make-label 'procReturnMultiple)] [proc-return (make-LinkedLabel (make-label 'procReturn) proc-return-multiple)]) (append-instruction-sequences (make-instruction-sequence `(,(make-PushControlFrame/Call proc-return))) maybe-install-jump-address (make-instruction-sequence `(,(make-GotoStatement entry-point-target))) proc-return-multiple (make-instruction-sequence `(,(make-PopEnvironment (make-Reg 'argcount) (make-Const 0)))) proc-return (make-instruction-sequence `(,(make-AssignImmediateStatement target (make-Reg 'val)) ,(make-GotoStatement (make-Label (LabelLinkage-label linkage)))))))])] ;; FIXME!!! this isn't doing the correct checks! [(LabelLinkage/Expects? linkage) (cond [(eq? target 'val) ;; This case happens for a function call that isn't in ;; tail position. (let* ([proc-return-multiple (make-label 'procReturnMultiple)] [proc-return (make-LinkedLabel (make-label 'procReturn) proc-return-multiple)]) (append-instruction-sequences (make-instruction-sequence `(,(make-PushControlFrame/Call proc-return))) maybe-install-jump-address (make-instruction-sequence `(,(make-GotoStatement entry-point-target))) proc-return-multiple ;; FIXME: this may need to raise a runtime error here! (make-instruction-sequence `(,(make-PopEnvironment (make-Reg 'argcount) (make-Const 0)))) proc-return (make-instruction-sequence `(,(make-GotoStatement (make-Label (LabelLinkage/Expects-label linkage)))))))] [else ;; This case happens for evaluating arguments, since the ;; arguments are being installed into the scratch space. (let* ([proc-return-multiple (make-label 'procReturnMultiple)] [proc-return (make-LinkedLabel (make-label 'procReturn) proc-return-multiple)]) (append-instruction-sequences (make-instruction-sequence `(,(make-PushControlFrame/Call proc-return))) maybe-install-jump-address (make-instruction-sequence `(,(make-GotoStatement entry-point-target))) proc-return-multiple ;; FIXME: this may need to raise a runtime error here! (make-instruction-sequence `(,(make-PopEnvironment (make-Reg 'argcount) (make-Const 0)))) proc-return (make-instruction-sequence `(,(make-AssignImmediateStatement target (make-Reg 'val)) ,(make-GotoStatement (make-Label (LabelLinkage/Expects-label linkage)))))))])]))) (: extract-static-knowledge (Expression CompileTimeEnvironment -> CompileTimeEnvironmentEntry)) ;; Statically determines what we know about exp, given the compile time environment. (define (extract-static-knowledge exp cenv) (cond [(Lam? exp) (make-StaticallyKnownLam (Lam-name exp) (Lam-entry-label exp) (if (Lam-rest? exp) (make-ArityAtLeast (Lam-num-parameters exp)) (Lam-num-parameters exp)))] [(and (LocalRef? exp) (not (LocalRef-unbox? exp))) (let ([entry (list-ref cenv (LocalRef-depth exp))]) entry)] [(ToplevelRef? exp) (let: ([name : (U Symbol False ModuleVariable) (list-ref (Prefix-names (ensure-prefix (list-ref cenv (ToplevelRef-depth exp)))) (ToplevelRef-pos exp))]) (cond [(ModuleVariable? name) name] [else '?]))] [(Constant? exp) (make-Const (Constant-v exp))] [else '?])) (: compile-let1 (Let1 CompileTimeEnvironment Target Linkage -> InstructionSequence)) (define (compile-let1 exp cenv target linkage) (let*: ([rhs-code : InstructionSequence (compile (Let1-rhs exp) (cons '? cenv) (make-EnvLexicalReference 0 #f) next-linkage-expects-single)] [after-let1 : Symbol (make-label 'afterLetOne)] [after-body-code : Symbol (make-label 'afterLetBody)] [extended-cenv : CompileTimeEnvironment (cons (extract-static-knowledge (Let1-rhs exp) (cons '? cenv)) cenv)] [let-linkage : Linkage (cond [(NextLinkage? linkage) linkage] [(NextLinkage/Expects? linkage) linkage] [(ReturnLinkage? linkage) linkage] [(ReturnLinkage/NonTail? linkage) (make-LabelLinkage after-body-code)] [(LabelLinkage? linkage) (make-LabelLinkage after-body-code)] [(LabelLinkage/Expects? linkage) (make-LabelLinkage/Expects after-body-code (LabelLinkage/Expects-expects linkage))])] [body-target : Target (adjust-target-depth target 1)] [body-code : InstructionSequence (compile (Let1-body exp) extended-cenv body-target let-linkage)]) (end-with-linkage linkage extended-cenv (append-instruction-sequences (make-instruction-sequence `(,(make-PushEnvironment 1 #f))) rhs-code body-code after-body-code (make-instruction-sequence `(,(make-PopEnvironment (make-Const 1) (make-Const 0)))) after-let1)))) (: compile-let-void (LetVoid CompileTimeEnvironment Target Linkage -> InstructionSequence)) (define (compile-let-void exp cenv target linkage) (let*: ([n : Natural (LetVoid-count exp)] [after-let : Symbol (make-label 'afterLet)] [after-body-code : Symbol (make-label 'afterLetBody)] [extended-cenv : CompileTimeEnvironment (append (build-list (LetVoid-count exp) (lambda: ([i : Natural]) '?)) cenv)] [let-linkage : Linkage (cond [(NextLinkage? linkage) linkage] [(NextLinkage/Expects? linkage) linkage] [(ReturnLinkage? linkage) linkage] [(ReturnLinkage/NonTail? linkage) (make-LabelLinkage after-body-code)] [(LabelLinkage? linkage) (make-LabelLinkage after-body-code)] [(LabelLinkage/Expects? linkage) (make-LabelLinkage/Expects after-body-code (LabelLinkage/Expects-expects linkage))])] [body-target : Target (adjust-target-depth target n)] [body-code : InstructionSequence (compile (LetVoid-body exp) extended-cenv body-target let-linkage)]) (end-with-linkage linkage extended-cenv (append-instruction-sequences (if (> n 0) (make-instruction-sequence `(,(make-PushEnvironment n (LetVoid-boxes? exp)))) empty-instruction-sequence) body-code after-body-code (if (> n 0) (make-instruction-sequence `(,(make-PopEnvironment (make-Const n) (make-Const 0)))) empty-instruction-sequence) after-let)))) (: compile-let-rec (LetRec CompileTimeEnvironment Target Linkage -> InstructionSequence)) (define (compile-let-rec exp cenv target linkage) (let*: ([extended-cenv : CompileTimeEnvironment (append (map (lambda: ([p : Lam]) (extract-static-knowledge p (append (build-list (length (LetRec-procs exp)) (lambda: ([i : Natural]) '?)) cenv))) (reverse (LetRec-procs exp))) cenv)] [n : Natural (length (LetRec-procs exp))] [after-body-code : Symbol (make-label 'afterBodyCode)] [letrec-linkage : Linkage (cond [(NextLinkage? linkage) linkage] [(NextLinkage/Expects? linkage) linkage] [(ReturnLinkage? linkage) linkage] [(ReturnLinkage/NonTail? linkage) (make-LabelLinkage after-body-code)] [(LabelLinkage? linkage) (make-LabelLinkage after-body-code)] [(LabelLinkage/Expects? linkage) (make-LabelLinkage/Expects after-body-code (LabelLinkage/Expects-expects linkage))])]) (end-with-linkage linkage extended-cenv (append-instruction-sequences (if (> n 0) (make-instruction-sequence `(,(make-PushEnvironment n #f))) empty-instruction-sequence) ;; Install each of the closure shells (apply append-instruction-sequences (map (lambda: ([lam : Lam] [i : Natural]) (compile-lambda-shell lam extended-cenv (make-EnvLexicalReference i #f) next-linkage-expects-single)) (LetRec-procs exp) (build-list n (lambda: ([i : Natural]) (ensure-natural (- n 1 i)))))) ;; Fix the closure maps of each (apply append-instruction-sequences (map (lambda: ([lam : Lam] [i : Natural]) (make-instruction-sequence `(,(make-PerformStatement (make-FixClosureShellMap! i (Lam-closure-map lam)))))) (LetRec-procs exp) (build-list n (lambda: ([i : Natural]) (ensure-natural (- n 1 i)))))) ;; Compile the body (compile (LetRec-body exp) extended-cenv (adjust-target-depth target n) letrec-linkage) after-body-code (if (> n 0) (make-instruction-sequence `(,(make-PopEnvironment (make-Const n) (make-Const 0)))) empty-instruction-sequence))))) (: compile-install-value (InstallValue CompileTimeEnvironment Target Linkage -> InstructionSequence)) (define (compile-install-value exp cenv target linkage) (compile (InstallValue-body exp) cenv (make-EnvLexicalReference (InstallValue-depth exp) (InstallValue-box? exp)) linkage)) (: compile-box-environment-value (BoxEnv CompileTimeEnvironment Target Linkage -> InstructionSequence)) (define (compile-box-environment-value exp cenv target linkage) (append-instruction-sequences (make-instruction-sequence `(,(make-AssignPrimOpStatement (make-EnvLexicalReference (BoxEnv-depth exp) #f) (make-MakeBoxedEnvironmentValue (BoxEnv-depth exp))))) (compile (BoxEnv-body exp) cenv target linkage))) (: compile-with-cont-mark (WithContMark CompileTimeEnvironment Target Linkage -> InstructionSequence)) (define (compile-with-cont-mark exp cenv target linkage) (cond [(or (ReturnLinkage/NonTail? linkage) (ReturnLinkage? linkage)) (append-instruction-sequences (compile (WithContMark-key exp) cenv 'val next-linkage-expects-single) (make-instruction-sequence `(,(make-AssignImmediateStatement (make-ControlFrameTemporary 'pendingContinuationMarkKey) (make-Reg 'val)))) (compile (WithContMark-value exp) cenv 'val next-linkage-expects-single) (make-instruction-sequence `(,(make-PerformStatement (make-InstallContinuationMarkEntry!)))) (compile (WithContMark-body exp) cenv target linkage))] [(or (NextLinkage? linkage) (LabelLinkage? linkage) (NextLinkage/Expects? linkage) (LabelLinkage/Expects? linkage)) (let ([body-next-linkage (cond [(NextLinkage? linkage) next-linkage] [(LabelLinkage? linkage) next-linkage] [(NextLinkage/Expects? linkage) linkage] [(LabelLinkage/Expects? linkage) (make-NextLinkage/Expects (LabelLinkage/Expects-expects linkage))])]) (end-with-linkage linkage cenv (append-instruction-sequences ;; Making a continuation frame; isn't really used for anything ;; but recording the key/value data. (make-instruction-sequence `(,(make-PushControlFrame/Generic))) (compile (WithContMark-key exp) cenv 'val next-linkage-expects-single) (make-instruction-sequence `(,(make-AssignImmediateStatement (make-ControlFrameTemporary 'pendingContinuationMarkKey) (make-Reg 'val)))) (compile (WithContMark-value exp) cenv 'val next-linkage-expects-single) (make-instruction-sequence `(,(make-PerformStatement (make-InstallContinuationMarkEntry!)))) (compile (WithContMark-body exp) cenv target body-next-linkage) (make-instruction-sequence `(,(make-PopControlFrame))))))])) (: append-instruction-sequences (InstructionSequence * -> InstructionSequence)) (define (append-instruction-sequences . seqs) (append-seq-list seqs)) (: append-2-sequences (InstructionSequence InstructionSequence -> InstructionSequence)) (define (append-2-sequences seq1 seq2) (make-instruction-sequence (append (statements seq1) (statements seq2)))) (: append-seq-list ((Listof InstructionSequence) -> InstructionSequence)) (define (append-seq-list seqs) (if (null? seqs) empty-instruction-sequence (append-2-sequences (car seqs) (append-seq-list (cdr seqs))))) (: ensure-natural (Integer -> Natural)) (define (ensure-natural n) (if (>= n 0) n (error 'ensure-natural "Not a natural: ~s\n" n))) (: ensure-prefix (CompileTimeEnvironmentEntry -> Prefix)) (define (ensure-prefix x) (if (Prefix? x) x (error 'ensure-prefix "Not a prefix: ~s" x))) (: ensure-lam (Any -> Lam)) (define (ensure-lam x) (if (Lam? x) x (error 'ensure-lam "Not a Lam: ~s" x))) (: adjust-target-depth (Target Natural -> Target)) (define (adjust-target-depth target n) (cond [(eq? target 'val) target] [(eq? target 'proc) target] [(eq? target 'argcount) target] [(EnvLexicalReference? target) (make-EnvLexicalReference (+ n (EnvLexicalReference-depth target)) (EnvLexicalReference-unbox? target))] [(EnvPrefixReference? target) (make-EnvPrefixReference (+ n (EnvPrefixReference-depth target)) (EnvPrefixReference-pos target))] [(PrimitivesReference? target) target] [(ControlFrameTemporary? target) target])) (: adjust-expression-depth (Expression Natural Natural -> Expression)) ;; Redirects references to the stack to route around a region of size n. ;; The region begins at offset skip into the environment. (define (adjust-expression-depth exp n skip) (cond [(Top? exp) (make-Top (Top-prefix exp) (adjust-expression-depth (Top-code exp) n (add1 skip)))] [(Constant? exp) exp] [(ToplevelRef? exp) (if (< (ToplevelRef-depth exp) skip) exp (make-ToplevelRef (ensure-natural (- (ToplevelRef-depth exp) n)) (ToplevelRef-pos exp)))] [(LocalRef? exp) (if (< (LocalRef-depth exp) skip) exp (make-LocalRef (ensure-natural (- (LocalRef-depth exp) n)) (LocalRef-unbox? exp)))] [(ToplevelSet? exp) (if (< (ToplevelSet-depth exp) skip) (make-ToplevelSet (ToplevelSet-depth exp) (ToplevelSet-pos exp) (ToplevelSet-name exp) (adjust-expression-depth (ToplevelSet-value exp) n skip)) (make-ToplevelSet (ensure-natural (- (ToplevelSet-depth exp) n)) (ToplevelSet-pos exp) (ToplevelSet-name exp) (adjust-expression-depth (ToplevelSet-value exp) n skip)))] [(Branch? exp) (make-Branch (adjust-expression-depth (Branch-predicate exp) n skip) (adjust-expression-depth (Branch-consequent exp) n skip) (adjust-expression-depth (Branch-alternative exp) n skip))] [(Lam? exp) (make-Lam (Lam-name exp) (Lam-num-parameters exp) (Lam-rest? exp) (Lam-body exp) (map (lambda: ([d : Natural]) (if (< d skip) d (ensure-natural (- d n)))) (Lam-closure-map exp)) (Lam-entry-label exp))] [(Seq? exp) (make-Seq (map (lambda: ([action : Expression]) (adjust-expression-depth action n skip)) (Seq-actions exp)))] [(Splice? exp) (make-Splice (map (lambda: ([action : Expression]) (adjust-expression-depth action n skip)) (Splice-actions exp)))] [(App? exp) (make-App (adjust-expression-depth (App-operator exp) n (+ skip (length (App-operands exp)))) (map (lambda: ([operand : Expression]) (adjust-expression-depth operand n (+ skip (length (App-operands exp))))) (App-operands exp)))] [(Let1? exp) (make-Let1 (adjust-expression-depth (Let1-rhs exp) n (add1 skip)) (adjust-expression-depth (Let1-body exp) n (add1 skip)))] [(LetVoid? exp) (make-LetVoid (LetVoid-count exp) (adjust-expression-depth (LetVoid-body exp) n (+ skip (LetVoid-count exp))) (LetVoid-boxes? exp))] [(LetRec? exp) (make-LetRec (let: loop : (Listof Lam) ([procs : (Listof Lam) (LetRec-procs exp)]) (cond [(empty? procs) '()] [else (cons (ensure-lam (adjust-expression-depth (first procs) n (+ skip (length (LetRec-procs exp))))) (loop (rest procs)))])) (adjust-expression-depth (LetRec-body exp) n (+ skip (length (LetRec-procs exp)))))] [(InstallValue? exp) (if (< (InstallValue-depth exp) skip) (make-InstallValue (InstallValue-depth exp) (adjust-expression-depth (InstallValue-body exp) n skip) (InstallValue-box? exp)) (make-InstallValue (ensure-natural (- (InstallValue-depth exp) n)) (adjust-expression-depth (InstallValue-body exp) n skip) (InstallValue-box? exp)))] [(BoxEnv? exp) (if (< (BoxEnv-depth exp) skip) (make-BoxEnv (BoxEnv-depth exp) (adjust-expression-depth (BoxEnv-body exp) n skip)) (make-BoxEnv (ensure-natural (- (BoxEnv-depth exp) n)) (adjust-expression-depth (BoxEnv-body exp) n skip)))] [(WithContMark? exp) (make-WithContMark (adjust-expression-depth (WithContMark-key exp) n skip) (adjust-expression-depth (WithContMark-value exp) n skip) (adjust-expression-depth (WithContMark-body exp) n skip))]))