File: compiler-opt.jl

package info (click to toggle)
librep 0.9-2
  • links: PTS
  • area: main
  • in suites: potato
  • size: 2,576 kB
  • ctags: 1,928
  • sloc: ansic: 21,612; sh: 7,386; lisp: 5,331; makefile: 392; sed: 93
file content (477 lines) | stat: -rw-r--r-- 14,931 bytes parent folder | download
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
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
;;;; compiler-opt.jl -- low-level compiler optimisations
;;;  Copyright (C) 1999 John Harper <john@dcs.warwick.ac.uk>
;;;  $Id: compiler-opt.jl,v 1.3 1999/12/11 11:37:35 john Exp $

;;; This file is part of librep.

;;; librep is free software; you can redistribute it and/or modify it
;;; under the terms of the GNU General Public License as published by
;;; the Free Software Foundation; either version 2, or (at your option)
;;; any later version.

;;; librep is distributed in the hope that it will be useful, but
;;; WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;;; GNU General Public License for more details.

;;; You should have received a copy of the GNU General Public License
;;; along with Jade; see the file COPYING.  If not, write to
;;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.

;; Most of the optimisation patterns in the peephole optimiser were
;; lifted from jwz's byte-optimize.el (XEmacs)

(require 'compiler)
(require 'bytecodes)
(provide 'compiler-opt)


;; Peephole optimiser

;; todo:

;; c{dd..d}r; car --> ca{dd..d}r
;; c{dd..d}r; cdr --> cd{dd..d}r

;; shift the instruction window
(defmacro comp-peep-shift ()
  '(progn
     (setq point (cdr point))
     (setq insn0 insn1)
     (setq insn1 insn2)
     (setq insn2 (nth 3 point))))

;; refill the window
(defmacro comp-peep-refill ()
  '(progn
     (setq insn0 (nth 1 point))
     (setq insn1 (nth 2 point))
     (setq insn2 (nth 3 point))))

;; delete the first instruction in the window
(defmacro comp-peep-del-0 ()
  '(progn
     (rplacd point (nthcdr 2 point))
     (setq insn0 insn1)
     (setq insn1 insn2)
     (setq insn2 (nth 3 point))))

;; delete the second instruction in the window
(defmacro comp-peep-del-1 ()
  '(progn
     (rplacd (cdr point) (nthcdr 3 point))
     (setq insn1 insn2)
     (setq insn2 (nth 3 point))))

;; delete the third instruction in the window
(defmacro comp-peep-del-2 ()
  '(progn
     (rplacd (nthcdr 2 point) (nthcdr 4 point))
     (setq insn2 (nth 3 point))))

;; delete the first two instructions in the window
(defmacro comp-peep-del-0-1 ()
  '(progn
     (rplacd point (nthcdr 3 point))
     (setq insn0 insn2)
     (setq insn1 (nth 2 point))
     (setq insn2 (nth 3 point))))

;; delete the second two instructions in the window
(defmacro comp-peep-del-1-2 ()
  '(progn
     (rplacd (cdr point) (nthcdr 4 point))
     (setq insn1 (nth 2 point))
     (setq insn2 (nth 3 point))))

;; delete all instructions in the window
(defmacro comp-peep-del-0-1-2 ()
  '(progn
     (rplacd point (nthcdr 4 point))
     (comp-peep-refill)))

;; run the optimiser over CODE-STRING, modifying and returning it
;; this assumes it's being called from somewhere inside the compiler;
;; it may modify comp-max-stack
(defun comp-peephole-opt (code-string)
  (let
      ((keep-going t)
       (extra-stack 0)
       point insn0 insn1 insn2 tem)
    ;; add an extra cons cell so we can always refer to the
    ;; cdr of the intsruction _before_ insn0, this makes it
    ;; easy to delete instructions
    (setq code-string (cons 'start code-string))
    (while keep-going
      (setq keep-going nil)
      (setq point code-string)
      (comp-peep-refill)
      (while insn0
	(cond
	 ;; <side-effect-free w/ stack+1>; pop --> <deleted>
	 ;; <side-effect-free w/ stack+0>; pop --> pop
	 ;; <side-effect-free w/ stack-1>; pop --> pop; pop
	 ((and (eq (car insn1) op-pop)
	       (memq (car insn0) comp-side-effect-free-insns))
	  (setq tem (aref comp-insn-stack-delta (car insn0)))
	  (cond ((= tem 1)
		 (comp-peep-del-0-1)
		 (setq keep-going t))
		((= tem 0)
		 (comp-peep-del-0)
		 (setq keep-going t))
		((= tem -1)
		 (rplaca insn0 op-pop)
		 (rplacd insn0 nil)
		 (setq keep-going t))))

	 ;; {<const>,dup}; {setq,bind} X; refq X
	 ;;    --> {<const>,dup}; {setq,bind} X; {<const>,dup}
	 ((and (or (eq (car insn1) op-setq)
		   (eq (car insn1) op-bind))
	       (eq (car insn2) op-refq)
	       (eq (cdr insn1) (cdr insn2))
	       (or (eq (car insn0) op-dup)
		   (memq (car insn0) comp-constant-insns)))
	  (rplaca insn2 (car insn0))
	  (rplacd insn2 (cdr insn0))
	  (setq keep-going t))

	 ;; {setq,bind} X; refq X --> dup; {setq,bind} X
	 ((and (or (eq (car insn0) op-setq)
		   (eq (car insn0) op-bind))
	       (eq (car insn1) op-refq)
	       (eq (cdr insn0) (cdr insn1)))
	  (rplaca insn1 (car insn0))
	  (rplaca insn0 op-dup)
	  (rplacd insn0 nil)
	  ;; this might require extra stack space
	  (setq extra-stack 1)
	  (setq keep-going t))

	 ;; dup; {setq,bind} X; pop --> {setq,bind} X
	 ((and (eq (car insn0) op-dup)
	       (or (eq (car insn1) op-setq)
		   (eq (car insn1) op-bind))
	       (eq (car insn2) op-pop))
	  (rplaca insn2 (car insn1))
	  (rplacd insn2 (cdr insn1))
	  (comp-peep-del-0-1)
	  (setq keep-going t))

	 ;; refq X; refq X --> refq X; dup
	 ((and (eq (car insn0) op-refq)
	       (eq (car insn1) op-refq)
	       (eq (cdr insn0) (cdr insn1)))
	  (rplaca insn1 op-dup)
	  (rplacd insn1 nil)
	  (setq keep-going t))

	 ;; c?r; c?r --> c??r
	 ((and (or (eq (car insn0) op-car)
		   (eq (car insn0) op-cdr))
	       (or (eq (car insn1) op-car)
		   (eq (car insn1) op-cdr)))
	  (rplaca insn1 (if (eq (car insn0) op-car)
			    (if (eq (car insn1) op-car)
				op-caar
			      op-cdar)
			  (if (eq (car insn1) op-car)
			      op-cadr
			    op-cddr)))
	  (comp-peep-del-0)
	  (setq keep-going t))

	 ;; jmp X; X: --> X:
	 ((and (eq (car insn0) op-jmp)
	       (eq (cdr insn0) insn1))
	  (comp-peep-del-0)
	  (setq keep-going t))

	 ;; {jn,jt} X; X: --> pop; X:
	 ((and (or (eq (car insn0) op-jn) (eq (car insn0) op-jt))
	       (eq (cdr insn0) insn1))
	  (rplaca insn0 op-pop)
	  (rplacd insn0 nil)
	  (setq keep-going t))

	 ;; {jpt,jpn} X; pop --> {jt,jn} X
	 ((and (or (eq (car insn0) op-jpt)
		   (eq (car insn0) op-jpn))
	       (eq (car insn1) op-pop))
	  (rplaca insn0 (if (eq (car insn0) op-jpt) op-jt op-jn))
	  (comp-peep-del-1)
	  (setq keep-going t))

	 ;; not; {jn,jt} X --> {jt,jn} X
	 ((and (eq (car insn0) op-not)
	       (or (eq (car insn1) op-jn)
		   (eq (car insn1) op-jt))
	       (memq (car insn1) comp-conditional-jmp-insns))
	  (rplaca insn1 (if (eq (car insn1) op-jn) op-jt op-jn))
	  (comp-peep-del-0)
	  (setq keep-going t))

	 ;; {jn,jt} X; jmp Y; X: --> {jt,jn} Y; X:
	 ((and (or (eq (car insn1) op-jn)
		   (eq (car insn1) op-jt))
	       (eq (car insn1) op-jmp)
	       (eq (car insn2) (cdr insn0)))
	  (rplaca insn1 (if (eq (car insn0) op-jn) op-jt op-jn))
	  (comp-peep-del-0)
	  (setq keep-going t))

	 ;; <const>; <cond. jump> X; --> whatever
	 ((and (memq (car insn0) comp-constant-insns)
	       (memq (car insn1) comp-conditional-jmp-insns))
	  (let*
	      ;; only way to get a nil constant is through op-nil
	      ((is-nil (eq (car insn0) op-nil))
	       (is-t (not is-nil)))
	    (cond ((or (and is-nil (eq (car insn1) op-jn))
		       (and is-t (eq (car insn1) op-jt))
		       (and is-nil (eq (car insn1) op-jpn))
		       (and is-t (eq (car insn1) op-jpt)))
		   ;; nil; jn X --> jmp X
		   ;; t; jt X --> jmp X
		   ;; nil; jpn X --> jmp X
		   ;; t; jpt X --> jmp X
		   (rplaca insn1 op-jmp)
		   (comp-peep-del-0))
		  ((or (and is-nil (eq (car insn1) op-jt))
		       (and is-t (eq (car insn1) op-jn))
		       (and is-t (eq (car insn1) op-jnp))
		       (and is-nil (eq (car insn1) op-jtp)))
		   ;; nil; jt X --> <deleted>
		   ;; t; jn X --> <deleted>
		   ;; t; jnp X --> <deleted>
		   ;; nil; jtp X --> <deleted>
		   (comp-peep-del-0-1))
		  ((or (and is-nil (eq (car insn1) op-jnp))
		       (and is-t (eq (car insn1) op-jtp)))
		   ;; nil; jnp X --> nil; jmp X
		   ;; t; jpt X --> t; jmp X
		   (rplaca insn1 op-jmp))
		  ((or (and is-t (eq (car insn1) op-jpn))
		       (and is-nil (eq (car insn1) op-jpt)))
		   ;; t; jpn X --> t
		   ;; nil; jpt X --> nil
		   (comp-peep-del-1))
		  (t
		   (error "Unhandled contional jump case")))
	    (setq keep-going t)))

	 ;; <varref-and-error-free-op>; unbind ---> unbind; op
	 ;; [ I'm not sure if this helps at all..? ]
	 ((and (eq (car insn1) op-unbind)
	       (memq (car insn0) comp-varref-free-insns))
	  (let
	      ((op (car insn0))
	       (arg (cdr insn0)))
	    (rplaca insn0 (car insn1))
	    (rplacd insn0 (cdr insn1))
	    (rplaca insn1 op)
	    (rplacd insn1 arg)
	    (setq keep-going t)))

	 ;; bind X; unbind --> unbind
	 ((and (eq (car insn0) op-bind)
	       (eq (car insn1) op-unbind))
	  (comp-peep-del-0)
	  (setq keep-going t))

	 ;; refq X; dup... ; refq X --> refq X; dup...; dup
	 ((and (eq (car insn0) op-refq)
	       (eq (car insn1) op-dup))
	  (let
	      ((tem (nthcdr 2 point)))
	    (while (eq (car (car tem)) op-dup)
	      (setq tem (cdr tem)))
	    (when (and (eq (car (car tem)) op-refq)
		       (eq (cdr insn0) (cdr (car tem))))
	      (rplaca (car tem) op-dup)
	      (rplacd (car tem) nil)
	      (setq keep-going t))))

	 ;; X: Y: --> X:  [s/X/Y/]
	 ((and (eq (car insn0) 'label)
	       (eq (car insn1) 'label))
	  (while (setq tem (rassq insn1 code-string))
	    (rplacd tem insn0))
	  (comp-peep-del-1)
	  (setq keep-going t))

	 ;; [unused] X: --> deleted
	 ((and (eq (car insn0) 'label)
	       (not (rassq insn0 code-string)))
	  (comp-peep-del-0)
	  (setq keep-going t))

	 ;; jmp X; ... Y: --> jmp X; Y:
	 ((and (eq (car insn0) op-jmp)
	       (not (eq (car insn1) 'label)))
	  (setq tem (nthcdr 2 point))
	  (while (and tem (not (eq (car (car tem)) 'label)))
	    (setq tem (cdr tem)))
	  (when (eq (cdr insn0) (car tem))
	    (rplacd (cdr point) tem)
	    (comp-peep-refill)
	    (setq keep-going t)))

	 ;; j* X; ... X: jmp Y --> j* Y; ... X: jmp Y
	 ((and (memq (car insn0) comp-jmp-insns)
	       (setq tem (or (memq (cdr insn0) (cdr code-string))
			     (error "Can't find jump destination")))
	       (setq tem (car (cdr tem)))
	       (eq (car tem) op-jmp))
	  (rplacd insn0 (cdr tem))
	  (setq keep-going t))

	 ;; {jnp,jtp} X; ... X: <cond. jmp> Y --> whatever
	 ((and (or (eq (car insn0) op-jnp)
		   (eq (car insn0) op-jtp))
	       (setq tem (cdr (or (memq (cdr insn0) (cdr code-string))
				  (error "Can't find jump destination"))))
	       (car tem)
	       (memq (car (car tem)) comp-conditional-jmp-insns))
	  (let
	      ((jmp (car tem))
	       need-new-label)
	    (if (eq (car insn0) op-jtp)
		(cond
		 ((or (eq (car jmp) op-jpt)
		      (eq (car jmp) op-jt))
		  ;; jtp X; ... X: jpt Y --> jt Y; ...
		  ;; jtp X; ... X: jt Y --> jt Y; ...
		  (rplaca insn0 op-jt))
		 ((eq (car jmp) op-jpn)
		  ;; jtp X; ... X: jpn Y --> jpt Z; ... X: jpn Y; Z:
		  (rplaca insn0 op-jpt)
		  (setq need-new-label t))
		 ((or (eq (car jmp) op-jn)
		      (eq (car jmp) op-jnp))
		  ;; jtp X; ... X: jn Y --> jt Z; ... X: jpn Y; Z:
		  ;; jtp X; ... X: jnp Y --> jt Z; ... X: jpn Y; Z:
		  (rplaca insn0 op-jt)
		  (setq need-new-label t))
		 ((eq (car jmp) op-jtp)
		  ;; jtp X; ... X: jtp Y --> jtp Y; ...
		  (rplaca insn0 op-jtp)))
	      (cond
	       ((eq (car jmp) op-jpt)
		;; jnp X; ... X: jpt Y --> jn Z; ... X: jpt Y; Z:
		(rplaca insn0 op-jnp)
		(setq need-new-label t))
	       ((or (eq (car jmp) op-jpn)
		    (eq (car jmp) op-jn))
		;; jnp X; ... X: jpn Y --> jn Y ...
		;; jnp X; ... X: jn Y --> jn Y ...
		(rplaca insn0 op-jn))
	       ((or (eq (car jmp) op-jt)
		    (eq (car jmp) op-jtp))
		;; jnp X; ... X: jt Y --> jn Z; ... X: jt Y; Z:
		;; jnp X; ... X: jtp Y --> jn Z; ... X: jt Y; Z:
		(rplaca insn0 op-jn)
		(setq need-new-label t))
	       ((eq (car jmp) op-jnp)
		;; jnp X; ... X: jnp Y --> jnp Y ...
		(rplaca insn0 op-jnp))))
	    (if (not need-new-label)
		(rplacd insn0 (cdr jmp))
	      ;; add label `Z:' following the second jump
	      (let
		  ((label (cons (comp-make-label) (cdr tem))))
		(rplacd insn0 (car label))
		(rplacd tem label)))
	    (setq keep-going t)))

	 ;; <const>; jmp X; ... X: <cond. jmp> Y --> whatever
	 ;;
	 ;; [ this should be handled already, by (1) changing the
	 ;;   first jump, then by (2) dereferencing the constant ]

	 ;; jmp X: Y: ... X: <cond. jmp> Y --> ???
	    
	 )
	;; shift in the next instruction
	(comp-peep-shift)))

    ;; now do one last pass, looking for simple things
    (setq point code-string)
    (comp-peep-refill)
    (while insn0
      (cond
       ;; <const> X; {setq,bind} Y; <const X>
       ;;   --> <const X>; dup; {setq,bind} Y
       ((and (or (eq (car insn1) op-setq)
		 (eq (car insn1) op-bind))
	     (memq (car insn0) comp-constant-insns)
	     (eq insn0 insn2))
	(rplaca insn2 (car insn1))
	(rplacd insn2 (cdr insn1))
	(rplaca insn1 op-dup)
	(rplacd insn1 nil)
	(setq extra-stack 1)
	(setq keep-going t))

       ;; <const> X; {dup,<const> X}... --> <const> X; dup...
       ;; refq X; {dup,refq X}... --> refq X; dup...
       ((or (memq (car insn0) comp-constant-insns)
	    (eq (car insn0) op-refq))
	(setq tem (nthcdr 2 point))
	(while (or (eq (car (car tem)) op-dup)
		   (equal (car tem) insn0))
	  (rplaca (car tem) op-dup)
	  (rplacd (car tem) nil)
	  (setq tem (cdr tem)))))
      (comp-peep-shift))

    (setq comp-max-stack (+ comp-max-stack extra-stack))
    ;; drop the extra cons we added
    (cdr code-string)))


;; Optimisation of the constant vector

;; All this does is to delete any unused constants, and reorders the
;; indices such that the most commonly used values get the smallest
;; indices. This will probably decrease the overall code size (using
;; 1-byte instructions instead of 2-byte, or 2 instead of 3)

;; modifies the comp-constant-alist variable, returns the new code string
(defun comp-optimise-constants (code-string)
  (let
      ((comp-constant-usage (make-vector comp-constant-index 0)))
    ;; first count how many times each constant is used
    (mapc (lambda (insn)
	    (when (memq (car insn) comp-insns-with-constants)
	      (aset comp-constant-usage (cdr insn)
		    (1+ (aref comp-constant-usage (cdr insn))))))
	  code-string)
    ;; now sort by usage, minimum to maximum
    (setq comp-constant-alist
	  (sort comp-constant-alist
		(lambda (x y)
		  (< (aref comp-constant-usage (cdr x))
		     (aref comp-constant-usage (cdr y))))))
    ;; delete any unused constants at the head of the list
    (while (and comp-constant-alist
		(zerop (aref comp-constant-usage
			     (cdr (car comp-constant-alist)))))
      (setq comp-constant-alist (cdr comp-constant-alist)))
    ;; reverse the list to get most-used-first
    (setq comp-constant-alist (nreverse comp-constant-alist))
    ;; then assign new indices, based on current list position
    ;; reuse comp-constant-usage to map from old to new positions
    (let
	((i 0))
      (mapc (lambda (c)
	      (aset comp-constant-usage (cdr c) i)
	      (rplacd c i)
	      (setq i (1+ i))) comp-constant-alist))
    ;; now update the code string
    (mapc (lambda (insn)
	    (when (memq (car insn) comp-insns-with-constants)
	      (rplacd insn (aref comp-constant-usage (cdr insn)))))
	  code-string)
    code-string))