-
Notifications
You must be signed in to change notification settings - Fork 10
/
elcomp.el
290 lines (248 loc) · 9.79 KB
/
elcomp.el
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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
;;; elcomp.el - Compiler for Emacs Lisp. -*- lexical-binding:t -*-
;;; Commentary:
;; This holds basic definitions for the compiler. Everything else is
;; in the elcomp subdir.
;;; Code:
(require 'cl-macs)
(require 'eieio)
(cl-defstruct (elcomp (:conc-name elcomp--))
;; An alist holding symbol rewrites. The car of each element is a
;; symbol in the original code. The cdr is the symbol to which it
;; is rewritten.
rewrite-alist
;; Next label value.
(next-label 0)
;; The entry block.
entry-block
;; The current basic block.
current-block
;; True if the back-edges in the CFG are considered valid.
;; FIXME - deal with IDOM being invalid too
back-edges-valid
;; The current list of exception handlers.
exceptions
;; The current defun being compiled.
;; This is a list (NAME ARGLIST DOC INTERACTIVE).
;; NAME is nil for an anonymous function.
;; FIXME this should just be separate slots of this struct.
defun
;; The name of the defun, a symbol. This must be computed using
;; elcomp--get-name, as this is either set lazily from 'defun', or
;; generated for lambdas.
name
;; In SSA mode, a list of the argument objects representing the
;; arguments to the defun.
arguments
;; A back link to the compilation unit. This is needed so we can
;; push new functions into the compilation unit as we go.
unit)
(cl-defstruct elcomp--compilation-unit
;; A hash table mapping a cons (a defun or a lambda) to a compiler
;; object.
(defuns (make-hash-table))
;; The work-list. This is separate from `defuns' for convenience.
work-list)
(cl-defstruct elcomp--basic-block
;; Block number.
number
;; The code for this basic block.
code
;; Last link of linearized code.
code-link
;; A hash table holding back-links to parent nodes.
;; Outgoing edges are represented directly by the last instruction
;; in the code sequence.
parents
;; The immediate dominator, or nil if not known.
immediate-dominator
;; The list of exception handlers.
exceptions
;; The phi nodes for this basic block. This is a hash table whose
;; keys are original variable names and whose values are phis. This
;; starts as nil and is initialized when converting to SSA form.
phis
;; Final type map for this BB.
final-type-map
;; Entry type map for this BB. This is not needed after type
;; inferencing. FIXME store on the side.
type-map)
(defclass elcomp--set nil
((sym :initform nil :initarg :sym
:accessor elcomp--sym
:documentation "The local variable being assigned to.
Initially this is a symbol.
After transformation to SSA, this will be an SSA name;
see `elcomp--ssa-name-p'.")
(value :initform nil :initarg :value
:accessor elcomp--value
:documentation "The value being assigned.
Initially this is a symbol.
After transformation to SSA, this will be an SSA name."))
"A `set' instruction.
This represents a simple assignment to a local variable.")
(defclass elcomp--call nil
((sym :initform nil :initarg :sym
:accessor elcomp--sym
:documentation "The local variable being assigned to.
This can be `nil' if the result of the call is not used.
Initially this is a symbol.
After transformation to SSA, this will be an SSA name;
see `elcomp--ssa-name-p'.")
(func :initform nil :initarg :func
:accessor elcomp--func
:documentation "The function to call.
This may be a symbol or a `lambda' list.")
(args :initform nil :initarg :args
:accessor elcomp--args
;; FIXME - can a symbol wind up in here or do we make
;; symbol-value explicit?
:documentation "The arguments to the function.
Initially this is a list of symbols.
After transformation to SSA, this will be a list of SSA names."))
"A function call instruction.")
(defclass elcomp--goto nil
((block :initform nil :initarg :block
:accessor elcomp--block
:documentation "The target block."))
"A `goto' instruction.
This instruction terminates a block.")
(defclass elcomp--if nil
((sym :initform nil :initarg :sym
:accessor elcomp--sym
:documentation "The condition to check.
Initially this is a symbol.
After transformation to SSA, this will be an SSA name;
see `elcomp--ssa-name-p'.")
(block-true :initform nil :initarg :block-true
:accessor elcomp--block-true
:documentation "The target block if the value is non-`nil'.")
(block-false :initform nil :initarg :block-false
:accessor elcomp--block-false
:documentation "The target block if the value is `nil'."))
"An `if' instruction.
This branches to one of two blocks based on whether or not the
argument is `nil'. This instruction terminates a block.")
(defclass elcomp--return nil
((sym :initform nil :initarg :sym
:accessor elcomp--sym
:documentation "The value to return.
Initially this is a symbol.
After transformation to SSA, this will be an SSA name;
see `elcomp--ssa-name-p'."))
"A `return' instruction.")
(defclass elcomp--diediedie (elcomp--call)
()
"An instruction which terminates a basic block without leading anywhere.
This can only be for a call to a `nothrow' function.")
(defclass elcomp--constant nil
((value :initform nil :initarg :value
:accessor elcomp--value
:documentation "The value of the constant."))
"This represents a constant after transformation to SSA form.")
(defclass elcomp--phi nil
((original-name :initform nil :initarg :original-name
:accessor elcomp--original-name
:documentation "The original name of this node.
This is handy for debugging.")
(args :initform (make-hash-table) :initarg :args
:accessor elcomp--args
:documentation "Arguments to this node.
This is a hash table whose keys are possible source values for the phi.
The values in the hash table are meaningless."))
"A `phi' node.
See any good source of information about SSA to understand this.")
(defclass elcomp--argument nil
((original-name :initform nil :initarg :original-name
:accessor elcomp--original-name
:documentation "The original name of this node.
This is handy for debugging.")
(is-rest :initform nil :initarg :is-rest
:accessor elcomp--is-rest
:documentation "True if this argument was from `&rest'."))
"A function argument. This is only used in SSA form.")
(defclass elcomp--exception nil
((handler :initform nil :initarg :handler
:accessor elcomp--handler
:documentation "The target block of this exception edge."))
"An exception edge.
A block's `exceptions' slot is a list of all the active exception
handlers, though in most cases only the first one is ever
taken.")
(defclass elcomp--catch (elcomp--exception)
((tag :initform nil :initarg :tag
:accessor elcomp--tag
:documentation "The tag of the `catch'."))
"An exception edge representing a `catch'.")
(defclass elcomp--condition-case (elcomp--exception)
((condition-name :initform nil :initarg :condition-name
:accessor elcomp--condition-name
:documentation "The name of the condition being handled.
This is either a symbol or nil. Note that the variable that can
be bound by `condition-case' is explicit in the target block."))
"An exception edge representing a single `condition-case' handler.")
(defclass elcomp--unwind-protect (elcomp--exception)
;; The original form is used when optimizing "catch".
;; Well.. it will be someday. FIXME.
((original-form :initform nil :initarg :original-form
:documentation "The original form.
This is not used now but may be later for `catch' optimization."))
"An exception edge representing an `unwind-protect'.")
;; A fake unwind-protect that is used to represent the unbind
;; operation from a `let' of a special variable. This is needed to
;; properly deal with `catch' optimization from inside a `let', like:
;; (catch 'x (let* ((var1 (something)) (var2 (throw 'x 99))) ...))
;; Here, the `throw' has to unbind "var1".
(defclass elcomp--fake-unwind-protect (elcomp--exception)
((count :initform nil :initarg :count
:accessor elcomp--count
:documentation "The number of unbinds that this represents."))
"An exception edge representing the unbind operation from a `let'
of a special variable. These unbinds are done implicitly, so this
exception edge does not represent any ordinary code -- but it is needed
to properly deal do the `catch' optimization from inside a `let', like:
(catch 'x (let* ((var1 (something)) (var2 (throw 'x 99))) ...))
Here, the `throw' has to unbind `var1'.")
(defun elcomp--ssa-name-p (arg)
"Return t if ARG is an SSA name."
(or
(elcomp--set-p arg)
(elcomp--phi-p arg)
(elcomp--call-p arg)
(elcomp--argument-p arg)))
(defun elcomp--last-instruction (block)
"Return the last instruction in BLOCK.
This can be used with `setf'."
(car (elcomp--basic-block-code-link block)))
(gv-define-setter elcomp--last-instruction (val block)
`(setcar (elcomp--basic-block-code-link ,block) ,val))
(defun elcomp--first-instruction (block)
"Return the first instruction in BLOCK.
This can be used with `setf'."
(car (elcomp--basic-block-code block)))
(gv-define-setter elcomp--first-instruction (val block)
`(setcar (elcomp--basic-block-code ,block) ,val))
(defun elcomp--nonreturn-terminator-p (obj)
"Return t if OBJ is a block-terminating instruction other than
`return' or `diediedie'."
(or (elcomp--goto-p obj)
(elcomp--if-p obj)))
(defun elcomp--terminator-p (obj)
"Return t if OBJ terminates a block."
(or (elcomp--goto-p obj)
(elcomp--if-p obj)
(elcomp--return-p obj)
(elcomp--diediedie-p obj)))
(cl-defun elcomp--any-hash-key (hash)
"Return any key of the hash table HASH, or nil."
(maphash (lambda (key _ignore) (cl-return-from elcomp--any-hash-key key))
hash))
(defun elcomp--get-name (elcomp)
"Get the name of the function represented by ELCOMP."
(unless (elcomp--name elcomp)
(setf (elcomp--name elcomp)
(if (car (elcomp--defun elcomp))
(car (elcomp--defun elcomp))
(cl-gensym "__lambda"))))
(elcomp--name elcomp))
(provide 'elcomp)
;;; elcomp.el ends here