aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorShagun Agrawal <agrawalshagun07@gmail.com>2024-08-08 01:01:35 +0530
committerShagun Agrawal <agrawalshagun07@gmail.com>2024-08-08 01:01:35 +0530
commitb82a214687d0b0f95f98dbd5ba7fefd82012fb85 (patch)
treeeb6a604e6c983aa8b5d93b6ed822494e6ed4dad5 /src
parent31afbc6b7393ea3a3682dd617cdd860ff28e9a0a (diff)
Add code emission, pass chapter 2 tests
Add generic code emitters Fix some minor bugs
Diffstat (limited to 'src')
-rw-r--r--src/cljcc/compiler.clj11
-rw-r--r--src/cljcc/driver.clj9
-rw-r--r--src/cljcc/emit.clj136
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)