bf.ml 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. open Llvm
  2. type program = command list
  3. and command =
  4. | Incptr | Decptr
  5. | Incdata | Decdata
  6. | Output | Input
  7. | Loop of program
  8. let read_program ic =
  9. let rec next cur stack =
  10. try
  11. match input_char ic, stack with
  12. | '>', _ -> next (Incptr :: cur) stack
  13. | '<', _ -> next (Decptr :: cur) stack
  14. | '+', _ -> next (Incdata :: cur) stack
  15. | '-', _ -> next (Decdata :: cur) stack
  16. | '.', _ -> next (Output :: cur) stack
  17. | ',', _ -> next (Input :: cur) stack
  18. | '[', _ -> next [] (cur :: stack)
  19. | ']', [] -> failwith "unmatched ']'"
  20. | ']', (hd :: tl) -> next (Loop (List.rev cur) :: hd) tl
  21. | _ -> next cur stack
  22. with End_of_file ->
  23. if List.length stack > 0 then failwith "unmatched '['";
  24. List.rev cur
  25. in
  26. next [] []
  27. let rec string_of_program program =
  28. let string_of_command = function
  29. | Incptr -> ">"
  30. | Decptr -> "<"
  31. | Incdata -> "+"
  32. | Decdata -> "-"
  33. | Output -> "."
  34. | Input -> ","
  35. | Loop p -> "[" ^ string_of_program p ^ "]"
  36. in
  37. let rec cat buf = function
  38. | [] -> buf
  39. | cmd :: tl -> cat (buf ^ string_of_command cmd) tl
  40. in
  41. cat "" program
  42. let compile memsize program =
  43. let ctx = global_context () in
  44. let m = create_module ctx "brainfucker" in
  45. let byte_ty = i8_type ctx in
  46. let byteptr_ty = pointer_type byte_ty in
  47. let i1_ty = i1_type ctx in
  48. let i32_ty = i32_type ctx in
  49. let int_ty = i32_ty in
  50. let void_ty = void_type ctx in
  51. let putchar = declare_function "putchar" (function_type int_ty [|byte_ty|]) m in
  52. let getchar = declare_function "getchar" (function_type byte_ty [||]) m in
  53. let f = define_function "_start" (function_type void_ty [||]) m in
  54. let bb_cur = ref (entry_block f) in
  55. let b = builder_at_end ctx !bb_cur in
  56. let set_cur_bb bb =
  57. position_at_end bb b;
  58. bb_cur := bb
  59. in
  60. let i n = const_int int_ty n in
  61. let i8 n = const_int byte_ty n in
  62. (*let mem = define_global "mem" (const_null (array_type byte_ty memsize)) m in
  63. set_linkage Linkage.Private mem;*)
  64. let mem = build_alloca (array_type byte_ty memsize) "mem" b in
  65. let idx = build_alloca int_ty "idx" b in
  66. let gep () = build_in_bounds_gep mem [|i 0; build_load idx "" b|] "" b in
  67. let load ptr = build_load ptr "" b in
  68. let store ptr value = ignore (build_store value ptr b) in
  69. let rec compile_command = function
  70. | Incptr ->
  71. build_add (load idx) (i 1) "" b |> store idx
  72. | Decptr ->
  73. build_sub (load idx) (i 1) "" b |> store idx
  74. | Incdata ->
  75. build_add (load (gep ())) (i8 1) "" b |> store (gep ())
  76. | Decdata ->
  77. build_sub (load (gep ())) (i8 1) "" b |> store (gep ())
  78. | Output ->
  79. build_call putchar [|load (gep ())|] "" b |> ignore
  80. | Input ->
  81. build_call getchar [||] "" b |> store (gep ())
  82. | Loop p ->
  83. let bb_end = append_block ctx "" f in
  84. move_block_after !bb_cur bb_end;
  85. let bb_body = insert_block ctx "" bb_end in
  86. let bb_cond = insert_block ctx "" bb_body in
  87. ignore (build_br bb_cond b);
  88. position_at_end bb_cond b;
  89. let cond = build_icmp Icmp.Eq (load (gep ())) (i8 0) "" b in
  90. ignore (build_cond_br cond bb_end bb_body b);
  91. set_cur_bb bb_body;
  92. List.iter compile_command p;
  93. ignore (build_br bb_cond b);
  94. set_cur_bb bb_end
  95. in
  96. (* zero-initialize memory (use intrinsic for optimization assumptions) *)
  97. set_data_layout "e" m; (* little-endian, needed for optimization *)
  98. let memset =
  99. let arg_types = [|byteptr_ty; byte_ty; i32_ty; i32_ty; i1_ty|] in
  100. declare_function "llvm.memset.p0i8.i32" (function_type void_ty arg_types) m
  101. in
  102. let ptr = build_bitcast mem byteptr_ty "" b in
  103. build_call memset [|ptr; i8 0; i memsize; i 0; const_int i1_ty 0|] "" b |> ignore;
  104. (* set pivot to index 0 and compile program commands *)
  105. store idx (i 0);
  106. List.iter compile_command program;
  107. (* exit gracefully *)
  108. let cexit = declare_function "exit" (function_type void_ty [|i32_ty|]) m in
  109. ignore (build_call cexit [|i 0|] "" b);
  110. ignore (build_ret_void b);
  111. m
  112. let () =
  113. stdin |> read_program |> compile 30000 |> string_of_llmodule |> print_string