noprompt / meander

Tools for transparent data transformation
MIT License
923 stars 55 forks source link

Compile a sequence of :check-lit to a case #36

Closed noprompt closed 5 years ago

noprompt commented 5 years ago

We can improve the code generated around sequences of literal checks by compiling them to a case expression. Currently the form

(r.match/match 'F
  X '[F - ((X) + X) + F (+ F X) - X]
  F '[F F]
  ?X ?X)

compiles to something like

(let [target__10357 'F
      fail__10358 (fn []
                    (let [?X target__10357]
                      ?X))]
  (letfn [(state__10359 []
            (if (= target__10357 'X)
              '[F - ((X) + X) + F (+ F X) - X]
              (state__10360)))
          (state__10360 []
            (if (= target__10357 'F)
              '[F F]
              (fail__10358)))]
    (state__10359)))

but would be more efficient as

(let [target__10357 'F
      fail__10358 (fn []
                    (let [?X target__10357]
                      ?X))]
  (letfn [(state__10359 []
            (case target__10357
              X '[F - ((X) + X) + F (+ F X) - X]
              F '[F F]
              ;; else
              (fail__10358)))]
    (state__10359)))

and even better if we could

(let [target__10357 'F
      fail__10358 (fn []
                    (let [?X target__10357]
                      ?X))]
  (case target__10357
    X '[F - ((X) + X) + F (+ F X) - X]
    F '[F F]
    ;; else
    (fail__10358)))

The difference in overall time execution between each is roughly a millisecond or two. That may not sound like much but when you consider say, an L-System, where rewrite rules may need to be applied thousands of times, it can add up.

The IR for the code above looks something like

{:op :branch,
 :arms
 [{:value {:op :eval, :form 'X},
   :op :check-lit,
   :then {:value '[F - ((X) + X) + F (+ F X) - X], :op :return},
   :target {:op :eval, :form target}}
  {:value {:op :eval, :form 'F},
   :op :check-lit,
   :then {:value '[F F], :op :return},
   :target {:op :eval, :form target}}]}

When compiling the :branch we should be able to check for any subsequence of :check-lit ops of length two or more and compile them as case expressions. IR nodes to the right of the subsequence should be compiled as the "else" part of the case.