Selaa lähdekoodia

Added compile-to-c and optimization options and added a performance comparison script

Taddeus Kroes 11 vuotta sitten
vanhempi
sitoutus
7583673377
4 muutettua tiedostoa jossa 163 lisäystä ja 5 poistoa
  1. 4 0
      .gitignore
  2. 21 4
      Makefile
  3. 107 1
      bf.ml
  4. 31 0
      compare.sh

+ 4 - 0
.gitignore

@@ -7,3 +7,7 @@ hello
 *.bc
 *.o
 *.swp
+*.c
+*-c
+*-nayuki
+*.dump

+ 21 - 4
Makefile

@@ -1,17 +1,28 @@
 LD := gcc
+CC := clang
+CFLAGS := -O3
 LDFLAGS := -nostartfiles
+BFFLAGS :=
 BFILES := $(patsubst %.b,%,$(wildcard *.b))
 
 .PHONY: check clean
-.PRECIOUS: $(addsuffix .ll,$(BFILES)) $(addsuffix -opt.ll,$(BFILES))
+.PRECIOUS: $(addsuffix .ll,$(BFILES)) $(addsuffix -opt.ll,$(BFILES)) \
+	$(addsuffix -c.c,$(BFILES)) $(addsuffix -nayuki.c,$(BFILES))
 
 bf: bf.ml
-	ocamlopt -o $@ -g -I /usr/lib/ocaml/llvm-3.4 llvm.cmxa $<
+	ocamlopt -o $@ -g -I /usr/lib/ocaml/llvm-3.4 llvm.cmxa str.cmxa $<
 	rm -f $@.cmi $@.cmx $@.o
 
+%-nayuki: LDFLAGS=
 %: %.o
 	$(LD) -o $@ $< $(LDFLAGS)
 
+%.o: %.c
+	$(CC) $(CFLAGS) -o $@ -c $<
+
+%-nayuki.c: %.b
+	python bfc.py $< $@
+
 %.o: %.ll
 	llc -filetype obj -o $@ $<
 
@@ -22,10 +33,16 @@ bf: bf.ml
 	opt -O3 -S -o $@ $<
 
 %.ll: %.b bf
-	./bf < $< > $@
+	./bf $(BFFLAGS) < $< > $@
+
+%-c.c: %.b bf
+	./bf $(BFFLAGS) -c < $< > $@
+
+%.dump: %
+	objdump -d -M intel $< > $@
 
 check: hello-opt
 	./$<
 
 clean:
-	rm -f bf *.cmi *.cmx *.ll *.bc *.o $(BFILES) $(addsuffix -opt,$(BFILES))
+	rm -f bf *.cmi *.cmx *.ll *.bc *.o *.c *-opt *-c *-nayuki $(BFILES) *.dump

+ 107 - 1
bf.ml

@@ -6,6 +6,11 @@ and command =
   | Incdata | Decdata
   | Output | Input
   | Loop of program
+  | Addptr of int
+  | Adddata of int
+  | Setptr of int
+  | Setdata of int
+
 
 let read_program ic =
   let rec next cur stack =
@@ -27,6 +32,28 @@ let read_program ic =
   in
   next [] []
 
+let rec string_of_program program =
+  let rec cat buf = function
+    | [] -> buf
+    | cmd :: tl -> cat (buf ^ string_of_command cmd) tl
+  in
+  cat "" program
+and string_of_command = function
+  | Incptr  -> ">"
+  | Decptr  -> "<"
+  | Incdata -> "+"
+  | Decdata -> "-"
+  | Output  -> "."
+  | Input   -> ","
+  | Loop p  -> "[" ^ string_of_program p ^ "]"
+
+  | Addptr n when n < 0  -> "(<" ^ string_of_int (-n) ^ ")"
+  | Addptr n             -> "(>" ^ string_of_int n ^ ")"
+  | Adddata n when n < 0 -> "(" ^ string_of_int n ^ ")"
+  | Adddata n            -> "(+" ^ string_of_int n ^ ")"
+  | Setptr n             -> "(<>" ^ string_of_int n ^ ")"
+  | Setdata n            -> "(=" ^ string_of_int n ^ ")"
+
 let compile memsize program =
   let ctx = global_context () in
   let m = create_module ctx "brainfucker" in
@@ -91,6 +118,15 @@ let compile memsize program =
       build_br bb_cond b |> ignore;
 
       set_cur_bb bb_end
+    | Addptr n ->
+      build_add (load idx) (i32 n) "" b |> store idx
+    | Adddata n ->
+      build_add (load (gep ())) (i8 n) "" b |> store (gep ())
+    | Setptr n when n >= 0 ->
+      store idx (i32 n)
+    | Setdata n when n >= 0 ->
+      store (gep ()) (i8 n)
+    | cmd -> failwith ("invalid command: " ^ string_of_command cmd)
   in
 
   (* zero-initialize memory (use intrinsic for optimization assumptions) *)
@@ -111,5 +147,75 @@ let compile memsize program =
   build_ret_void b |> ignore;
   m
 
+let compile_to_c memsize program =
+  let indent = Str.global_replace (Str.regexp "^\\(.\\)") "    \\1" in
+  let rec compile_commands buf = function
+    | [] -> buf
+    | cmd :: tl -> compile_commands (buf ^ compile_command cmd ^ "\n") tl
+  and compile_command = function
+    | Incptr    -> "idx++;"
+    | Decptr    -> "idx--;"
+    | Incdata   -> "mem[idx]++;"
+    | Decdata   -> "mem[idx]--;"
+    | Output    -> "putchar(mem[idx]);"
+    | Input     -> "mem[idx] = getchar();"
+    | Loop p    -> "while (mem[idx] != 0) {\n" ^ indent (compile_commands "" p) ^ "}"
+    | Addptr n  -> "idx += " ^ string_of_int n ^ ";"
+    | Adddata n -> "mem[idx] += " ^ string_of_int n ^ ";"
+    | Setptr n  -> "idx = " ^ string_of_int n ^ ";"
+    | Setdata n -> "mem[idx] = " ^ string_of_int n ^ ";"
+  in
+  "#include <stdio.h>\n" ^
+  "#include <stdlib.h>\n" ^
+  "void _start() {\n" ^
+  "    unsigned char mem[" ^ string_of_int memsize ^ "] = {};\n" ^
+  "    unsigned idx = 0;\n" ^
+       indent (compile_commands "" program) ^
+  "    exit(0);\n" ^
+  "}\n"
+
+let rec optimize program =
+  let rec opt = function
+    | Incptr :: tl -> opt (Addptr 1 :: tl)
+    | Decptr :: tl -> opt (Addptr (-1) :: tl)
+    | Incdata :: tl -> opt (Adddata 1 :: tl)
+    | Decdata :: tl -> opt (Adddata (-1) :: tl)
+
+    | Addptr a :: Addptr b :: tl -> opt (Addptr (a + b) :: tl)
+    | Adddata a :: Adddata b :: tl -> opt (Adddata (a + b) :: tl)
+
+    | Loop [Addptr -1] :: tl -> opt (Setptr 0 :: tl)
+    | Loop [Adddata (1 | -1)] :: tl -> opt (Setdata 0 :: tl)
+
+    | (Addptr 0 | Adddata 0) :: tl
+    | (Addptr _ | Setptr _) :: (Setptr _ :: _ as tl)
+    | (Adddata _ | Setdata _) :: (Setdata _ :: _ as tl) -> opt tl
+
+    | Loop p :: tl -> Loop (optimize p) :: opt tl
+    | hd :: tl -> hd :: opt tl
+    | [] -> []
+  in
+  match opt program with
+  | p when p <> program -> optimize p
+  | p -> p
+
+let rec flatten = function
+  | Addptr 1 :: tl     -> Incptr :: flatten tl
+  | Addptr (-1) :: tl  -> Decptr :: flatten tl
+  | Adddata 1 :: tl    -> Incdata :: flatten tl
+  | Adddata (-1) :: tl -> Decdata :: flatten tl
+  | Loop p :: tl -> Loop (flatten p) :: flatten tl
+  | hd :: tl -> hd :: flatten tl
+  | [] -> []
+
 let () =
-  stdin |> read_program |> compile 30000 |> string_of_llmodule |> print_string
+  let args = List.tl (Array.to_list Sys.argv) in
+  stdin |> read_program
+  |> (if List.mem "-o" args then optimize else fun p -> p)
+  |> fun program ->
+    if List.mem "-e" args then
+      program |> flatten |> string_of_program |> print_endline
+    else if List.mem "-c" args then
+      program |> flatten |> compile_to_c 30000 |> print_string
+    else
+      program |> compile 30000 |> string_of_llmodule |> print_string

+ 31 - 0
compare.sh

@@ -0,0 +1,31 @@
+#!/usr/bin/env bash
+if [ $# -eq 1 ]
+then
+    basename=`echo $1 | sed 's/.b$//'`
+else
+    basename=_tmp
+    cat > ${basename}.b
+fi
+
+mytime () {
+    (`which time` -f %e $1 > /dev/null) 2>&1
+}
+set -e
+
+echo -n "compiling opt..."
+make -s $basename-opt
+echo done
+
+echo -n "compiling c..."
+make -s $basename-c
+echo done
+
+echo -n "compiling nayuki..."
+make -s $basename-nayuki
+echo done
+
+echo "opt:    `mytime ./$basename-opt`"
+echo "c:      `mytime ./$basename-c`"
+echo "nayuki: `mytime ./$basename-nayuki`"
+
+rm -f _tmp*