aboutsummaryrefslogtreecommitdiffstats
path: root/csc/linker.csc
blob: 9095647dd161d6de43a1152b80432fef84c9c1b1 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
(define-library (csc linker)
  (export
    add-to-environment
    compare-globals
    link)
  (import (scheme base)
          (only (csc format)
            sprintf)
          (only (csc hash-map)
            compare-numbers
            hash-bytevector
            insert
            lookup
            make-comparer
            make-map
            map-for-each)
          (only (csc loop)
            loop
            return)
          (only (csc match) match)
          (only (scheme case-lambda)
            case-lambda))
  (begin
    ; A CSC bytecode program is a list of opcodes and labels. An opcode is a
    ; list of an opcode and arguments. Arguments can be any of:
    ;   - (local x),
    ;   - (global x lib),
    ;   - (const x),
    ;   - or (label x).
    ; The full list of opcodes can be found in encoding.csc.


    ; Rewrites each (label x) form into a integer constant.
    (define (translate-labels program label-map)
      (loop for opcode in program
            for op = (car opcode)
            for args = (cdr opcode)
            unless (symbol=? op 'label)
              collect (cons op (loop for arg in args
                                     collect (match arg
                                               (('label 'init) 
                                                 (list 'const (lookup label-map -1)))
                                               (('label x)
                                                 (list 'const (lookup label-map x)))
                                               (_ arg))))))


    (define (make-label-map offset program)
      (loop for op in program
            for i from offset
            with m = (make-map compare-numbers)
            do (match op
                 (('label 'init)
                   (set! m (insert m -1 i))
                   (set! i (- i 1)))  ; Labels will be removed later.
                 (('label id)
                   (set! m (insert m id i))
                   (set! i (- i 1))))
            finally (return m)))


    (define (add-to-environment environment programs)
      (define next-global-id 0)
      (map-for-each (lambda (k v)
                      (when (>= v next-global-id)
                        (set! next-global-id (+ 1 v))))
        environment)
      (loop for opcode in (apply append programs)
            do (loop for arg in (cdr opcode)
                     do (match arg
                          (('global name lib)
                            (define id next-global-id)
                            (set! next-global-id (+ 1 next-global-id))
                            (set! environment (insert environment arg id))))))
      environment)


    (define compare-globals
      (make-comparer
        (lambda (x)
          (hash-bytevector (string->utf8 (sprintf "{}" x))))
        (lambda (x y)
          (cond
            ((equal? x y) 0)
            ((string<? (sprintf "{}" x) (sprintf "{}" y)) -1)
            (else 1)))))


    (define (translate-globals program environment)
      (loop for opcode in program
            for op = (car opcode)
            for args = (cdr opcode)
            collect (cons op (loop for arg in args
                                   collect (match arg
                                             (('global x lib)
                                               (list 'const (lookup environment arg)))
                                             (_ arg))))))


    (define link
      (case-lambda
        ((programs environment)
          (translate-globals
            (loop for prog in programs
                  for off = 0 then (+ off (length converted-prog))
                  for converted-prog = (let ((prog-prelude (cons
                                                             (list 'jmp '(label init))
                                                             prog)))
                                         (translate-labels
                                           prog-prelude
                                           (make-label-map off prog-prelude)))
                  append converted-prog)
            environment))
        ((programs)
          (link programs (add-to-environment (make-map compare-globals) programs)))))))