From e0de6ddf16a37435966e31a73b4425a0300dc1e6 Mon Sep 17 00:00:00 2001 From: Shagun Agrawal Date: Sat, 3 Aug 2024 02:45:43 +0530 Subject: Add tacky IR stage Add Tacky IR stage General formatting etc --- src/cljcc/cljcc.clj | 1 + src/cljcc/compiler.clj | 40 ++++++++--------- src/cljcc/driver.clj | 36 +++++++++------ src/cljcc/parser.clj | 19 ++++---- src/cljcc/tacky.clj | 118 +++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 168 insertions(+), 46 deletions(-) create mode 100644 src/cljcc/tacky.clj (limited to 'src') diff --git a/src/cljcc/cljcc.clj b/src/cljcc/cljcc.clj index 8c8211d..0ebe609 100644 --- a/src/cljcc/cljcc.clj +++ b/src/cljcc/cljcc.clj @@ -19,6 +19,7 @@ (def cli-options [[nil "--parse" "Runs parser. Does not emit any files."] [nil "--codegen" "Runs compiler. Does not emit any files."] + [nil "--tacky" "Runs tacky generation. Does not emit any files."] ["-h" "--help"]]) (defn validate-args [args] diff --git a/src/cljcc/compiler.clj b/src/cljcc/compiler.clj index 9e0a9a4..eececdd 100644 --- a/src/cljcc/compiler.clj +++ b/src/cljcc/compiler.clj @@ -12,17 +12,17 @@ :body body}) (defn ast->compile [ast] - (insta/transform - {:function transform-function - :identifier str - :constant (comp edn/read-string str) - :exp (fn [v] - {:op :movl - :src v - :dst :eax}) + (insta/transform + {:function transform-function + :identifier str + :constant (comp edn/read-string str) + :exp (fn [v] + {:op :movl + :src v + :dst :eax}) :statement (fn [_ v] [v {:op :ret}])} - ast)) + ast)) (defn handle-function-name [name] (if (= :mac (get-os)) @@ -37,8 +37,8 @@ (defn statement-fn [stmt] (condp = (:op stmt) - :ret (emit-instruction :ret) - :movl (emit-instruction (:op stmt) (:src stmt) (:dst stmt)))) + :ret (emit-instruction :ret) + :movl (emit-instruction (:op stmt) (:src stmt) (:dst stmt)))) (statement-fn {:op :movl :src 1 :dst :eax}) @@ -71,19 +71,17 @@ (comment - (def ex "int main(void) {return 2;}") + (def ex "int main(void) {return 2;}") - (-> ex + (-> ex p/parse) - (-> ex + (-> ex p/parse ast->compile) - (-> ex - p/parse - ast->compile - il->assembly - join-assembly) - - ,) + (-> ex + p/parse + ast->compile + il->assembly + join-assembly)) diff --git a/src/cljcc/driver.clj b/src/cljcc/driver.clj index e3c4822..80e4096 100644 --- a/src/cljcc/driver.clj +++ b/src/cljcc/driver.clj @@ -1,10 +1,12 @@ (ns cljcc.driver (:require - [clojure.java.io :as io] - [cljcc.compiler :as c] - [cljcc.log :as log] - [cljcc.util :refer [get-os handle-sh mac-aarch64? make-file-name]] - [cljcc.parser :as p])) + [clojure.java.io :as io] + [cljcc.compiler :as c] + [cljcc.tacky :as t] + [clojure.pprint :as pp] + [cljcc.log :as log] + [cljcc.util :refer [get-os handle-sh mac-aarch64? make-file-name]] + [cljcc.parser :as p])) (defn validate-os [] (let [os (get-os)] @@ -45,6 +47,15 @@ (log/info "Input file is succesfully parsed.") (throw (Exception. "Failed during parsing"))))) +(defn tacky-step [directory filename] + (let [preprocessed-file-path (make-file-name directory (remove-extension filename) "i") + file (io/file preprocessed-file-path) + source (slurp file) + output (t/tacky-generate (p/parse source))] + (log/info (str + "Successfully generated Tacky IR.\n" + (with-out-str (pp/pprint output)))))) + (defn compiler-step [directory filename] (let [preprocessed-file-path (make-file-name directory (remove-extension filename) "i") file (io/file preprocessed-file-path) @@ -65,14 +76,13 @@ parser-step-fn (partial parser-step directory filename) compiler-step-fn (partial compiler-step directory filename) assemble-step-fn (partial assemble-step directory filename) - cleanup-step-fn (partial cleanup-step directory filename)] + tacky-step-fn (partial tacky-step directory filename)] (cond - (:parse options) (concat base-steps - [parser-step-fn cleanup-step-fn]) - (:codegen options) (concat base-steps - [parser-step-fn compiler-step-fn cleanup-step-fn]) + (:parse options) (concat base-steps [parser-step-fn]) + (:tacky options) (concat base-steps [parser-step-fn tacky-step-fn]) + (:codegen options) (concat base-steps [parser-step-fn tacky-step-fn compiler-step-fn]) :else (concat base-steps - [parser-step-fn compiler-step-fn assemble-step-fn cleanup-step-fn])))) + [parser-step-fn tacky-step-fn compiler-step-fn assemble-step-fn])))) (defn run-steps [options directory filename] (let [steps (create-steps options directory filename)] @@ -91,6 +101,4 @@ (comment - (run "./test-programs/ex1.c" {}) - - ,) + (run "./test-programs/ex1.c" {})) diff --git a/src/cljcc/parser.clj b/src/cljcc/parser.clj index d19ddd1..8852d91 100644 --- a/src/cljcc/parser.clj +++ b/src/cljcc/parser.clj @@ -11,10 +11,11 @@ (def c-parser (insta/parser " = function+ - function = #'int\\b' identifier <'('> #'void\\b' <')'> <'{'> statement+ <'}'> + function = #'int\\b' identifier <'('> #'void\\b' <')'> <'{'> statement <'}'> statement = #'return\\b' exp <';'> exp = exp-prime - = constant | unop exp-prime | <'('> exp-prime <')'> + = constant | unop-exp | <'('> exp-prime <')'> + unop-exp = unop exp unop = #'-' | #'~' identifier = #'[a-zA-Z_]\\w*\\b' constant = #'[0-9]+\\b' @@ -29,21 +30,17 @@ (comment - (parse "int main(void) {return 2;}") + (parse "int main(void) {return 2;}") - (parse " + (parse " int main(void) { return 2; - return 2; }") - (parse "int main(void) { + (parse "int main(void) { return -(((((10))))); }") - (parse "int main(void) { - return --2; + (parse "int main(void) { return -(((((10))))); - }") - - ,) + }")) diff --git a/src/cljcc/tacky.clj b/src/cljcc/tacky.clj new file mode 100644 index 0000000..e25c87f --- /dev/null +++ b/src/cljcc/tacky.clj @@ -0,0 +1,118 @@ +(ns cljcc.tacky + (:require + [clojure.pprint :as pp] + [instaparse.core :as insta] + [cljcc.parser :as p])) + +(def counter "Global integer counter for generating unique identifier names." (atom 0)) + +(defn create-identifier + "Returns a unique identifier. Used for generating tacky variable names." + ([] + (create-identifier "tmp")) + ([identifier] + (let [n @counter + _ (swap! counter #(+ % 1))] + (str identifier "." n)))) + +(defn variable + ([] + {:type :variable + :value (create-identifier)}) + ([^String identifier] + {:type :variable + :value (create-identifier identifier)})) + +(defn constant [^String v] + {:type :constant + :value (Long. v)}) + +(defn unop-operator [^String unop] + (condp = unop + "~" :complement + "-" :negate)) + +(defn unary-instruction [unop src dst] + {:type :unary + :unary-operator unop + :dst dst + :src src}) + +(defn return-instruction [val] + {:type :return + :val val}) + +(defn exp-instructions [exp] + (when-let [expr (second exp)] + (condp = (first expr) + :constant {:val (constant (second expr))} + :unop-exp (let [inner (exp-instructions (nth expr 2)) + dst (variable) + src (:val inner) + unop (unop-operator (second (second expr))) + inst (unary-instruction unop src dst)] + {:val dst + :instructions (conj (:instructions inner) inst)})))) + +(defn ret-instructions [exp] + (let [e (exp-instructions exp) + val (:val e) + instructions (:instructions e)] + (conj instructions (return-instruction val)))) + +(defn statement-transform [_ret-keyword exp] + (reverse (ret-instructions exp))) + +(defn tacky-generate [ast] + (insta/transform {:statement statement-transform} ast)) + +(comment + + (reset! counter 0) + + (pp/pprint (tacky-generate (p/parse "int main(void) {return -~-8;}"))) + + (pp/pprint + (exp-instructions [:exp [:constant "2"]])) + + (pp/pprint + (exp-instructions [:exp [:constant "2"]])) + + (pp/pprint + (exp-instructions [:exp [:unop-exp [:unop "-"] [:exp [:constant "2"]]]])) + + (def ex-exp + [:exp + [:unop-exp + [:unop "-"] + [:exp + [:unop-exp + [:unop "~"] + [:exp [:unop-exp [:unop "-"] [:exp [:constant "8"]]]]]]]]) + + (def ex-ret + [:statement "return" + [:exp + [:unop-exp + [:unop "-"] + [:exp + [:unop-exp + [:unop "~"] + [:exp [:unop-exp [:unop "-"] [:exp [:constant "8"]]]]]]]]]) + + (pp/pprint + (exp-instructions ex-exp)) + + (pp/pprint + (ret-instructions ex-ret)) + + (def exprg + "int main(void) { + return -(~(-8)); + }") + + (pp/pprint (parse "int main(void) {return 2;}")) + + (pp/pprint (parse exprg)) + + ,) -- cgit v1.2.3