ICFP Contest 2006
Team: Abstraction Anonymous

um.c

Download

  1. /*
  2. * um.c
  3. * by team Abstraction Anonymous: Bill Weiss, Daniel Lyons, David Baird
  4. * Written for the 2006 ICFP Programming Contest (www.icfpcontest.org)
  5. *
  6. * Complete working implementation of the virtual machine specified at
  7. * http://www.icfpcontest.org/um-spec.txt
  8. */
  9.  
  10. #include <stdio.h>
  11. #include <stdlib.h>
  12. #include <string.h>
  13. #include <assert.h>
  14. #include <arpa/inet.h>
  15. #include <sys/types.h>
  16. #include <sys/mman.h>
  17. #include <sys/stat.h>
  18. #include <fcntl.h>
  19.  
  20. /*
  21. * Unpacking functions: uint32 -> instruction with operands
  22. *
  23. * For speed, we decided that these would all be macros. A good compiler
  24. * would probably make inline functions equivalent to this in speed, but
  25. * we were in a hurry.
  26. */
  27. #define A(inst) ((inst >> 0x6) & 0x7)
  28. #define B(inst) ((inst >> 0x3) & 0x7)
  29. #define C(inst) (inst & 0x7)
  30. #define A13(inst) ((inst >> 25) & 0x7)
  31. #define DATA(inst) (inst & 0x1ffffff)
  32. #define OP(inst) (inst >> 28)
  33. #define REG(num) (registers[num])
  34. #define REGA(inst) (REG(A(inst)))
  35. #define REGB(inst) (REG(B(inst)))
  36. #define REGC(inst) (REG(C(inst)))
  37. #define REGA13(inst) (REG(A13(inst)))
  38.  
  39. /*
  40. * This was carefully tuned by increasing it every time memory allocation
  41. * failed in the VM. While that sounds like a bad way to go, it worked
  42. * ok. I suspect that the actual needs are near (1024 * 1024), but it
  43. * crashed after a while with only that much memory. We just added the
  44. * 32 in to make it work.
  45. */
  46. #define MEMSIZ (1024 * 1024 * 32)
  47.  
  48. typedef unsigned int platter;
  49.  
  50. /*
  51. * For a while we thought we were going to use functions for this, and
  52. * wanted to not have to think about variable passing. Shameful, I know.
  53. * These could just all be put in main() now since we don't have any
  54. * functions,but this is the way it was during the competition.
  55. */
  56. platter registers[8] = {0,0,0,0,0,0,0,0};
  57. platter * arrays[MEMSIZ];
  58. size_t siz_arrays[MEMSIZ];
  59. size_t num_array = 1;
  60. platter finger = 0;
  61.  
  62. int main(int argc, char *argv[]) {
  63. int lastloc = 0, i;
  64. platter cur;
  65. platter * temp;
  66. struct stat x;
  67.  
  68. /*
  69. * I'm ashamed to say that originally, we were just hardcoding the
  70. * filesize (while we were making it work). This helped run the umix
  71. * image :)
  72. */
  73. stat(argv[1], &x);
  74. /*
  75. * Originally, we were working directly on the mmap()ed file. Our
  76. * memory allocation system didn't play well with it (calling free()
  77. * on a mmap() pointer doesn't work), so we came up with this garbage.
  78. * It works pretty well.
  79. */
  80. temp = (platter *) mmap(NULL, x.st_size,
  81. PROT_READ | PROT_WRITE, MAP_PRIVATE,
  82. open(argv[1], O_RDONLY, 0600), (off_t) 0);
  83. arrays[0] = (platter *) malloc(x.st_size);
  84. memcpy(arrays[0], temp, x.st_size);
  85. munmap(temp, x.st_size);
  86.  
  87. /*
  88. * The VM was written on a Mac, and this wasn't there (since the
  89. * endian-ness was the same). Once David came in, we had to add this
  90. * to make it work. Benchmarking found that this is really fast.
  91. * Since it's a one-time cost anyway, who cares?
  92. */
  93. for (i = finger; i < (finger + x.st_size / sizeof(platter)); i++)
  94. arrays[0][i] = htonl(arrays[0][i]);
  95. siz_arrays[0] = x.st_size;
  96. /*
  97. * This is the (potentially) interesting part. Grab an instruction,
  98. * do it.
  99. */
  100. while(1) {
  101. cur = arrays[0][finger++];
  102.  
  103. switch(OP(cur)) {
  104. /* #0. Conditional Move.
  105. *
  106. * The register A receives the value in register B,
  107. * unless the register C contains 0.
  108. */
  109. case 0:
  110. if (REGC(cur) != 0) {
  111. REGA(cur) = REGB(cur);
  112. }
  113. break;
  114. /* #1. Array Index.
  115. *
  116. * The register A receives the value stored at offset
  117. * in register C in the array identified by B.
  118. */
  119. case 1:
  120. REGA(cur) = arrays[REGB(cur)][REGC(cur)];
  121. break;
  122. /* #2. Array Amendment.
  123. *
  124. * The array identified by A is amended at the offset
  125. * in register B to store the value in register C.
  126. */
  127. case 2:
  128. assert(arrays[REGA(cur)] != 0);
  129. arrays[REGA(cur)][REGB(cur)] = REGC(cur);
  130. break;
  131. /* #3. Addition.
  132. *
  133. * The register A receives the value in register B plus
  134. * the value in register C, modulo 2^32.
  135. */
  136. case 3:
  137. REGA(cur) = REGB(cur) + REGC(cur);
  138. break;
  139. /* #4. Multiplication.
  140. *
  141. * The register A receives the value in register B times
  142. * the value in register C, modulo 2^32.
  143. */
  144. case 4:
  145. REGA(cur) = REGB(cur) * REGC(cur);
  146. break;
  147. /* #5. Division.
  148. *
  149. * The register A receives the value in register B
  150. * divided by the value in register C, if any, where
  151. * each quantity is treated treated as an unsigned 32
  152. * bit number.
  153. */
  154. case 5:
  155. REGA(cur) = REGB(cur) / REGC(cur);
  156. break;
  157. /* #6. Not-And.
  158. *
  159. * Each bit in the register A receives the 1 bit if
  160. * either register B or register C has a 0 bit in that
  161. * position. Otherwise the bit in register A receives
  162. * the 0 bit.
  163. */
  164. case 6:
  165. REGA(cur) = ~ (REGB(cur) & REGC(cur));
  166. break;
  167. /* #7. Halt.
  168. *
  169. * The universal machine stops computation.
  170. */
  171. case 7:
  172. exit(0);
  173. break;
  174. /* #8. Allocation.
  175. *
  176. * A new array is created with a capacity of platters
  177. * commensurate to the value in the register C. This
  178. * new array is initialized entirely with platters
  179. * holding the value 0. A bit pattern not consisting of
  180. * exclusively the 0 bit, and that identifies no other
  181. * active allocated array, is placed in the B register.
  182. */
  183. case 8:
  184. for (i = lastloc+1;
  185. arrays[i] != NULL && i != lastloc;
  186. i = (i + 1) % MEMSIZ);
  187.  
  188. assert(i != lastloc);
  189. lastloc = i;
  190. arrays[i] = (platter *) calloc(REGC(cur), sizeof(platter));
  191. siz_arrays[i] = REGC(cur);
  192. REGB(cur) = i;
  193. break;
  194. /* #9. Abandonment.
  195. *
  196. * The array identified by the register C is abandoned.
  197. * Future allocations may then reuse that identifier.
  198. */
  199. case 9:
  200. free(arrays[REGC(cur)]);
  201. arrays[REGC(cur)] = 0;
  202. break;
  203. /* #10. Output.
  204. *
  205. * The value in the register C is displayed on the console
  206. * immediately. Only values between and including 0 and 255
  207. * are allowed.
  208. */
  209. case 10:
  210. if (REGC(cur) < 256) {
  211. putchar(REGC(cur));
  212. fflush(stdout);
  213. }
  214. break;
  215. /* #11. Input.
  216. *
  217. * The universal machine waits for input on the console.
  218. * When input arrives, the register C is loaded with the
  219. * input, which must be between and including 0 and 255.
  220. * If the end of input has been signaled, then the
  221. * register C is endowed with a uniform value pattern
  222. * where every place is pregnant with the 1 bit.
  223. */
  224. case 11:
  225. REGC(cur) = getchar();
  226. break;
  227. /* #12. Load Program.
  228. *
  229. * The array identified by the B register is duplicated
  230. * and the duplicate shall replace the '0' array,
  231. * regardless of size. The execution finger is placed
  232. * to indicate the platter of this array that is
  233. * described by the offset given in C, where the value
  234. * 0 denotes the first platter, 1 the second, et
  235. * cetera.
  236. *
  237. * The '0' array shall be the most sublime choice for
  238. * loading, and shall be handled with the utmost
  239. * velocity.
  240. */
  241. case 12:
  242. if (REGB(cur) != 0) {
  243. free(arrays[0]);
  244. arrays[0] = (platter *) calloc(siz_arrays[REGB(cur)],
  245. sizeof(platter));
  246. assert(arrays[0] != 0);
  247. assert(arrays[REGB(cur)] != 0);
  248. memcpy(arrays[0], arrays[REGB(cur)],
  249. siz_arrays[REGB(cur)] * sizeof(platter));
  250. siz_arrays[0] = siz_arrays[REGB(cur)];
  251. }
  252. finger = REGC(cur);
  253. break;
  254. /* #13. Orthography.
  255. *
  256. * The value indicated is loaded into the register A
  257. * forthwith.
  258. */
  259. case 13:
  260. REGA13(cur) = DATA(cur);
  261. break;
  262. /* There aren't opcodes 14 or 15, and it's permissible to just
  263. * fail when that happens.
  264. */
  265. default:
  266. exit(1);
  267. }
  268. }
  269. }
  270.  
  271.  

Hell is other programming languages. -- Sartran
Hell is that programming language! -- Dan
Ordinarily, one would enrich this language with more powerful means of computation. Instead I take a different tack... -- Harmonious Monk