Brian Gaeke | ddc1aaa | 2004-10-05 18:05:25 +0000 | [diff] [blame] | 1 | //===-- BFtoLLVM.cpp - BF language Front End for LLVM ---------------------===// |
Misha Brukman | 237cef4 | 2005-04-20 16:42:34 +0000 | [diff] [blame] | 2 | // |
Brian Gaeke | ddc1aaa | 2004-10-05 18:05:25 +0000 | [diff] [blame] | 3 | // The LLVM Compiler Infrastructure |
| 4 | // |
| 5 | // This file was developed by the LLVM research group and is distributed under |
| 6 | // the University of Illinois Open Source License. See LICENSE.TXT for details. |
Misha Brukman | 237cef4 | 2005-04-20 16:42:34 +0000 | [diff] [blame] | 7 | // |
Brian Gaeke | ddc1aaa | 2004-10-05 18:05:25 +0000 | [diff] [blame] | 8 | //===----------------------------------------------------------------------===// |
| 9 | // |
| 10 | // This is a simple front end for the BF language. It is compatible with the |
| 11 | // language as described in "The BrainF*** Language Specification (01 January |
| 12 | // 2002)", which is available from http://esoteric.sange.fi/ENSI . It does not |
| 13 | // implement the optional keyword # ("Output partial tape state"). |
| 14 | // |
| 15 | //===----------------------------------------------------------------------===// |
| 16 | |
| 17 | #include <iostream> |
| 18 | #include <vector> |
| 19 | #include <fstream> |
| 20 | #include <cerrno> |
| 21 | #include <cstring> |
| 22 | #include <string> |
| 23 | #include <cstdio> |
| 24 | #include <cassert> |
| 25 | |
| 26 | void emitDeclarations(std::ofstream &dest) { |
| 27 | dest << "; This assembly code brought to you by BFtoLLVM\n" |
| 28 | << "\nimplementation\n" |
| 29 | << "\n; Declarations\n" |
| 30 | << "\ndeclare int %getchar()\n" |
| 31 | << "declare int %putchar(int)\n" |
Chris Lattner | fb882ce | 2006-03-03 01:31:12 +0000 | [diff] [blame] | 32 | << "declare void %llvm.memset.i32(sbyte*, ubyte, uint, uint)\n" |
Brian Gaeke | ddc1aaa | 2004-10-05 18:05:25 +0000 | [diff] [blame] | 33 | << "\n"; |
| 34 | } |
| 35 | |
| 36 | void emitMainFunctionProlog(std::ofstream &dest) { |
| 37 | dest << "\n; Main function\n" |
| 38 | << "int %main(int %argc, sbyte** %argv) {\n" |
| 39 | << "\nentry:\n" |
| 40 | << "%arr = alloca sbyte, uint 30000\n" |
Chris Lattner | fb882ce | 2006-03-03 01:31:12 +0000 | [diff] [blame] | 41 | << "call void (sbyte*, ubyte, uint, uint)* %llvm.memset.i32" |
| 42 | << "(sbyte* %arr, ubyte 0, uint 30000, uint 1)\n" |
Brian Gaeke | ddc1aaa | 2004-10-05 18:05:25 +0000 | [diff] [blame] | 43 | << "%ptrbox = alloca sbyte*\n" |
| 44 | << "store sbyte* %arr, sbyte **%ptrbox\n" |
| 45 | << "\n"; |
| 46 | } |
| 47 | |
| 48 | void emitMainFunctionEpilog(std::ofstream &dest) { |
| 49 | dest << "ret int 0\n" |
| 50 | << "}\n"; |
| 51 | } |
| 52 | |
| 53 | std::string gensym (const std::string varName, bool percent = true) { |
| 54 | char buf[80]; |
| 55 | static unsigned int SymbolCounter = 0; |
| 56 | sprintf (buf, "%s%s%u", percent ? "%" : "", varName.c_str(), SymbolCounter++); |
| 57 | return std::string (buf); |
| 58 | } |
| 59 | |
| 60 | void emitArith (std::string op, char delta, std::ofstream &dest) { |
| 61 | std::string ptr = gensym (op + "ptr"), |
Misha Brukman | 59f6449 | 2005-04-22 03:18:56 +0000 | [diff] [blame] | 62 | val = gensym (op + "val"), |
| 63 | result = gensym (op + "result"); |
Misha Brukman | 237cef4 | 2005-04-20 16:42:34 +0000 | [diff] [blame] | 64 | dest << ptr << " = load sbyte** %ptrbox\n" |
Brian Gaeke | ddc1aaa | 2004-10-05 18:05:25 +0000 | [diff] [blame] | 65 | << val << " = load sbyte* " << ptr << "\n" |
| 66 | << result << " = add sbyte " << val << ", " << (int)delta << "\n" |
| 67 | << "store sbyte " << result << ", sbyte* " << ptr << "\n"; |
| 68 | } |
| 69 | |
| 70 | // + becomes ++*p; and - becomes --*p; |
| 71 | void emitPlus (std::ofstream &dest, int ct) { emitArith ("plus", +ct, dest); } |
| 72 | void emitMinus (std::ofstream &dest, int ct) { emitArith ("minus", -ct, dest); } |
| 73 | |
| 74 | void emitLoadAndCast (std::string ptr, std::string val, std::string cast, |
| 75 | std::string type, std::ofstream &dest) { |
| 76 | dest << ptr << " = load sbyte** %ptrbox\n" |
| 77 | << val << " = load sbyte* " << ptr << "\n" |
| 78 | << cast << " = cast sbyte " << val << " to " << type << "\n"; |
| 79 | } |
| 80 | |
| 81 | // , becomes *p = getchar(); |
| 82 | void emitComma(std::ofstream &dest, int ct) { |
| 83 | assert (ct == 1); |
| 84 | std::string ptr = gensym("commaptr"), read = gensym("commaread"), |
| 85 | cast = gensym("commacast"); |
| 86 | dest << ptr << " = load sbyte** %ptrbox\n" |
| 87 | << read << " = call int %getchar()\n" |
| 88 | << cast << " = cast int " << read << " to sbyte\n" |
| 89 | << "store sbyte " << cast << ", sbyte* " << ptr << "\n"; |
| 90 | } |
| 91 | |
| 92 | // . becomes putchar(*p); |
| 93 | void emitDot(std::ofstream &dest, int ct) { |
| 94 | assert (ct == 1); |
| 95 | std::string ptr = gensym("dotptr"), val = gensym("dotval"), |
| 96 | cast = gensym("dotcast"); |
| 97 | emitLoadAndCast (ptr, val, cast, "int", dest); |
| 98 | dest << "call int %putchar(int " << cast << ")\n"; |
| 99 | } |
| 100 | |
| 101 | void emitPointerArith(std::string opname, int delta, std::ofstream &dest) { |
| 102 | std::string ptr = gensym(opname + "ptr"), result = gensym(opname + "result"); |
| 103 | dest << ptr << " = load sbyte** %ptrbox\n" |
| 104 | << result << " = getelementptr sbyte* " << ptr << ", int " << delta |
| 105 | << "\n" |
| 106 | << "store sbyte* " << result << ", sbyte** %ptrbox\n"; |
| 107 | } |
| 108 | |
| 109 | // < becomes --p; and > becomes ++p; |
| 110 | void emitLT(std::ofstream &dest, int ct) { emitPointerArith ("lt", -ct, dest); } |
| 111 | void emitGT(std::ofstream &dest, int ct) { emitPointerArith ("gt", +ct, dest); } |
| 112 | |
| 113 | static std::vector<std::string> whileStack; |
| 114 | |
| 115 | // [ becomes while (*p) { |
| 116 | void emitLeftBracket(std::ofstream &dest, int ct) { |
| 117 | assert (ct == 1); |
| 118 | std::string whileName = gensym ("While", false); |
| 119 | whileStack.push_back (whileName); |
| 120 | dest << "br label %testFor" << whileName << "\n" |
| 121 | << "\ninside" << whileName << ":\n"; |
| 122 | } |
| 123 | |
| 124 | // ] becomes } |
| 125 | void emitRightBracket(std::ofstream &dest, int ct) { |
| 126 | assert (ct == 1); |
| 127 | std::string whileName = whileStack.back (), |
| 128 | ptr = gensym("bracketptr"), |
| 129 | val = gensym("bracketval"), |
| 130 | cast = gensym("bracketcast"); |
| 131 | whileStack.pop_back (); |
| 132 | dest << "br label %testFor" << whileName << "\n" |
| 133 | << "\ntestFor" << whileName << ":\n"; |
| 134 | emitLoadAndCast (ptr, val, cast, "bool", dest); |
| 135 | dest << "br bool " << cast << ", label %inside" << whileName << ", " |
| 136 | << "label %after" << whileName << "\n" |
| 137 | << "\nafter" << whileName << ":\n"; |
| 138 | } |
| 139 | |
| 140 | typedef void (*FuncTy)(std::ofstream &, int); |
| 141 | static FuncTy table[256]; |
| 142 | static bool multi[256]; |
| 143 | |
| 144 | void consume (int ch, int repeatCount, std::ofstream &dest) { |
| 145 | FuncTy func = table[ch]; |
| 146 | if (!func) |
| 147 | return; |
| 148 | else if (multi[ch]) |
| 149 | func (dest, repeatCount); |
| 150 | else |
| 151 | for (int i = 0; i < repeatCount; ++i) |
| 152 | func (dest, 1); |
| 153 | } |
| 154 | |
| 155 | void initializeTable() { |
| 156 | memset (table, 0, 256); |
| 157 | memset (multi, 0, 256); |
| 158 | table[(int)'+'] = emitPlus; multi[(int)'+'] = true; |
| 159 | table[(int)'-'] = emitMinus; multi[(int)'-'] = true; |
| 160 | table[(int)','] = emitComma; multi[(int)','] = false; |
| 161 | table[(int)'.'] = emitDot; multi[(int)'.'] = false; |
| 162 | table[(int)'<'] = emitLT; multi[(int)'<'] = true; |
| 163 | table[(int)'>'] = emitGT; multi[(int)'>'] = true; |
| 164 | table[(int)'['] = emitLeftBracket; multi[(int)'['] = false; |
| 165 | table[(int)']'] = emitRightBracket; multi[(int)']'] = false; |
| 166 | } |
| 167 | |
| 168 | int main (int argc, char **argv) { |
| 169 | if (argc != 3) { |
| 170 | std::cerr << "usage: " << argv[0] << " input-source output-llvm\n"; |
| 171 | return 1; |
| 172 | } |
| 173 | |
| 174 | char *sourceFileName = argv[1]; |
| 175 | char *destFileName = argv[2]; |
Misha Brukman | 237cef4 | 2005-04-20 16:42:34 +0000 | [diff] [blame] | 176 | |
Brian Gaeke | ddc1aaa | 2004-10-05 18:05:25 +0000 | [diff] [blame] | 177 | std::ifstream src (sourceFileName); |
| 178 | if (!src.good()) { |
| 179 | std::cerr << sourceFileName << ": " << strerror(errno) << "\n"; |
| 180 | return 1; |
| 181 | } |
| 182 | |
| 183 | std::ofstream dest (destFileName); |
| 184 | if (!dest.good()) { |
| 185 | std::cerr << destFileName << ": " << strerror(errno) << "\n"; |
| 186 | return 1; |
| 187 | } |
Misha Brukman | 237cef4 | 2005-04-20 16:42:34 +0000 | [diff] [blame] | 188 | |
Brian Gaeke | ddc1aaa | 2004-10-05 18:05:25 +0000 | [diff] [blame] | 189 | emitDeclarations(dest); |
| 190 | emitMainFunctionProlog(dest); |
| 191 | |
| 192 | initializeTable(); |
| 193 | char ch, lastCh; |
| 194 | src >> lastCh; |
| 195 | int repeatCount = 1; |
| 196 | for (src >> ch; !src.eof (); src >> ch, ++repeatCount) |
| 197 | if (ch != lastCh) { |
| 198 | consume (lastCh, repeatCount, dest); |
| 199 | lastCh = ch; |
| 200 | repeatCount = 0; |
| 201 | } |
| 202 | consume (lastCh, repeatCount, dest); |
Misha Brukman | 237cef4 | 2005-04-20 16:42:34 +0000 | [diff] [blame] | 203 | |
Brian Gaeke | ddc1aaa | 2004-10-05 18:05:25 +0000 | [diff] [blame] | 204 | emitMainFunctionEpilog(dest); |
| 205 | |
| 206 | src.close(); |
| 207 | dest.close(); |
| 208 | return 0; |
| 209 | } |