Merge remote branch 'origin/master'

This commit is contained in:
Danny Yoo 2011-02-20 23:22:26 -05:00
commit 79074ccecb
3 changed files with 246 additions and 187 deletions

View File

@ -1,5 +1,5 @@
#lang racket/base
(require "structs.rkt"
#lang typed/racket/base
(require "typed-structs.rkt"
racket/string
racket/list)
@ -7,43 +7,46 @@
;; assemble/write-invoke: (listof statement) output-port -> void
(: assemble/write-invoke ((Listof Statement) Output-Port -> Void))
(define (assemble/write-invoke stmts op)
(let ([basic-blocks (fracture stmts)])
(fprintf op "function(k) {\n")
(for-each (lambda (basic-block)
(for-each (lambda: ([basic-block : BasicBlock])
(displayln (assemble-basic-block basic-block) op)
(newline op))
basic-blocks)
(fprintf op "MACHINE.cont = k;\n")
(fprintf op "trampoline(~a, function() {}); }"
(basic-block-name (first basic-blocks)))))
(BasicBlock-name (first basic-blocks)))))
;; fracture: (listof stmt) -> (listof basic-block)
(: fracture ((Listof Statement) -> (Listof BasicBlock)))
(define (fracture stmts)
(let* ([first-block-label (make-label 'start)]
[jump-targets
(cons first-block-label (collect-general-jump-targets stmts))])
(let loop ([name first-block-label]
[acc '()]
[basic-blocks '()]
[stmts stmts]
[last-stmt-goto? #f])
(let: loop : (Listof BasicBlock)
([name : Symbol first-block-label]
[acc : (Listof UnlabeledStatement) '()]
[basic-blocks : (Listof BasicBlock) '()]
[stmts : (Listof Statement) stmts]
[last-stmt-goto? : Boolean #f])
(cond
[(null? stmts)
(reverse (cons (make-basic-block name (reverse acc))
(reverse (cons (make-BasicBlock name (reverse acc))
basic-blocks))]
[(symbol? (car stmts))
(cond
[(member (car stmts) jump-targets)
(loop (car stmts)
'()
(cons (make-basic-block name
(cons (make-BasicBlock name
(if last-stmt-goto?
(reverse acc)
(reverse (append `((goto (label ,(car stmts))))
(reverse (append `(,(make-GotoStatement (make-Label (car stmts))))
acc))))
basic-blocks)
(cdr stmts)
@ -59,37 +62,39 @@
(cons (car stmts) acc)
basic-blocks
(cdr stmts)
(tagged-list? (car stmts) 'goto))]))))
(GotoStatement? (car stmts)))]))))
;; unique: (listof symbol -> listof symbol)
(: unique ((Listof Symbol) -> (Listof Symbol)))
(define (unique los)
(let ([ht (make-hasheq)])
(let: ([ht : (HashTable Symbol Boolean) (make-hasheq)])
(for ([l los])
(hash-set! ht l #t))
(for/list ([k (in-hash-keys ht)])
k)))
(hash-map ht (lambda: ([k : Symbol] [v : Boolean]) k))))
;; collect-general-jump-targets: (listof stmt) -> (listof label)
;; collects all the labels that are potential targets for GOTOs or branches.
(: collect-general-jump-targets ((Listof Statement) -> (Listof Symbol)))
(define (collect-general-jump-targets stmts)
(define (collect-input an-input)
(cond
[(reg? an-input)
[(Reg? an-input)
empty]
[(const? an-input)
[(Const? an-input)
empty]
[(label? an-input)
(list (label-name an-input))]
[(Label? an-input)
(list (Label-name an-input))]
[else (error 'collect-input "~e" an-input)]))
(define (collect-location a-location)
(cond
[(reg? a-location)
[(Reg? a-location)
empty]
[(label? a-location)
(list (label-name a-location))]
[(Label? a-location)
(list (Label-name a-location))]
[else (error 'collect-location "~e" a-location)]))
(unique
(let loop ([stmts stmts])
@ -100,32 +105,29 @@
(append (cond
[(symbol? stmt)
empty]
[(tagged-list? stmt 'assign)
(cond
[(reg? (caddr stmt))
empty]
[(label? (caddr stmt))
(list (label-name (caddr stmt)))]
[(const? (caddr stmt))
empty]
[(op? (caddr stmt))
(apply append (map collect-input (cdddr stmt)))]
[else
(error 'assemble "~a" stmt)])]
[(tagged-list? stmt 'perform)
(apply append (map collect-input (cddr stmt)))]
[(tagged-list? stmt 'test)
(apply append (map collect-input (cddr stmt)))]
[(tagged-list? stmt 'branch)
(collect-location (cadr stmt))]
[(tagged-list? stmt 'goto)
(collect-location (cadr stmt))]
[(tagged-list? stmt 'save)
[(AssignImmediateStatement? stmt)
(let ([v (AssignImmediateStatement-value stmt)])
(cond
[(Reg? v)
empty]
[(Label? v)
(list (Label-name v))]
[(Const? v)
empty]))]
[(AssignPrimOpStatement? stmt)
(apply append (map collect-input (AssignPrimOpStatement-rands stmt)))]
[(PerformStatement? stmt)
(apply append (map collect-input (PerformStatement-rands stmt)))]
[(TestStatement? stmt)
empty]
[(tagged-list? stmt 'restore)
[(BranchLabelStatement? stmt)
(list (BranchLabelStatement-label stmt))]
[(GotoStatement? stmt)
(collect-location (GotoStatement-target stmt))]
[(SaveStatement? stmt)
empty]
[else
(error 'assemble "~a" stmt)])
[(RestoreStatement? stmt)
empty])
(loop (rest stmts))))]))))
@ -135,7 +137,8 @@
;; indirect jumps.
;; The only interesting case should be where there's a register assignment
;; whose value is a label.
(define (collect-indirect-jump-targets stmts)
#;(: collect-indirect-jump-targets ((Listof Statement) -> (Listof Symbol)))
#;(define (collect-indirect-jump-targets stmts)
(define (collect-input an-input)
(cond
[(reg? an-input)
@ -196,70 +199,75 @@
;; assemble-basic-block: basic-block -> string
(: assemble-basic-block (BasicBlock -> String))
(define (assemble-basic-block a-basic-block)
(format "var ~a=function(){\nif(--MACHINE.callsBeforeTrampoline < 0) { throw ~a; }\n~a};"
(basic-block-name a-basic-block)
(basic-block-name a-basic-block)
(string-join (map assemble-stmt (basic-block-stmts a-basic-block))
(BasicBlock-name a-basic-block)
(BasicBlock-name a-basic-block)
(string-join (map assemble-stmt (BasicBlock-stmts a-basic-block))
"\n")))
;; assemble-stmt: stmt -> string
(: assemble-stmt (UnlabeledStatement -> String))
(define (assemble-stmt stmt)
(cond
[(tagged-list? stmt 'assign)
(cond
[(reg? (caddr stmt))
(format "MACHINE.~a=~a"
(cadr stmt)
(assemble-reg (caddr stmt)))]
[(label? (caddr stmt))
(format "MACHINE.~a=~a;" (cadr stmt)
(assemble-label (caddr stmt)))]
[(const? (caddr stmt))
(format "MACHINE.~a=~a;"
(cadr stmt)
(assemble-const (caddr stmt)))]
[(op? (caddr stmt))
(format "MACHINE.~a=~a;"
(cadr stmt)
(assemble-op-expression (op-name (caddr stmt))
(cdddr stmt)))]
[else
(error 'assemble "~a" stmt)])]
[(tagged-list? stmt 'perform)
(assemble-op-statement (op-name (cadr stmt))
(cddr stmt))]
[(tagged-list? stmt 'test)
[(AssignImmediateStatement? stmt)
(let ([v (AssignImmediateStatement-value stmt)])
(cond
[(Reg? v)
(format "MACHINE.~a=~a"
(AssignImmediateStatement-target stmt)
(assemble-reg v))]
[(Label? v)
(format "MACHINE.~a=~a;"
(AssignImmediateStatement-target stmt)
(assemble-label v))]
[(Const? v)
(format "MACHINE.~a=~a;"
(AssignImmediateStatement-target stmt)
(assemble-const v))]))]
[(AssignPrimOpStatement? stmt)
(format "MACHINE.~a=~a;"
(AssignPrimOpStatement-target stmt)
(assemble-op-expression (AssignPrimOpStatement-op stmt)
(AssignPrimOpStatement-rands stmt)))]
[(PerformStatement? stmt)
(assemble-op-statement (PerformStatement-op stmt)
(PerformStatement-rands stmt))]
[(TestStatement? stmt)
(format "if(~a){"
(assemble-op-expression (op-name (cadr stmt))
(cddr stmt)))]
[(tagged-list? stmt 'branch)
(assemble-op-expression (TestStatement-op stmt)
(list (make-Reg (TestStatement-register-rand stmt)))))]
[(BranchLabelStatement? stmt)
;; the unbalanced } is deliberate: test and branch always follow each other.
(format "return ~a();}"
(assemble-location (cadr stmt)))]
[(tagged-list? stmt 'goto)
(assemble-location (make-Label (BranchLabelStatement-label stmt))))]
[(GotoStatement? stmt)
(format "return ~a();"
(assemble-location (cadr stmt)))]
[(tagged-list? stmt 'save)
(assemble-location (GotoStatement-target stmt)))]
[(SaveStatement? stmt)
(format "MACHINE.stack.push(MACHINE.~a);"
(cadr stmt))]
[(tagged-list? stmt 'restore)
(SaveStatement-reg stmt))]
[(RestoreStatement? stmt)
(format "MACHINE.~a=MACHINE.stack.pop();"
(cadr stmt))]
[else (error 'assemble "~a" stmt)]))
(RestoreStatement-reg stmt))]))
;; fixme: use js->string
(: assemble-const (Const -> String))
(define (assemble-const stmt)
(let loop ([val (cadr stmt)])
(cond [(symbol? val)
(format "~s" (symbol->string val))]
[(list? val)
(format "_list(~a)" (string-join (map loop val)
","))]
[else
(format "~s" val)])))
(let: loop : String ([val : Any (Const-const stmt)])
(cond [(symbol? val)
(format "~s" (symbol->string val))]
[(list? val)
(format "_list(~a)" (string-join (map loop val)
","))]
[else
(format "~s" val)])))
(: assemble-op-expression (Symbol (Listof OpArg) -> String))
(define (assemble-op-expression op-name inputs)
(let ([assembled-inputs (map assemble-input inputs)])
(case op-name
@ -280,7 +288,7 @@
(cond [(empty? inputs)
"undefined"]
[else
(let loop ([assembled-inputs assembled-inputs])
(let: loop : String ([assembled-inputs : (Listof String) assembled-inputs])
(cond
[(empty? assembled-inputs)
"undefined"]
@ -311,7 +319,7 @@
[else
(error 'assemble "~e" op-name)])))
(: assemble-op-statement (Symbol (Listof OpArg) -> String))
(define (assemble-op-statement op-name inputs)
(let ([assembled-inputs (map assemble-input inputs)])
(case op-name
@ -341,28 +349,28 @@
(: assemble-input ((U Reg Const Label) -> String))
(define (assemble-input an-input)
(cond
[(reg? an-input)
[(Reg? an-input)
(assemble-reg an-input)]
[(const? an-input)
[(Const? an-input)
(assemble-const an-input)]
[(label? an-input)
(assemble-label an-input)]
[else (error 'assemble-input "~e" an-input)]))
[(Label? an-input)
(assemble-label an-input)]))
(: assemble-location ((U Reg Label) -> String))
(define (assemble-location a-location)
(cond
[(reg? a-location)
[(Reg? a-location)
(assemble-reg a-location)]
[(label? a-location)
(assemble-label a-location)]
[else (error 'assemble-location "~e" a-location)]))
[(Label? a-location)
(assemble-label a-location)]))
(: assemble-reg (Reg -> String))
(define (assemble-reg a-reg)
(string-append "MACHINE." (symbol->string (cadr a-reg))))
(string-append "MACHINE." (symbol->string (Reg-name a-reg))))
(: assemble-label (Label -> String))
(define (assemble-label a-label)
(symbol->string (label-name a-label)))
(symbol->string (Label-name a-label)))

View File

@ -1,7 +1,6 @@
#lang typed/racket/base
(require "typed-structs.rkt"
#;"assemble.rkt"
racket/list)
(provide compile)
@ -19,6 +18,8 @@
(define-type CompileTimeEnvironment (Listof (Listof Symbol)))
(define-type LexicalAddress (U (List Number Number) 'not-found))
;; find-variable: symbol compile-time-environment -> lexical-address
;; Find where the variable should be located.
(: find-variable (Symbol CompileTimeEnvironment -> LexicalAddress))
@ -80,12 +81,12 @@
(define (compile-linkage linkage)
(cond
[(eq? linkage 'return)
(make-instruction-sequence '(cont) '() '((goto (reg cont))))]
(make-instruction-sequence '(cont) '() `(,(make-GotoStatement (make-Reg 'cont))))]
[(eq? linkage 'next)
empty-instruction-sequence]
[else
(make-instruction-sequence '() '()
`((goto (label ,linkage))))]))
`(,(make-GotoStatement (make-Label linkage))))]))
(: end-with-linkage (Linkage InstructionSequence -> InstructionSequence))
(define (end-with-linkage linkage instruction-sequence)
@ -99,7 +100,7 @@
(make-instruction-sequence
'()
(list target)
`((assign ,target (const ,exp))))))
`(,(make-AssignImmediateStatement target (make-Const exp))))))
(: compile-quoted (Quote CompileTimeEnvironment Target Linkage -> InstructionSequence))
(define (compile-quoted exp cenv target linkage)
@ -107,7 +108,7 @@
(make-instruction-sequence
'()
(list target)
`((assign ,target (const ,(Quote-text exp)))))))
`(,(make-AssignImmediateStatement target (make-Const (Quote-text exp)))))))
(: compile-variable (Var CompileTimeEnvironment Target Linkage -> InstructionSequence))
(define (compile-variable exp cenv target linkage)
@ -120,23 +121,23 @@
(list target)
;; Slight modification: explicitly testing for
;; global variable binding before lookup.
`((perform (op check-bound-global!)
(const ,(Var-id exp))
(reg env))
(assign ,target
(op lookup-variable-value)
(const ,(Var-id exp))
(reg env)))))]
`(,(make-PerformStatement 'check-bound-global!
(list (make-Const (Var-id exp))
(make-Reg 'env)))
,(make-AssignPrimOpStatement target
'lookup-variable-value
(list (make-Const (Var-id exp))
(make-Reg 'env))))))]
[else
(end-with-linkage linkage
(make-instruction-sequence
'(env)
(list target)
`((assign ,target
(op lexical-address-lookup)
(const ,(first lexical-pos))
(const ,(second lexical-pos))
(reg env)))))])))
`(,(make-AssignPrimOpStatement target
'lexical-address-lookup
(list (make-Const (first lexical-pos))
(make-Const (second lexical-pos))
(make-Reg 'env))))))])))
(: compile-assignment (Assign CompileTimeEnvironment Target Linkage -> InstructionSequence))
@ -155,11 +156,11 @@
(make-instruction-sequence
'(env val)
(list target)
`((perform (op set-variable-value!)
(const ,var)
(reg val)
(reg env))
(assign ,target (const ok))))))]
`(,(make-PerformStatement 'set-variable-value!
(list (make-Const var)
(make-Reg 'val)
(make-Reg 'env)))
,(make-AssignImmediateStatement target (make-Const 'ok))))))]
[else
(end-with-linkage
linkage
@ -168,12 +169,12 @@
(make-instruction-sequence
'(env val)
(list target)
`((perform (op lexical-address-set!)
(const ,(first lexical-address))
(const ,(second lexical-address))
(reg env)
(reg val))
(assign ,target (const ok))))))])))
`(,(make-PerformStatement 'lexical-address-set!
(list (make-Const (first lexical-address))
(make-Const (second lexical-address))
(make-Reg 'env)
(make-Reg 'val)))
,(make-AssignImmediateStatement target (make-Const 'ok))))))])))
;; FIXME: exercise 5.43
@ -190,11 +191,11 @@
(make-instruction-sequence
'(env val)
(list target)
`((perform (op define-variable!)
(const ,var)
(reg val)
(reg env))
(assign ,target (const ok))))))))
`(,(make-PerformStatement 'define-variable!
(list (make-Const var)
(make-Reg 'val)
(make-Reg 'env)))
,(make-AssignImmediateStatement target (make-Const 'ok))))))))
(: compile-if (Branch CompileTimeEnvironment Target Linkage -> InstructionSequence))
@ -215,8 +216,8 @@
(make-instruction-sequence
'(val)
'()
`((test (op false?) (reg val))
(branch (label ,f-branch))))
`(,(make-TestStatement 'false? 'val)
,(make-BranchLabelStatement f-branch)))
(parallel-instruction-sequences
(append-instruction-sequences t-branch c-code)
(append-instruction-sequences f-branch a-code))
@ -246,16 +247,16 @@
(make-instruction-sequence
'(env)
(list target)
`((assign ,target
(op make-compiled-procedure)
(label ,proc-entry)
;; TODO: rather than capture the whole
;; environment, we may instead
;; just capture the free variables.
;; But that requires that we box
;; up all set!-ed variables, in order
;; to preserve semantics of set!
(reg env)))))
`(,(make-AssignPrimOpStatement target
'make-compiled-procedure
(list (make-Label proc-entry)
;; TODO: rather than capture the whole
;; environment, we may instead
;; just capture the free variables.
;; But that requires that we box
;; up all set!-ed variables, in order
;; to preserve semantics of set!
(make-Reg 'env))))))
(compile-lambda-body exp cenv
proc-entry))
after-lambda))))
@ -269,11 +270,13 @@
'(env proc argl)
'(env)
`(,proc-entry
(assign env (op compiled-procedure-env) (reg proc))
(assign env
(op extend-environment)
(reg argl)
(reg env))))
,(make-AssignPrimOpStatement 'env
'compiled-procedure-env
(list (make-Reg 'proc)))
,(make-AssignPrimOpStatement 'env
'extend-environment
(list (make-Reg 'argl)
(make-Reg 'env)))))
(compile-sequence (Lam-body exp) extended-cenv 'val 'return))))
(: compile-application (App CompileTimeEnvironment Target Linkage -> InstructionSequence))
@ -294,12 +297,13 @@
(if (null? operand-codes)
(make-instruction-sequence '()
'(argl)
'((assign argl (const ()))))
`(,(make-AssignImmediateStatement 'argl (make-Const '()))))
(let ([code-to-get-last-arg
(append-instruction-sequences
(car operand-codes)
(make-instruction-sequence '(val) '(argl)
'((assign argl (op list) (reg val)))))])
`(,(make-AssignPrimOpStatement 'argl 'list
(list (make-Reg 'val))))))])
(if (null? (cdr operand-codes))
code-to-get-last-arg
(preserving '(env)
@ -314,7 +318,10 @@
(make-instruction-sequence
'(val argl)
'(argl)
'((assign argl (op cons) (reg val) (reg argl)))))])
`(,(make-AssignPrimOpStatement 'argl
'cons
(list (make-Reg 'val)
(make-Reg 'argl))))))])
(if (null? (cdr operand-codes))
code-for-next-arg
(preserving '(env)
@ -330,8 +337,8 @@
(if (eq? linkage 'next) after-call linkage)])
(append-instruction-sequences
(make-instruction-sequence '(proc) '()
`((test (op primitive-procedure?) (reg proc))
(branch (label ,primitive-branch))))
`(,(make-TestStatement 'primitive-procedure? 'proc)
,(make-BranchLabelStatement primitive-branch)))
(parallel-instruction-sequences
(append-instruction-sequences
compiled-branch
@ -342,10 +349,10 @@
(make-instruction-sequence
'(proc argl)
(list target)
`((assign ,target
(op apply-primitive-procedure)
(reg proc)
(reg argl)))))))
`(,(make-AssignPrimOpStatement target
'apply-primitive-procedure
(list (make-Reg 'proc)
(make-Reg 'argl))))))))
after-call))))
(: compile-proc-appl (Target Linkage -> InstructionSequence))
@ -355,31 +362,31 @@
(make-instruction-sequence
'(proc)
all-regs
`((assign cont (label ,linkage))
(assign val (op compiled-procedure-entry)
(reg proc))
(goto (reg val))))]
`(,(make-AssignImmediateStatement 'cont (make-Label linkage))
,(make-AssignPrimOpStatement 'val 'compiled-procedure-entry
(list (make-Reg 'proc)))
,(make-GotoStatement (make-Reg 'val))))]
[(and (not (eq? target 'val))
(not (eq? linkage 'return)))
(let ([proc-return (make-label 'procReturn)])
(make-instruction-sequence
'(proc)
all-regs
`((assign cont (label ,proc-return))
(assign val (op compiled-procedure-entry)
(reg proc))
(goto (reg val))
`(,(make-AssignImmediateStatement 'cont (make-Label proc-return))
,(make-AssignPrimOpStatement 'val 'compiled-procedure-entry
(list (make-Reg 'proc)))
,(make-GotoStatement (make-Reg 'val))
,proc-return
(assign ,target (reg val))
(goto (label ,linkage)))))]
,(make-AssignImmediateStatement target (make-Reg 'val))
,(make-GotoStatement (make-Label linkage)))))]
[(and (eq? target 'val)
(eq? linkage 'return))
(make-instruction-sequence
'(proc cont)
all-regs
'((assign val (op compiled-procedure-entry)
(reg proc))
(goto (reg val))))]
`(,(make-AssignPrimOpStatement 'val 'compiled-procedure-entry
(list (make-Reg 'proc)))
,(make-GotoStatement (make-Reg 'val))))]
[(and (not (eq? target 'val))
(eq? linkage 'return))
(error 'compile "return linkage, target not val: ~s" target)]))
@ -413,9 +420,9 @@
(registers-needed seq1))
(list-difference (registers-modified seq1)
(list first-reg))
(append `((save ,first-reg))
(append `(,(make-SaveStatement first-reg))
(statements seq1)
`((restore ,first-reg))))
`(,(make-RestoreStatement first-reg))))
seq2)
(preserving (cdr regs) seq1 seq2)))))

View File

@ -30,11 +30,50 @@
;; instruction sequences
(define-type UnlabeledStatement (U
AssignImmediateStatement
AssignPrimOpStatement
PerformStatement
TestStatement
BranchLabelStatement
GotoStatement
SaveStatement
RestoreStatement))
(define-type Statement (U UnlabeledStatement
Symbol ;; label
))
(define-struct: AssignImmediateStatement ([target : Symbol]
[value : (U Const Reg Label)])
#:transparent)
(define-struct: AssignPrimOpStatement ([target : Symbol]
[op : Symbol]
[rands : (Listof (U Label Reg Const))])
#:transparent)
(define-struct: PerformStatement ([op : Symbol]
[rands : (Listof (U Label Reg Const))]) #:transparent)
(define-struct: TestStatement ([op : (U 'false? 'primitive-procedure?)]
[register-rand : Symbol]) #:transparent)
(define-struct: BranchLabelStatement ([label : Symbol]) #:transparent)
(define-struct: GotoStatement ([target : (U Label Reg)]) #:transparent)
(define-struct: SaveStatement ([reg : Symbol]) #:transparent)
(define-struct: RestoreStatement ([reg : Symbol]) #:transparent)
(define-struct: Label ([name : Symbol]))
(define-struct: Reg ([name : Symbol]))
(define-struct: Const ([const : Any]))
(define-type OpArg (U Const Label Reg))
(define-type InstructionSequence (U Symbol instruction-sequence))
(define-struct: instruction-sequence ([needs : (Listof Symbol)]
[modifies : (Listof Symbol)]
[statements : (Listof Any)]) #:transparent)
[statements : (Listof Statement)]) #:transparent)
(define empty-instruction-sequence (make-instruction-sequence '() '() '()))
(: make-label (Symbol -> Symbol))
@ -53,7 +92,7 @@
(define (registers-modified s)
(if (symbol? s) '() (instruction-sequence-modifies s)))
(: statements (InstructionSequence -> (Listof Any)))
(: statements (InstructionSequence -> (Listof Statement)))
(define (statements s)
(if (symbol? s) (list s) (instruction-sequence-statements s)))
@ -63,4 +102,9 @@
(define-type Target Symbol)
;; Linkage
(define-type Linkage (U 'return 'next Symbol))
(define-type Linkage (U 'return 'next Symbol))
(define-struct: BasicBlock ([name : Symbol]
[stmts : (Listof UnlabeledStatement)]) #:transparent)