diff options
Diffstat (limited to 'src/cljcc')
| -rw-r--r-- | src/cljcc/compiler.clj | 11 | ||||
| -rw-r--r-- | src/cljcc/driver.clj | 9 | ||||
| -rw-r--r-- | src/cljcc/emit.clj | 136 |
3 files changed, 117 insertions, 39 deletions
diff --git a/src/cljcc/compiler.clj b/src/cljcc/compiler.clj index b4232e8..028cfbe 100644 --- a/src/cljcc/compiler.clj +++ b/src/cljcc/compiler.clj @@ -89,12 +89,6 @@ {"current" 0} pseudo-values)) -(create-pseudo-value-map - (find-pseudo-values - (->> (:instructions (nth tacky-ex 4)) - (map tacky-inst->assembly-inst) - (flatten)))) - (defn- pseudo->stack-operand-instruction [pvs instruction] (let [pseudo? (fn [inst path] (let [v (get-in inst [path :operand])] @@ -112,11 +106,6 @@ (replace-pseudo :dst) (replace-pseudo :operand)))) -(pseudo->stack-operand-instruction - {"tmp.1" 0} - {:op :mov - :src {:operand :pseudo :identifier "tmp.1"}}) - (defn- replace-pseudoregisters [instructions] (let [pseudo-values (find-pseudo-values instructions) pseudo-value-map (create-pseudo-value-map pseudo-values)] diff --git a/src/cljcc/driver.clj b/src/cljcc/driver.clj index 80e4096..b039ba4 100644 --- a/src/cljcc/driver.clj +++ b/src/cljcc/driver.clj @@ -3,6 +3,7 @@ [clojure.java.io :as io] [cljcc.compiler :as c] [cljcc.tacky :as t] + [cljcc.emit :as e] [clojure.pprint :as pp] [cljcc.log :as log] [cljcc.util :refer [get-os handle-sh mac-aarch64? make-file-name]] @@ -60,10 +61,12 @@ (let [preprocessed-file-path (make-file-name directory (remove-extension filename) "i") file (io/file preprocessed-file-path) source (slurp file) - assembled-source (c/run-compile source) + assembly-ast (c/generate-assembly source) + assembly-output (e/emit assembly-ast) out-file-path (make-file-name directory (remove-extension filename) "s")] - (spit out-file-path assembled-source) - (log/info (str "Succesfully generated assembly file.\n" assembled-source)))) + (spit out-file-path assembly-output) + (log/info (str "Succesfully generated assembly ast.\n" assembly-ast)) + (log/info (str "Succesfully generated assembly file.\n" assembly-output)))) (defn cleanup-step [directory filename] (let [file-without-ext (remove-extension filename)] diff --git a/src/cljcc/emit.clj b/src/cljcc/emit.clj index 3cd4f00..f405119 100644 --- a/src/cljcc/emit.clj +++ b/src/cljcc/emit.clj @@ -1,46 +1,132 @@ (ns cljcc.emit (:require [cljcc.parser :as p] [cljcc.util :refer [get-os]] - [clojure.string :as str])) + [cljcc.compiler :as c] + [clojure.string :as str] + [clojure.pprint :as pp])) -(defn handle-function-name [name] +(defn- imm-opernad-emit [operand] + (format "$%d" (:value operand))) + +(defn- stack-operand-emit [operand] + (format "%d(%%rbp)" (:value operand))) + +(defn- register-operand [operand] + (condp = (:register operand) + :ax "%eax" + :r10 "%r10d" + (throw (AssertionError. (str "Invalid register operand: " operand))))) + +(def operand-emitters + "Map of assembly operands to operand emitters." + {:imm #'imm-opernad-emit + :reg #'register-operand + :stack #'stack-operand-emit}) + +(defn- operand-emit [operand] + (if-let [[_ operand-emit-fn] (find operand-emitters (:operand operand))] + (operand-emit-fn operand) + (throw (AssertionError. (str "Invalid operand: " operand))))) + +(defn- mov-instruction-emit [instruction] + (let [src (operand-emit (:src instruction)) + dst (operand-emit (:dst instruction))] + [(format " %s %s, %s" "movl" src dst)])) + +(defn- ret-instruction-emit [_instruction] + [" movq %rbp, %rsp" + " popq %rbp" + " ret"]) + +(defn- unary-instruction-emit [instruction] + (let [operand (operand-emit (:operand instruction)) + assembly-operator (condp = (:unary-operator instruction) + :complement "notl" + :negate "negl" + (throw (AssertionError. (str "Invalid unary operator." instruction))))] + [(format " %s %s" assembly-operator operand)])) + +(defn- allocate-stack-instruction-emit [instruction] + [(format " subq $%d, %%rsp" (:value instruction))]) + +(def instruction-emitters + "Map of assembly instructions to function emitters." + {:mov #'mov-instruction-emit + :ret #'ret-instruction-emit + :unary #'unary-instruction-emit + :allocate-stack #'allocate-stack-instruction-emit}) + +(defn instruction-emit [instruction] + (if-let [[_ instruction-emit-fn] (find instruction-emitters (:op instruction))] + (instruction-emit-fn instruction) + (throw (AssertionError. (str "Invalid instruction: " instruction))))) + +(defn- handle-function-name [name] (if (= :mac (get-os)) (str "_" name) name)) -(defn emit-instruction - ([inst] - (str " " (symbol inst))) - ([inst src dst] - (str " " (symbol inst) " " "$" src ", %" (symbol dst)))) +(defn function-emit [f] + (let [name (handle-function-name (:identifier f)) + globl (format " .globl %s", name) + name-line (format "%s:" name) + instructions (map instruction-emit (:instructions f))] + (flatten [globl + name-line + " pushq %rbp" + " movq %rsp, %rbp" + instructions]))) -(defn statement-fn [stmt] - (condp = (:op stmt) - :ret (emit-instruction :ret) - :movl (emit-instruction (:op stmt) (:src stmt) (:dst stmt)))) +(def emitters-top-level + "Map of assembly top level constructs to their emitters." + {:function #'function-emit}) -(defn emit-function-assembly [fn-ast] - (let [name (handle-function-name (:identifier fn-ast)) - globl-line (str " .globl " name) - fn-start-line (str name ":") - body-statements (map statement-fn (:body fn-ast))] - (flatten [globl-line fn-start-line body-statements]))) +(defn emit-top-level [assembly-ast] + (if-let [[_ emit-fn] (find emitters-top-level (:op assembly-ast))] + (emit-fn assembly-ast) + (throw (AssertionError. (str "Invalid ast: " assembly-ast))))) (def linux-assembly-end ".section .note.GNU-stack,\"\",@progbits") -(defn emit-assembly [ast] - (let [fn-assembly (emit-function-assembly (first ast))] - (if (= :linux (get-os)) - (concat fn-assembly [linux-assembly-end]) - fn-assembly))) - -(defn join-assembly [assembly-lines] - (str/join "\n" assembly-lines)) +(defn emit [ast] + (let [handle-os (fn [ast] + (if (= :linux (get-os)) + (conj (vec ast) linux-assembly-end) + ast))] + (->> ast + (map emit-top-level) + concat + flatten + handle-os + (str/join "\n")))) (comment (def ex "int main(void) {return 2;}") + (mov-instruction-emit + {:op :mov + :src {:operand :imm :value 1} + :dst {:operand :stack :value -4}}) + + (c/generate-assembly + "int main(void) { + return ~(-(~(-1))); + }") + + (pp/pprint + (c/generate-assembly + "int main(void) { + return ~(-(~(-1))); + }")) + + (println + (emit + (c/generate-assembly + "int main(void) { + return -(((((10))))); + }"))) + (-> ex p/parse) |
