bf.c 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779
  1. /*
  2. * bf.c
  3. * https://gitlab.com/bztsrc/brainfuck
  4. *
  5. * Copyright (C) 2021 bzt (bztsrc@gitlab)
  6. *
  7. * Permission is hereby granted, free of charge, to any person
  8. * obtaining a copy of this software and associated documentation
  9. * files (the "Software"), to deal in the Software without
  10. * restriction, including without limitation the rights to use, copy,
  11. * modify, merge, publish, distribute, sublicense, and/or sell copies
  12. * of the Software, and to permit persons to whom the Software is
  13. * furnished to do so, subject to the following conditions:
  14. *
  15. * The above copyright notice and this permission notice shall be
  16. * included in all copies or substantial portions of the Software.
  17. *
  18. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  19. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  20. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  21. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  22. * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  23. * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  24. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  25. * DEALINGS IN THE SOFTWARE.
  26. *
  27. * @brief Brainfuck interpreter and compiler
  28. *
  29. */
  30. #include <unistd.h>
  31. #include <stdlib.h>
  32. #include <stdio.h>
  33. #include <string.h>
  34. #include <sys/time.h>
  35. /* bytecode */
  36. #define IMM 0x80
  37. /* tokens that can occur in scripts */
  38. #define INC 0x00 /* > ptr++; */
  39. #define DEC 0x10 /* < ptr--; */
  40. #define ADD 0x20 /* + (*ptr)++; */
  41. #define SUB 0x30 /* - (*ptr)--; */
  42. #define JZ 0x40 /* [ while(*ptr) { */
  43. #define JNZ 0x50 /* ] } */
  44. #define PUT 0x60 /* . putchar(*ptr); */
  45. #define GET 0x70 /* , *ptr = getchar(); */
  46. /* tokens generated by the optimizer */
  47. #define ZRO 0xF0 /* [-] */
  48. #define FFZ 0xF1 /* [>] */
  49. #define FLZ 0xF2 /* [<] */
  50. #define MVD 0xF3 /* [-<+>] */
  51. #define MVU 0xF4 /* [->+<] */
  52. #define MAXNEST 256
  53. /***************************
  54. * Main Brainfuck function *
  55. ***************************/
  56. int main(int argc, char **argv)
  57. {
  58. void *end;
  59. char **arg = argv, *inp = NULL, *out = NULL, *src = NULL, *tmp = NULL, *c, *il, cell = 'b';
  60. unsigned char *tokens = NULL, *datab = NULL, *db, bytecode[4], *b;
  61. unsigned short *dataw = NULL, *dw;
  62. unsigned int *params = NULL, *datad = NULL, *dd;
  63. int i, profiling = 0, mode = 0, memory = 32768, opt = 3, tsize = 0, size, nl, nest, jmp[MAXNEST];
  64. long int tim = 0, tim2 = 0;
  65. struct timeval tv;
  66. FILE *f;
  67. /* parse arguments */
  68. if(argc < 2) {
  69. usage: printf("BrainFuck by bzt Copyright (C) 2021 MIT license\n\n"
  70. "%s [-p] [-O[01234]] [-m<size>] [-w|-d] <bf source> [-b|-c|-s|-o] [out]\n\n"
  71. " -p turn on profiling\n"
  72. " -O optimization level\n"
  73. " -m set program memory size\n"
  74. " -w use word cells (16 bit)\n"
  75. " -d use dword cells (32 bit)\n"
  76. " -b output bytecode\n"
  77. " -c output C source\n"
  78. " -s output x86_64 assembly\n"
  79. " -o output ELF binary\n"
  80. "\n", argv[0]);
  81. exit(0);
  82. }
  83. while(arg && *arg) {
  84. arg++;
  85. if(!arg || !*arg) break;
  86. if(arg[0][0] == '-')
  87. switch(arg[0][1]) {
  88. case 'p': profiling++; break;
  89. case 'b': mode = 1; break;
  90. case 'c': mode = 2; break;
  91. case 's': mode = 3; break;
  92. case 'o': mode = 4; break;
  93. case 'O': opt = atoi(arg[0] + 2); break;
  94. case 'm': memory = atoi(arg[0] + 2); break;
  95. case 'w': cell = 'w'; break;
  96. case 'd': cell = 'l'; break;
  97. default: fprintf(stderr, "bf: unknown flag '%c'\n", arg[0][1]); exit(1); break;
  98. }
  99. else if(!inp) inp = arg[0];
  100. else out = arg[0];
  101. }
  102. if(!inp) {
  103. fprintf(stderr, "bf: no input file specified.\n");
  104. goto usage;
  105. }
  106. if(memory < 256) memory = 256;
  107. if(memory > 0xFFFFFF) memory = 0xFFFFFF;
  108. /* read input */
  109. f = fopen(inp, "r");
  110. if(f) {
  111. fseek(f, 0, SEEK_END);
  112. size = (int)ftell(f);
  113. if(size < 1) { fclose(f); goto err; }
  114. fseek(f, 0, SEEK_SET);
  115. src = (char*)malloc(size+1);
  116. if(!src) { fprintf(stderr, "bf: memory allocation error (%d bytes)\n", size+1); exit(2); }
  117. fread(src, size, 1, f);
  118. fclose(f);
  119. src[size] = 0;
  120. } else {
  121. err: fprintf(stderr, "bf: unable to read '%s'\n", inp);
  122. exit(1);
  123. }
  124. if(profiling) {
  125. gettimeofday(&tv, NULL);
  126. tim = (long int)(tv.tv_sec * 1000000L + tv.tv_usec);
  127. }
  128. /* tokenize (or read back bytecode) */
  129. if(src[0] == 'B' && src[1] == 'C') {
  130. tsize = size;
  131. } else {
  132. /* quickly count the number of valid instructions */
  133. for(tsize = 0, c = src; c < src + size; c++)
  134. switch(*c) {
  135. case '<': case '>': case '+': case '-': case '[': case ']': case '.': case ',': tsize++; break;
  136. }
  137. }
  138. if(tsize > 0xFFFFF) { fprintf(stderr, "bf: %s: too many instructions\n", inp); exit(1); }
  139. tokens = (unsigned char*)malloc(tsize);
  140. if(!tokens) { fprintf(stderr, "bf: memory allocation error (%d bytes)\n", tsize); exit(2); }
  141. params = (unsigned int*)malloc(tsize * sizeof(unsigned int));
  142. if(!params) { fprintf(stderr, "bf: memory allocation error (%d bytes)\n", tsize * (int)sizeof(unsigned int)); exit(2); }
  143. memset(params, 0, tsize * sizeof(unsigned int));
  144. if(src[0] == 'B' && src[1] == 'C') {
  145. /* bytecode */
  146. for(b = (unsigned char*)src + 2, i = 0; b < (unsigned char*)src + size && i < tsize; i++, b++) {
  147. tokens[i] = *b & 0x70;
  148. if((*b & 0xF0) == 0xF0) {
  149. tokens[i] = *b & 0xF7;
  150. if(tokens[i] != ZRO) {
  151. if(*b & 0x08) {
  152. params[i] = (unsigned int)((*(b + 1) << 16) | (*(b + 2) << 8) | *(b + 3));
  153. b += 3;
  154. } else {
  155. params[i] = (unsigned int)((*(b + 1) << 8) | *(b + 2));
  156. b += 2;
  157. }
  158. }
  159. } else {
  160. if(*b & IMM) {
  161. params[i] = (unsigned int)(((*b & 0x0F) << 16) | (*(b + 1) << 8) | *(b + 2));
  162. b += 2;
  163. } else {
  164. params[i] = (unsigned int)(*b & 0x0F);
  165. }
  166. }
  167. }
  168. } else {
  169. /* plain text */
  170. for(c = il = src, i = nest = 0, nl = 1; c < src + size && i < tsize; ) {
  171. switch(*c) {
  172. case '<':
  173. do { params[i]++; c++; } while(opt && *c == '<');
  174. tokens[i++] = DEC;
  175. break;
  176. case '>':
  177. do { params[i]++; c++; } while(opt && *c == '>');
  178. tokens[i++] = INC;
  179. break;
  180. case '+':
  181. do { params[i]++; c++; } while(opt && *c == '+');
  182. tokens[i++] = ADD;
  183. break;
  184. case '-':
  185. do { params[i]++; c++; } while(opt && *c == '-');
  186. tokens[i++] = SUB;
  187. break;
  188. case '[':
  189. if(nest + 1 >= MAXNEST) {
  190. fprintf(stderr, "%s:%d:%d: maximum nesting level reached\n", inp, nl, (int)(c - il + 1));
  191. exit(1);
  192. }
  193. jmp[nest++] = i;
  194. tokens[i++] = JZ;
  195. c++;
  196. break;
  197. case ']':
  198. if(nest < 1) {
  199. fprintf(stderr, "%s:%d:%d: ] without opening [\n", inp, nl, (int)(c - il + 1));
  200. exit(1);
  201. }
  202. nest--;
  203. /* optimize bytecode */
  204. if(opt > 1 && i && tokens[i - 1] == JZ) {
  205. i--;
  206. } else
  207. if(opt > 1 && i > 1 && tokens[i - 1] == SUB && tokens[i - 2] == JZ && params[i - 1] == 1) {
  208. i--;
  209. tokens[i - 1] = ZRO;
  210. params[i - 1] = params[i] = 0;
  211. } else
  212. if(opt > 2 && i > 1 && tokens[i - 1] == INC && tokens[i - 2] == JZ) {
  213. i--;
  214. tokens[i - 1] = FFZ;
  215. params[i - 1] = params[i];
  216. params[i] = params[i + 1] = 0;
  217. } else
  218. if(opt > 2 && i > 1 && tokens[i - 1] == DEC && tokens[i - 2] == JZ) {
  219. i--;
  220. tokens[i - 1] = FLZ;
  221. params[i - 1] = params[i];
  222. params[i] = params[i + 1] = 0;
  223. } else
  224. if(opt > 2 && i > 4 && tokens[i - 1] == INC && tokens[i - 2] == ADD && tokens[i - 3] == DEC &&
  225. tokens[i - 4] == SUB && tokens[i - 5] == JZ && params[i - 1] == params[i - 3] &&
  226. params[i - 2] == 1 && params[i - 4] == 1) {
  227. i -= 4;
  228. tokens[i - 1] = MVD;
  229. params[i - 1] = params[i + 1];
  230. params[i] = params[i + 1] = params[i + 2] = params[i + 3] = 0;
  231. } else
  232. if(opt > 2 && i > 4 && tokens[i - 1] == DEC && tokens[i - 2] == ADD && tokens[i - 3] == INC &&
  233. tokens[i - 4] == SUB && tokens[i - 5] == JZ && params[i - 1] == params[i - 3] &&
  234. params[i - 2] == 1 && params[i - 4] == 1) {
  235. i -= 4;
  236. tokens[i - 1] = MVU;
  237. params[i - 1] = params[i + 1];
  238. params[i] = params[i + 1] = params[i + 2] = params[i + 3] = 0;
  239. } else {
  240. params[i] = jmp[nest];
  241. params[jmp[nest]] = i;
  242. tokens[i++] = JNZ;
  243. }
  244. c++;
  245. break;
  246. case '.':
  247. tokens[i++] = PUT; c++;
  248. break;
  249. case ',':
  250. tokens[i++] = GET; c++;
  251. break;
  252. case '\n':
  253. nl++; c++; il = c;
  254. break;
  255. default: c++; break;
  256. }
  257. }
  258. if(nest > 0) {
  259. fprintf(stderr, "%s:%d:%d: unclosed [\n", inp, nl, (int)(c - il + 1));
  260. exit(1);
  261. }
  262. }
  263. free(src);
  264. tsize = i;
  265. if(tsize < 1) goto err;
  266. if(profiling) {
  267. gettimeofday(&tv, NULL);
  268. tim2 = (long int)(tv.tv_sec * 1000000L + tv.tv_usec);
  269. tim = tim2 - tim;
  270. fprintf(stderr, "Optimizer: %3ld.%06ld sec\n", tim / 1000000L, tim % 1000000L);
  271. }
  272. /* do the stuff */
  273. if(out) {
  274. f = fopen(out, "w");
  275. if(!f) {
  276. fprintf(stderr, "bf: unable to write '%s'\n", out);
  277. exit(1);
  278. }
  279. } else {
  280. if(mode == 1 || mode == 4) {
  281. fprintf(stderr, "bf: no output specified.\n");
  282. exit(1);
  283. }
  284. f = stdout;
  285. }
  286. switch(mode) {
  287. case 0:
  288. /* interpret bytecode */
  289. switch(cell) {
  290. /* byte cells (8 bit) */
  291. case 'b':
  292. datab = (unsigned char*)malloc(memory);
  293. if(!datab) { fprintf(stderr, "bf: memory allocation error (%d bytes)\n", memory); exit(2); }
  294. memset(datab, 0, memory);
  295. for(i = 0, db = datab, end = datab + memory; i < tsize; i++) {
  296. switch(tokens[i]) {
  297. case INC:
  298. db += params[i];
  299. if((void*)db >= end) { fprintf(stderr, "bf: out of bound addressing\n"); exit(2); }
  300. break;
  301. case DEC:
  302. db -= params[i];
  303. if(db < datab) { fprintf(stderr, "bf: out of bound addressing\n"); exit(2); }
  304. break;
  305. case ADD: (*db) += (unsigned char)params[i]; break;
  306. case SUB: (*db) -= (unsigned char)params[i]; break;
  307. case JZ: if(!*db) i = params[i]; break;
  308. case JNZ: if(*db) i = params[i]; break;
  309. case PUT: putchar(*db); break;
  310. case GET:
  311. *db = getchar();
  312. if(*db == 27) i = tsize;
  313. break;
  314. case ZRO: *db = 0; break;
  315. case FFZ:
  316. while(*db) {
  317. db += params[i];
  318. if((void*)db >= end) { fprintf(stderr, "bf: out of bound addressing\n"); exit(2); }
  319. }
  320. break;
  321. case FLZ:
  322. while(*db) {
  323. db -= params[i];
  324. if(db < datab) { fprintf(stderr, "bf: out of bound addressing\n"); exit(2); }
  325. }
  326. break;
  327. case MVD:
  328. if(*db) {
  329. if((db - params[i]) < datab) { fprintf(stderr, "bf: out of bound addressing\n"); exit(2); }
  330. *(db - params[i]) += *db; *db = 0;
  331. }
  332. break;
  333. case MVU:
  334. if(*db) {
  335. if((void*)(db + params[i]) >= end) { fprintf(stderr, "bf: out of bound addressing\n"); exit(2); }
  336. *(db + params[i]) += *db; *db = 0;
  337. }
  338. break;
  339. }
  340. }
  341. free(datab);
  342. break;
  343. /* word cells (16 bit) */
  344. case 'w':
  345. dataw = (unsigned short*)malloc(memory * sizeof(unsigned short));
  346. if(!dataw) { fprintf(stderr, "bf: memory allocation error (%d bytes)\n", memory * (int)sizeof(unsigned short)); exit(2); }
  347. memset(dataw, 0, memory * sizeof(unsigned short));
  348. for(i = 0, dw = dataw, end = dataw + memory; i < tsize; i++) {
  349. switch(tokens[i]) {
  350. case INC:
  351. dw += params[i];
  352. if((void*)dw >= end) { fprintf(stderr, "bf: out of bound addressing\n"); exit(2); }
  353. break;
  354. case DEC:
  355. dw -= params[i];
  356. if(dw < dataw) { fprintf(stderr, "bf: out of bound addressing\n"); exit(2); }
  357. break;
  358. case ADD: (*dw) += (unsigned short)params[i]; break;
  359. case SUB: (*dw) -= (unsigned short)params[i]; break;
  360. case JZ: if(!*dw) i = params[i]; break;
  361. case JNZ: if(*dw) i = params[i]; break;
  362. case PUT: putchar((unsigned char)*dw); break;
  363. case GET:
  364. *dw = (unsigned short)getchar();
  365. if(*dw == 27) i = tsize;
  366. break;
  367. case ZRO: *dw = 0; break;
  368. case FFZ:
  369. while(*dw) {
  370. dw += params[i];
  371. if((void*)dw >= end) { fprintf(stderr, "bf: out of bound addressing\n"); exit(2); }
  372. }
  373. break;
  374. case FLZ:
  375. while(*dw) {
  376. dw -= params[i];
  377. if(dw < dataw) { fprintf(stderr, "bf: out of bound addressing\n"); exit(2); }
  378. }
  379. break;
  380. case MVD:
  381. if(*dw) {
  382. if((dw - params[i]) < dataw) { fprintf(stderr, "bf: out of bound addressing\n"); exit(2); }
  383. *(dw - params[i]) += *dw; *dw = 0;
  384. }
  385. break;
  386. case MVU:
  387. if(*dw) {
  388. if((void*)(dw + params[i]) >= end) { fprintf(stderr, "bf: out of bound addressing\n"); exit(2); }
  389. *(dw + params[i]) += *dw; *dw = 0;
  390. }
  391. break;
  392. }
  393. }
  394. free(dataw);
  395. break;
  396. /* dword cells (32 bit) */
  397. case 'l':
  398. datad = (unsigned int*)malloc(memory * sizeof(unsigned int));
  399. if(!datad) { fprintf(stderr, "bf: memory allocation error (%d bytes)\n", memory * (int)sizeof(unsigned int)); exit(2); }
  400. memset(datad, 0, memory * sizeof(unsigned int));
  401. for(i = 0, dd = datad, end = datad + memory; i < tsize; i++) {
  402. switch(tokens[i]) {
  403. case INC:
  404. dd += params[i];
  405. if((void*)dd >= end) { fprintf(stderr, "bf: out of bound addressing\n"); exit(2); }
  406. break;
  407. case DEC:
  408. dd -= params[i];
  409. if(dd < datad) { fprintf(stderr, "bf: out of bound addressing\n"); exit(2); }
  410. break;
  411. case ADD: (*dd) += params[i]; break;
  412. case SUB: (*dd) -= params[i]; break;
  413. case JZ: if(!*dd) i = params[i]; break;
  414. case JNZ: if(*dd) i = params[i]; break;
  415. case PUT: putchar((unsigned char)*dd); break;
  416. case GET:
  417. *dd = (unsigned int)getchar();
  418. if(*dd == 27) i = tsize;
  419. break;
  420. case ZRO: *dd = 0; break;
  421. case FFZ:
  422. while(*dd) {
  423. dd += params[i];
  424. if((void*)dd >= end) { fprintf(stderr, "bf: out of bound addressing\n"); exit(2); }
  425. }
  426. break;
  427. case FLZ:
  428. while(*dd) {
  429. dd -= params[i];
  430. if(dd < datad) { fprintf(stderr, "bf: out of bound addressing\n"); exit(2); }
  431. }
  432. break;
  433. case MVD:
  434. if(*dd) {
  435. if((dd - params[i]) < datad) { fprintf(stderr, "bf: out of bound addressing\n"); exit(2); }
  436. *(dd - params[i]) += *dd; *dd = 0;
  437. }
  438. break;
  439. case MVU:
  440. if(*dd) {
  441. if((void*)(dd + params[i]) >= end) { fprintf(stderr, "bf: out of bound addressing\n"); exit(2); }
  442. *(dd + params[i]) += *dd; *dd = 0;
  443. }
  444. break;
  445. }
  446. }
  447. free(datad);
  448. break;
  449. }
  450. break;
  451. case 1:
  452. /* output bytecode */
  453. fwrite("BC", 2, 1, f);
  454. for(i = 0; i < tsize; i++) {
  455. bytecode[0] = tokens[i];
  456. switch(tokens[i]) {
  457. /* tokens generated by the optimizer */
  458. case ZRO:
  459. fwrite(bytecode, 1, 1, f);
  460. break;
  461. case FFZ: case FLZ: case MVD: case MVU:
  462. if(params[i] > 0xFFFF) {
  463. bytecode[0] |= 0x08;
  464. bytecode[1] = (params[i] >> 16) & 0xFF;
  465. bytecode[2] = (params[i] >> 8) & 0xFF;
  466. bytecode[3] = params[i] & 0xFF;
  467. fwrite(bytecode, 4, 1, f);
  468. } else {
  469. bytecode[1] = (params[i] >> 8) & 0xFF;
  470. bytecode[2] = params[i] & 0xFF;
  471. fwrite(bytecode, 3, 1, f);
  472. }
  473. break;
  474. /* normal tokens in the script */
  475. default:
  476. if(params[i] < 16) {
  477. bytecode[0] |= params[i];
  478. fwrite(bytecode, 1, 1, f);
  479. } else {
  480. bytecode[0] |= IMM | ((params[i] >> 16) & 0x0F);
  481. bytecode[1] = (params[i] >> 8) & 0xFF;
  482. bytecode[2] = params[i] & 0xFF;
  483. fwrite(bytecode, 3, 1, f);
  484. }
  485. break;
  486. }
  487. }
  488. break;
  489. case 2:
  490. /* output C */
  491. fprintf(f, "/* BrainFuck compiled ANSI C */\n\n"
  492. "#include <stdio.h>\n");
  493. if(profiling)
  494. fprintf(f, "#include <string.h>\n#include <sys/time.h>\n");
  495. fprintf(f, "\nint main(int argc, char **argv)\n{\n"
  496. " unsigned %s data[%d], *ptr = data;\n", cell == 'l' ? "int" : (cell == 'w' ? "short" : "char"), memory);
  497. if(profiling)
  498. fprintf(f, " long int tim = 0;\n struct timeval tv;\n\n"
  499. " gettimeofday(&tv, NULL);\n tim = (long int)(tv.tv_sec * 1000000L + tv.tv_usec);\n");
  500. fprintf(f, "\n");
  501. for(i = nest = 0; i < tsize; i++) {
  502. if(tokens[i] == JNZ) nest--;
  503. for(nl = 0; nl < nest + 1; nl++)
  504. fprintf(f, " ");
  505. switch(tokens[i]) {
  506. case INC:
  507. if(params[i] == 1) fprintf(f, "ptr++;");
  508. else fprintf(f, "ptr += %d;", params[i]);
  509. if(opt < 4)
  510. fprintf(f, " if(ptr >= data + %d) goto end;", memory);
  511. fprintf(f, "\n");
  512. break;
  513. case DEC:
  514. if(params[i] == 1) fprintf(f, "ptr--;");
  515. else fprintf(f, "ptr -= %d;", params[i]);
  516. if(opt < 4)
  517. fprintf(f, " if(ptr < data) goto end;");
  518. fprintf(f, "\n");
  519. break;
  520. case ADD:
  521. if(params[i] == 1) fprintf(f, "(*ptr)++;\n");
  522. else fprintf(f, "(*ptr) += %d;\n", params[i]);
  523. break;
  524. case SUB:
  525. if(params[i] == 1) fprintf(f, "(*ptr)--;\n");
  526. else fprintf(f, "(*ptr) -= %d;\n", params[i]);
  527. break;
  528. case JZ:
  529. fprintf(f, "while(*ptr) {\n");
  530. nest++;
  531. break;
  532. case JNZ:
  533. fprintf(f, "}\n");
  534. break;
  535. case PUT:
  536. fprintf(f, "putchar((unsigned char)*ptr);\n");
  537. break;
  538. case GET:
  539. fprintf(f, "*ptr = (unsigned %s)getchar(); if(*ptr == 27) goto end;\n",
  540. cell == 'l' ? "int" : (cell == 'w' ? "short" : "char"));
  541. break;
  542. case ZRO:
  543. fprintf(f, "*ptr = 0;\n");
  544. break;
  545. case FFZ:
  546. fprintf(f, "while(*ptr) { ptr += %d; ", params[i]);
  547. if(opt < 4)
  548. fprintf(f, "if(ptr >= data + %d) { goto end; } ", memory);
  549. fprintf(f, "}\n");
  550. break;
  551. case FLZ:
  552. fprintf(f, "while(*ptr) { ptr -= %d; ", params[i]);
  553. if(opt < 4)
  554. fprintf(f, "if(ptr < data) { goto end; } ");
  555. fprintf(f, "}\n");
  556. break;
  557. case MVD:
  558. fprintf(f, "if(*ptr) { ");
  559. if(opt < 4)
  560. fprintf(f, "if(ptr - %d < data) { goto end; } ", params[i]);
  561. fprintf(f, "*(ptr - %d) += *ptr; *ptr = 0; }\n", params[i]);
  562. break;
  563. case MVU:
  564. fprintf(f, "if(*ptr) { ");
  565. if(opt < 4)
  566. fprintf(f, "if(ptr + %d >= data + %d) { goto end; } ", params[i], memory);
  567. fprintf(f, "*(ptr + %d) += *ptr; *ptr = 0; }\n", params[i]);
  568. break;
  569. }
  570. }
  571. fprintf(f, "\nend:");
  572. if(profiling)
  573. fprintf(f, "gettimeofday(&tv, NULL);\n"
  574. " tim = (long int)(tv.tv_sec * 1000000L + tv.tv_usec) - tim;\n"
  575. " fprintf(stderr, \"Execution: %%3ld.%%06ld sec\\n\", tim / 1000000L, tim %% 1000000L);\n ");
  576. fprintf(f, "return 0;\n}\n");
  577. break;
  578. case 3:
  579. case 4:
  580. if(mode == 4) {
  581. if(f != stdout) fclose(f);
  582. tmp = (char*)malloc(16 + strlen(out));
  583. if(!tmp) { fprintf(stderr, "bf: memory allocation error (%d bytes)\n", 4 + (int)strlen(out)); exit(2); }
  584. sprintf(tmp, "%s.s", out);
  585. f = fopen(tmp, "w");
  586. if(!f) {
  587. fprintf(stderr, "bf: unable to create temporary file '%s'\n", tmp);
  588. exit(2);
  589. }
  590. }
  591. /* output assembly */
  592. fprintf(f, "/* BrainFuck compiled native instructions */\n\n"
  593. ".globl %s\n.type %s, @function\n\n", mode == 4 ? "_start" : "main", mode == 4 ? "_start" : "main");
  594. if(profiling)
  595. fprintf(f, ".section .rodata\nstr: .string \"Execution: %%3ld.%%06ld sec\\n\"\n\n");
  596. fprintf(f, ".text\n%s:\n"
  597. " pushq %%rbp\n"
  598. " movq %%rsp, %%rbp\n"
  599. " subq $%d, %%rsp\n"
  600. " movq %%rsp, %%rbx\n", mode == 4 ? "_start" : "main",
  601. memory * (cell == 'l' ? 4 : (cell == 'w' ? 2 : 1)) + profiling * 32);
  602. if(profiling)
  603. fprintf(f,
  604. " leaq -16(%%rbp), %%rdi\n"
  605. " xorq %%rsi, %%rsi\n"
  606. " call gettimeofday@PLT\n"
  607. " movq -16(%%rbp), %%rax\n"
  608. " xorq %%rdx, %%rdx\n"
  609. " imulq $1000000, %%rax, %%rdx\n"
  610. " movq -8(%%rbp), %%rax\n"
  611. " addq %%rdx, %%rax\n"
  612. " movq %%rax, -24(%%rbp)\n\n");
  613. /* use RBX as it is a callee saved GPR */
  614. for(i = 0; i < tsize; i++) {
  615. switch(tokens[i]) {
  616. case INC:
  617. if(params[i] == 1) fprintf(f, " incq %%rbx\n");
  618. else fprintf(f, " addq $%d, %%rbx\n", params[i]);
  619. if(opt < 4)
  620. fprintf(f, " cmpq %%rbx, %%rbp\n"
  621. " jbe end\n");
  622. break;
  623. case DEC:
  624. if(params[i] == 1) fprintf(f, " decq %%rbx\n");
  625. else fprintf(f, " subq $%d, %%rbx\n", params[i]);
  626. if(opt < 4)
  627. fprintf(f, " cmpq %%rbx, %%rsp\n"
  628. " ja end\n");
  629. break;
  630. case ADD:
  631. if(params[i] == 1) fprintf(f, " inc%c (%%rbx)\n", cell);
  632. else fprintf(f, " add%c $%d, (%%rbx)\n", cell, params[i]);
  633. break;
  634. case SUB:
  635. if(params[i] == 1) fprintf(f, " dec%c (%%rbx)\n", cell);
  636. else fprintf(f, " sub%c $%d, (%%rbx)\n", cell, params[i]);
  637. break;
  638. case JZ:
  639. fprintf(f, " cmp%c $0, (%%rbx)\n"
  640. " jz .L%04X\n"
  641. ".L%04X:\n", cell, params[i], i);
  642. break;
  643. case JNZ:
  644. fprintf(f, " cmp%c $0, (%%rbx)\n"
  645. " jnz .L%04X\n"
  646. ".L%04X:\n", cell, params[i], i);
  647. break;
  648. case PUT:
  649. fprintf(f, " movz%cq (%%rbx), %%rdi\n"
  650. " call putchar@PLT\n", cell);
  651. break;
  652. case GET:
  653. fprintf(f, " call getchar@PLT\n"
  654. " cmpb $27, %%al\n"
  655. " je end\n"
  656. " mov%s %%al, (%%rbx)\n", cell == 'l' ? "zbl" : (cell == 'w' ? "zbw" : "b"));
  657. break;
  658. case ZRO:
  659. fprintf(f, " mov%c $0, (%%rbx)\n", cell);
  660. break;
  661. case FFZ:
  662. fprintf(f, "1: cmp%c $0, (%%rbx)\n"
  663. " jz 2f\n"
  664. " addq $%d, %%rbx\n", cell, params[i]);
  665. if(opt < 4)
  666. fprintf(f, " cmpq %%rbx, %%rbp\n"
  667. " jbe end\n");
  668. fprintf(f, " jmp 1b\n"
  669. "2:\n");
  670. break;
  671. case FLZ:
  672. fprintf(f, "1: cmp%c $0, (%%rbx)\n"
  673. " jz 2f\n"
  674. " subq $%d, %%rbx\n", cell, params[i]);
  675. if(opt < 4)
  676. fprintf(f, " cmpq %%rbx, %%rsp\n"
  677. " ja end\n");
  678. fprintf(f, " jmp 1b\n"
  679. "2:\n");
  680. break;
  681. case MVD:
  682. fprintf(f, " cmp%c $0, (%%rbx)\n"
  683. " jz 1f\n", cell);
  684. if(opt < 4)
  685. fprintf(f, " leaq -%d(%%rbx), %%rax\n"
  686. " cmpq %%rax, %%rsp\n"
  687. " ja end\n", params[i] * (cell == 'l' ? 4 : (cell == 'w' ? 2 : 1)));
  688. fprintf(f, " mov%c (%%rbx), %%%s\n"
  689. " add%c %%%s, -%d(%%rbx)\n"
  690. " mov%c $0, (%%rbx)\n"
  691. "1:\n", cell, cell == 'l' ? "eax" : (cell == 'w' ? "ax" : "al"),
  692. cell, cell == 'l' ? "eax" : (cell == 'w' ? "ax" : "al"),
  693. params[i] * (cell == 'l' ? 4 : (cell == 'w' ? 2 : 1)), cell);
  694. break;
  695. case MVU:
  696. fprintf(f, " cmp%c $0, (%%rbx)\n"
  697. " jz 1f\n", cell);
  698. if(opt < 4)
  699. fprintf(f, " leaq %d(%%rbx), %%rax\n"
  700. " cmpq %%rax, %%rbp\n"
  701. " jbe end\n", params[i] * (cell == 'l' ? 4 : (cell == 'w' ? 2 : 1)));
  702. fprintf(f, " mov%c (%%rbx), %%%s\n"
  703. " add%c %%%s, %d(%%rbx)\n"
  704. " mov%c $0, (%%rbx)\n"
  705. "1:\n", cell, cell == 'l' ? "eax" : (cell == 'w' ? "ax" : "al"),
  706. cell, cell == 'l' ? "eax" : (cell == 'w' ? "ax" : "al"),
  707. params[i] * (cell == 'l' ? 4 : (cell == 'w' ? 2 : 1)), cell);
  708. break;
  709. }
  710. }
  711. fprintf(f, "\nend:\n");
  712. if(profiling)
  713. fprintf(f, " leaq -16(%%rbp), %%rdi\n"
  714. " xorq %%rsi, %%rsi\n"
  715. " call gettimeofday@PLT\n"
  716. " movq -16(%%rbp), %%rax\n"
  717. " xorq %%rdx, %%rdx\n"
  718. " imulq $1000000, %%rax, %%rdx\n"
  719. " movq -8(%%rbp), %%rax\n"
  720. " addq %%rdx, %%rax\n"
  721. " subq -24(%%rbp), %%rax\n"
  722. " xorq %%rdx, %%rdx\n"
  723. " movq $1000000, %%rcx\n"
  724. " idivq %%rcx\n"
  725. " movq %%rdx,%%rcx\n"
  726. " movq %%rax,%%rdx\n"
  727. " leaq str(%%rip),%%rsi\n"
  728. " movq stderr(%%rip),%%rdi\n"
  729. " xorq %%rax,%%rax\n"
  730. " call fprintf@PLT\n");
  731. fprintf(f, " leave\n");
  732. if(mode == 4) {
  733. fprintf(f, " xorq %%rdi, %%rdi\n call exit@PLT\n");
  734. /* use "as" and "ld" from binutils to create ELF */
  735. fclose(f); f = stdout;
  736. src = (char*)malloc(128 + 2*strlen(out));
  737. if(!src) { fprintf(stderr, "bf: memory allocation error (%d bytes)\n", 128 + 2*(int)strlen(out)); exit(2); }
  738. sprintf(src, "as %s -o %s.o", tmp, out);
  739. if(system(src)) { fprintf(stderr, "bf: unable to execute '%s'\n", src); }
  740. else {
  741. sprintf(src, "ld --dynamic-linker /lib/ld-linux-x86-64.so.2 -lc %s.o -o %s", out, out);
  742. if(system(src)) {
  743. sprintf(src, "ld --dynamic-linker /lib/ld-linux.so.2 -lc %s.o -o %s", out, out);
  744. if(system(src)) { fprintf(stderr, "bf: unable to execute '%s'\n", src); }
  745. }
  746. }
  747. unlink(tmp);
  748. tmp[strlen(tmp)-1] = 'o';
  749. unlink(tmp);
  750. free(tmp);
  751. free(src);
  752. } else
  753. fprintf(f, " ret\n");
  754. break;
  755. }
  756. if(profiling && !mode) {
  757. gettimeofday(&tv, NULL);
  758. tim = (long int)(tv.tv_sec * 1000000L + tv.tv_usec) - tim2;
  759. fprintf(stderr, "Execution: %3ld.%06ld sec\n", tim / 1000000L, tim % 1000000L);
  760. }
  761. /* clean up */
  762. if(f != stdout) fclose(f);
  763. free(tokens);
  764. free(params);
  765. return 0;
  766. }