1 // A Small LLVM Compiler for a Simple Functional Language |
1 // A Small LLVM Compiler for a Simple Functional Language |
2 // (includes an external lexer and parser) |
2 // (includes an external lexer and parser) |
3 // |
3 // |
|
4 // |
4 // call with |
5 // call with |
5 // |
6 // |
|
7 // amm fun_llvm.sc main fact.fun |
|
8 // amm fun_llvm.sc main defs.fun |
|
9 // |
|
10 // or |
|
11 // |
6 // amm fun_llvm.sc write fact.fun |
12 // amm fun_llvm.sc write fact.fun |
7 // |
|
8 // amm fun_llvm.sc write defs.fun |
13 // amm fun_llvm.sc write defs.fun |
9 // |
14 // |
10 // this will generate a .ll file. Other options are compile and run. |
15 // this will generate an .ll file. |
11 // |
16 // |
12 // You can interpret an .ll file using lli. |
17 // or |
|
18 // |
|
19 // amm fun_llvm.sc run fact.fun |
|
20 // amm fun_llvm.sc run defs.fun |
|
21 // |
|
22 // |
|
23 // You can interpret an .ll file using lli, for example |
|
24 // |
|
25 // lli fact.ll |
13 // |
26 // |
14 // The optimiser can be invoked as |
27 // The optimiser can be invoked as |
15 // |
28 // |
16 // opt -O1 -S in_file.ll > out_file.ll |
29 // opt -O1 -S in_file.ll > out_file.ll |
17 // opt -O3 -S in_file.ll > out_file.ll |
30 // opt -O3 -S in_file.ll > out_file.ll |
18 // |
31 // |
19 // The code produced for the various architectures can be obtains with |
32 // The code produced for the various architectures can be obtain with |
20 // |
33 // |
21 // llc -march=x86 -filetype=asm in_file.ll -o - |
34 // llc -march=x86 -filetype=asm in_file.ll -o - |
22 // llc -march=arm -filetype=asm in_file.ll -o - |
35 // llc -march=arm -filetype=asm in_file.ll -o - |
23 // |
36 // |
24 // Producing an executable can be achieved by |
37 // Producing an executable can be achieved by |
148 case "*" => "mul i32 " |
160 case "*" => "mul i32 " |
149 case "-" => "sub i32 " |
161 case "-" => "sub i32 " |
150 case "/" => "sdiv i32 " |
162 case "/" => "sdiv i32 " |
151 case "%" => "srem i32 " |
163 case "%" => "srem i32 " |
152 case "==" => "icmp eq i32 " |
164 case "==" => "icmp eq i32 " |
153 case "<=" => "icmp sle i32 " // signed less or equal |
165 case "<=" => "icmp sle i32 " // signed less or equal |
154 case "<" => "icmp slt i32 " // signed less than |
166 case "<" => "icmp slt i32 " // signed less than |
155 } |
167 } |
156 |
168 |
|
169 // compile K values |
157 def compile_val(v: KVal) : String = v match { |
170 def compile_val(v: KVal) : String = v match { |
158 case KNum(i) => s"$i" |
171 case KNum(i) => s"$i" |
159 case KVar(s) => s"%$s" |
172 case KVar(s) => s"%$s" |
160 case Kop(op, x1, x2) => |
173 case Kop(op, x1, x2) => |
161 s"${compile_op(op)} ${compile_val(x1)}, ${compile_val(x2)}" |
174 s"${compile_op(op)} ${compile_val(x1)}, ${compile_val(x2)}" |
209 compile_exp(CPS(body)(_ => KReturn(KNum(0)))) ++ |
222 compile_exp(CPS(body)(_ => KReturn(KNum(0)))) ++ |
210 m"}\n" |
223 m"}\n" |
211 } |
224 } |
212 } |
225 } |
213 |
226 |
|
227 |
214 // main compiler functions |
228 // main compiler functions |
215 |
229 def compile(prog: List[Decl]) : String = |
216 def compile_prog(prog: List[Decl]) : String = |
|
217 prelude ++ (prog.map(compile_decl).mkString) |
230 prelude ++ (prog.map(compile_decl).mkString) |
218 |
231 |
219 |
232 |
|
233 import ammonite.ops._ |
|
234 |
|
235 |
220 @main |
236 @main |
221 def compile(fname: String) = { |
237 def main(fname: String) = { |
222 val path = os.pwd / fname |
238 val path = os.pwd / fname |
223 val file = fname.stripSuffix("." ++ path.ext) |
239 val file = fname.stripSuffix("." ++ path.ext) |
224 val tks = tokenise(os.read(path)) |
240 val tks = tokenise(os.read(path)) |
225 val ast = parse_tks(tks) |
241 val ast = parse_tks(tks) |
226 println(compile_prog(ast)) |
242 println(compile(ast)) |
227 } |
243 } |
228 |
244 |
229 @main |
245 @main |
230 def write(fname: String) = { |
246 def write(fname: String) = { |
231 val path = os.pwd / fname |
247 val path = os.pwd / fname |
232 val file = fname.stripSuffix("." ++ path.ext) |
248 val file = fname.stripSuffix("." ++ path.ext) |
233 val tks = tokenise(os.read(path)) |
249 val tks = tokenise(os.read(path)) |
234 val ast = parse_tks(tks) |
250 val ast = parse_tks(tks) |
235 val code = compile_prog(ast) |
251 val code = compile(ast) |
236 os.write.over(os.pwd / (file ++ ".ll"), code) |
252 os.write.over(os.pwd / (file ++ ".ll"), code) |
237 } |
253 } |
238 |
254 |
239 @main |
255 @main |
240 def run(fname: String) = { |
256 def run(fname: String) = { |
241 val path = os.pwd / fname |
257 val path = os.pwd / fname |
242 val file = fname.stripSuffix("." ++ path.ext) |
258 val file = fname.stripSuffix("." ++ path.ext) |
243 val tks = tokenise(os.read(path)) |
259 write(fname) |
244 val ast = parse_tks(tks) |
|
245 val code = compile_prog(ast) |
|
246 os.write.over(os.pwd / (file ++ ".ll"), code) |
|
247 os.proc("llc", "-filetype=obj", file ++ ".ll").call() |
260 os.proc("llc", "-filetype=obj", file ++ ".ll").call() |
248 os.proc("gcc", file ++ ".o", "-o", file).call() |
261 os.proc("gcc", file ++ ".o", "-o", file ++ ".bin").call() |
249 print(os.proc(os.pwd / file).call().out.string) |
262 os.proc(os.pwd / (file ++ ".bin")).call(stdout = os.Inherit) |
250 } |
263 println(s"done.") |
251 |
264 } |
252 |
265 |
253 |
266 |
254 |
267 |
255 |
268 |
|
269 |