making an optimizing compiler

the obvious

  • less code
    • to process
    • to run
    • to debug

the honest

  1. gee, vectors cause a lot of my bugs
  2. wow, functions cause a lot of my bugs

motivation

optimization pass overview

inline functions

reveal functions

minimize vectors

propagate let-bindings

simplify

convert to closures

repeat?

until

  • effort limit reached
  • ast and defs have not changed

inline functions

simple functions

complex functions

(define (sum3 [x_1 : Integer]
              [y_2 : Integer]
              [z_3 : Integer]) : Integer
    (+ x_1 (+ y_2 z_3)))


(define (zero? [x_4 : Integer]) : Boolean
    (eq? x_4 0))


(define (foo [x_5 : Integer]) : Integer
    (if (zero? x_5)
        42
        (sum3 x_5 x_5 x_5)))
(define (mult [x_1 : Integer]
              [y_2 : Integer]) : Integer
  (if (zero? x_1)
    0
    (+ y_2 (mult (sub1 x_1) y_2))))


(define (sq [x_3 : Integer]) : Integer
    (mult x_3 x_3))

inline functions

(define (foo [x_5 : Integer]) : Integer
    (if (zero? x_5) 42 (sum3 x_5 x_5 x_5)))

by generating lets

(define (foo [x_5 : Integer]) : Integer
    (if
        (let ([x_4 x_5]) (eq? x_4 0))
        42
        (let ([x_1 x_5])
            (let ([y_2 x_5])
                (let ([z_3 x_5])
                    (+ x_1 (+ y_2 z_3)))))))

minimize vectors

simple elements

(vector 40 (vector 42) 2 (mult 6 7))

complex elements

minimize vectors

vector-set! can make simple elements complex

(let ([v_1 (vector (vector 42) 2)])
    (let ([_2 (vector-set! v_1 1 (sq 3))])
        (let ([_3 (vector-set! v_1 0 42)])
            v_1)))

with a caveat...

and make complex elements simple

... from anywhere in the program

by replacing simple vector-refs

(let ([v_1 (vector (mult 6 7) 2)])
    (+ 40 2))

minimize vectors

(let ([v_1 (vector (mult 6 7) 2)])
    (let ([_3 (vector-set! v_1 0 40)])
        (+ (vector-ref v_1 0) (vector-ref v_1 1))))
(+ 40 2)

on subsequent runs

complex lets should not be propagated

(let ([v_0 (vector 42)])
    (let ([a_1 (mult 2 1)])
        (let ([x_2 40])
            (let ([y_3 (read)])
                (let ([z_4 (+ 1 0)])
                    (mult (+ x_2 a_1) z_4))))))

with a caveat...

propagate let-bindings

(let ([a_1 (mult 2 1)])
    (let ([y_3 (read)])
        (mult (+ 40 a_1) (+ 1 0))))

but can still be removed if never referenced and no side effects

simplify

...is the best pass

(not #f)

negations

simplify

#t
(not (not (eq? (read) 0)))
(eq? (read) 0)
(- (- (read)))
(read)
(- 6)
-6

and

simplify

(and #f (eq? (read) (mult 6 7)))
#f
(and #t #t)
#t
(+ 40 2)

operations

simplify

42
(>= 4 5)
#f
(eq? 0 0)
#t

if statements

simplify

(if #f (read) (mult 6 7))
(mult 6 7)
(if #t 42 777)
42
42

recursively

(if (and (eq? (+ 2 3) (- (+ 2 (- 3)))) (> (mult (read) 7) 42))
    (let ([v_1 (vector (mult 6 7))])
        (vector-ref v_1 0))
    (if (not (not (<= 2 (+ 20 20))))
        (+ 11 (- (- (+ 21 10))))
        (- (- (read)))))

simplify

all together now

results

questions?

More info about the compiler and language referenced can be found here:

https://homes.soic.indiana.edu/classes/fall2017/csci/p523-rrnewton/book.pdf

making an optimizing compiler

By Trevan Haskell

making an optimizing compiler

A short presentation on optimizing racket code during compilation. Based on this book: https://homes.soic.indiana.edu/classes/fall2017/csci/p523-rrnewton/book.pdf

  • 1,168