You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

611 lines
24 KiB

  1. #include "molasses/parser_primitives.h"
  2. #include "molasses/generator_primitives.h"
  3. namespace molasses {
  4. namespace unix_system {
  5. #ifdef linux
  6. struct syscall1 {
  7. int64_t operator()(int64_t syscall_id, int64_t p1) const {
  8. int64_t ret;
  9. asm volatile("syscall" : "=a"(ret) : "0"(syscall_id), "D"(p1) : "rcx", "r11", "memory");
  10. return ret;
  11. }
  12. };
  13. struct syscall2 {
  14. int64_t operator()(int64_t syscall_id, int64_t p1, int64_t p2) const {
  15. int64_t ret;
  16. asm volatile("syscall" : "=a"(ret) : "0"(syscall_id), "D"(p1), "S"(p2) : "rcx", "r11", "memory");
  17. return ret;
  18. }
  19. };
  20. struct syscall3 {
  21. int64_t operator()(int64_t syscall_id, int64_t p1, int64_t p2, int64_t p3) const {
  22. int64_t ret;
  23. asm volatile("syscall"
  24. : "=a"(ret)
  25. : "0"(syscall_id), "D"(p1), "S"(p2), "d"(p3)
  26. : "rcx", "r11", "memory");
  27. return ret;
  28. }
  29. };
  30. struct syscall4 {
  31. int64_t operator()(int64_t syscall_id, int64_t p1, int64_t p2, int64_t p3, int64_t p4) const {
  32. int64_t ret;
  33. register long r10 asm("r10") = p4;
  34. asm volatile("syscall"
  35. : "=a"(ret)
  36. : "0"(syscall_id), "D"(p1), "S"(p2), "d"(p3), "r"(r10)
  37. : "rcx", "r11", "memory");
  38. return ret;
  39. }
  40. };
  41. struct syscall5 {
  42. int64_t operator()(int64_t syscall_id, int64_t p1, int64_t p2, int64_t p3, int64_t p4, int64_t p5) const {
  43. int64_t ret;
  44. register long r10 asm("r10") = p4;
  45. register long r8 asm("r8") = p5;
  46. asm volatile("syscall"
  47. : "=a"(ret)
  48. : "0"(syscall_id), "D"(p1), "S"(p2), "d"(p3), "r"(r10), "r"(r8)
  49. : "rcx", "r11", "memory");
  50. return ret;
  51. }
  52. };
  53. struct syscall6 {
  54. int64_t operator()(int64_t syscall_id, int64_t p1, int64_t p2, int64_t p3, int64_t p4, int64_t p5, int64_t p6) const {
  55. int64_t ret;
  56. register long r10 asm("r10") = p4;
  57. register long r8 asm("r8") = p5;
  58. register long r9 asm("r9") = p6;
  59. asm volatile("syscall"
  60. : "=a"(ret)
  61. : "0"(syscall_id), "D"(p1), "S"(p2), "d"(p3), "r"(r10), "r"(r8), "r"(r9)
  62. : "rcx", "r11", "memory");
  63. return ret;
  64. }
  65. };
  66. #endif
  67. }
  68. std::string marshal(const std::string& target) {
  69. std::stringstream builder;
  70. bool is_first = true;
  71. for(char character : target) {
  72. if(isalpha(character)) {
  73. builder << character;
  74. } else if(isdigit(character) and is_first) {
  75. builder << "___" << (int)character << "___";
  76. } else {
  77. builder << "__" << (int)character << "__";
  78. }
  79. is_first = false;
  80. }
  81. return builder.str();
  82. }
  83. std::string escape(const std::string& target) {
  84. std::stringstream builder;
  85. for(char character : target) {
  86. switch(character) {
  87. case 0:
  88. builder << "\\0";
  89. break;
  90. case '\n':
  91. builder << "\\n";
  92. break;
  93. case '"':
  94. builder << "\\\"";
  95. break;
  96. case '\t':
  97. builder << "\\t";
  98. break;
  99. case '\'':
  100. builder << "\\'";
  101. break;
  102. default:
  103. builder << character;
  104. }
  105. }
  106. return builder.str();
  107. }
  108. // TODO: move to a platform independent file
  109. parser_context register_integers(parser_context ctx)
  110. {
  111. ctx.types.push_back(std::make_shared<primitive_type>("i8", 1));
  112. ctx.types.push_back(std::make_shared<primitive_type>("i16", 2));
  113. ctx.types.push_back(std::make_shared<primitive_type>("i32", 4));
  114. ctx.types.push_back(std::make_shared<primitive_type>("i64", 8));
  115. ctx.types.push_back(std::make_shared<primitive_type>("u8", 1));
  116. ctx.types.push_back(std::make_shared<primitive_type>("u16", 2));
  117. ctx.types.push_back(std::make_shared<primitive_type>("u32", 4));
  118. ctx.types.push_back(std::make_shared<primitive_type>("u64", 8));
  119. return ctx;
  120. }
  121. template<>
  122. parser_context register_i32_operations<architecture_t::x86_64_linux>(parser_context ctx) {
  123. ctx.operations.emplace_back(
  124. std::make_shared<molasses::primitive_operation>(
  125. std::string{"+"},
  126. std::vector<std::string>({"i32", "i32"}),
  127. std::vector<std::string>({"i32"}),
  128. std::vector<std::string>({
  129. " popq %rax\n",
  130. " popq %rbx\n",
  131. " addl %ebx, %eax\n",
  132. " andl $0xFFFFFFFF, %eax\n",
  133. " pushq %rax\n"
  134. }),
  135. [](const generate_context&, interpreter_stack& current_stack){
  136. auto value_a = current_stack.top();current_stack.pop();
  137. auto value_b = current_stack.top();current_stack.pop();
  138. if(not std::holds_alternative<int32_t>(value_a) or not std::holds_alternative<int32_t>(value_b)) {
  139. // TODO: handle errors
  140. }
  141. current_stack.emplace(get<int32_t>(value_a) + get<int32_t>(value_b));
  142. }
  143. )
  144. );
  145. ctx.operations.emplace_back(
  146. std::make_shared<molasses::primitive_operation>(
  147. std::string{"+_i64"},
  148. std::vector<std::string>({"i64", "i64"}),
  149. std::vector<std::string>({"i64"}),
  150. std::vector<std::string>({
  151. " popq %rax\n",
  152. " popq %rbx\n",
  153. " addq %rbx, %rax\n",
  154. " pushq %rax\n"
  155. }),
  156. [](const generate_context&, interpreter_stack& current_stack){
  157. auto value_a = current_stack.top();current_stack.pop();
  158. auto value_b = current_stack.top();current_stack.pop();
  159. if(not std::holds_alternative<int64_t>(value_a) or not std::holds_alternative<int64_t>(value_b)) {
  160. // TODO: handle errors
  161. }
  162. current_stack.emplace(get<int64_t>(value_a) + get<int64_t>(value_b));
  163. }
  164. )
  165. );
  166. ctx.operations.emplace_back(
  167. std::make_shared<molasses::primitive_operation>(
  168. std::string{"/%_i64"},
  169. std::vector<std::string>({"i64", "i64"}),
  170. std::vector<std::string>({"i64", "i64"}),
  171. std::vector<std::string>({
  172. " popq %rax\n",
  173. " popq %rbx\n",
  174. " divq %rbx, %rax\n",
  175. " pushq %rax\n",
  176. " pushq %rdx\n"
  177. // TODO: this is actually unsigned division, so it needs improvements on negative numbers
  178. }),
  179. [](const generate_context&, interpreter_stack& current_stack){
  180. auto value_a = current_stack.top();current_stack.pop();
  181. auto value_b = current_stack.top();current_stack.pop();
  182. if(not std::holds_alternative<int64_t>(value_a) or not std::holds_alternative<int64_t>(value_b)) {
  183. // TODO: handle errors
  184. }
  185. current_stack.emplace(get<int64_t>(value_a) / get<int64_t>(value_b));
  186. current_stack.emplace(get<int64_t>(value_a) % get<int64_t>(value_b));
  187. }
  188. )
  189. );
  190. ctx.operations.emplace_back(
  191. std::make_shared<molasses::primitive_operation>(
  192. std::string{"u8-ptr_to_i64"},
  193. std::vector<std::string>({"u8 ptr"}),
  194. std::vector<std::string>({"i64"}),
  195. std::vector<std::string>({
  196. }),
  197. [](const generate_context&, interpreter_stack& current_stack){
  198. auto value = current_stack.top();current_stack.pop();
  199. if(not std::holds_alternative<ptr_type>(value) && not (get<ptr_type>(value).pointed->name() == "u8 ptr")) {
  200. // TODO: handle errors
  201. }
  202. current_stack.emplace(intptr_t(get<ptr_type>(value).ptr));
  203. }
  204. )
  205. );
  206. ctx.operations.emplace_back(
  207. std::make_shared<molasses::primitive_operation>(
  208. std::string{"*"},
  209. std::vector<std::string>({"i32", "i32"}),
  210. std::vector<std::string>({"i32"}),
  211. std::vector<std::string>({
  212. " popq %rax\n",
  213. " popq %rbx\n",
  214. " imull %ebx, %eax\n",
  215. " andl $0xFFFFFFFF, %eax\n",
  216. " pushq %rax\n"
  217. }),
  218. [](const generate_context&, interpreter_stack& current_stack){
  219. auto value_a = current_stack.top();current_stack.pop();
  220. auto value_b = current_stack.top();current_stack.pop();
  221. if(not std::holds_alternative<int32_t>(value_a) or not std::holds_alternative<int32_t>(value_b)) {
  222. // TODO: handle errors
  223. }
  224. current_stack.emplace(get<int32_t>(value_a) * get<int32_t>(value_b));
  225. }
  226. )
  227. );
  228. ctx.operations.emplace_back(
  229. std::make_shared<molasses::primitive_operation>(
  230. std::string{"-"},
  231. std::vector<std::string>({"i32", "i32"}),
  232. std::vector<std::string>({"i32"}),
  233. std::vector<std::string>({
  234. " popq %rax\n",
  235. " popq %rbx\n",
  236. " subl %ebx, %eax\n",
  237. " andl $0xFFFFFFFF, %eax\n",
  238. " pushq %rax\n"
  239. }),
  240. [](const generate_context&, interpreter_stack& current_stack){
  241. auto value_a = current_stack.top();current_stack.pop();
  242. auto value_b = current_stack.top();current_stack.pop();
  243. if(not std::holds_alternative<int32_t>(value_a) or not std::holds_alternative<int32_t>(value_b)) {
  244. // TODO: handle errors
  245. }
  246. current_stack.emplace(get<int32_t>(value_a) - get<int32_t>(value_b));
  247. }
  248. )
  249. );
  250. ctx.operations.emplace_back(
  251. std::make_shared<molasses::primitive_operation>(
  252. std::string{"i32-to-i64"},
  253. std::vector<std::string>({"i32"}),
  254. std::vector<std::string>({"i64"}),
  255. std::vector<std::string>({
  256. " popq %rax\n",
  257. " movslq %eax, %rax\n",
  258. " pushq %rax\n"
  259. }),
  260. [](const generate_context&, interpreter_stack& current_stack){
  261. auto value = current_stack.top();current_stack.pop();
  262. if(not std::holds_alternative<int32_t>(value)) {
  263. // TODO: handle errors
  264. }
  265. current_stack.emplace(int64_t(get<int32_t>(value)));
  266. }
  267. )
  268. );
  269. ctx.operations.emplace_back(
  270. std::make_shared<molasses::primitive_operation>(
  271. std::string{"drop_i64"},
  272. std::vector<std::string>({"i64"}),
  273. std::vector<std::string>({}),
  274. std::vector<std::string>({
  275. " popq %rax\n"
  276. }),
  277. [](const generate_context&, interpreter_stack& current_stack){
  278. auto value = current_stack.top();current_stack.pop();
  279. if(not std::holds_alternative<int64_t>(value)) {
  280. // TODO: handle errors
  281. }
  282. }
  283. )
  284. );
  285. ctx.operations.emplace_back(
  286. std::make_shared<molasses::primitive_operation>(
  287. std::string{"syscall1"},
  288. std::vector<std::string>({"i64", "i64"}),
  289. std::vector<std::string>({"i64"}),
  290. std::vector<std::string>({
  291. " popq %rax\n",
  292. " popq %rdi\n",
  293. " syscall\n",
  294. " pushq %rax\n"
  295. }),
  296. [](const generate_context&, interpreter_stack& current_stack){
  297. auto value_0 = current_stack.top();current_stack.pop();
  298. auto value_1 = current_stack.top();current_stack.pop();
  299. if(
  300. not std::holds_alternative<int64_t>(value_0) or
  301. not std::holds_alternative<int64_t>(value_1)
  302. ) {
  303. // TODO: handle errors
  304. }
  305. #ifdef linux
  306. unix_system::syscall1{}(get<int64_t>(value_0), get<int64_t>(value_1));
  307. #endif
  308. }
  309. )
  310. );
  311. ctx.operations.emplace_back(
  312. std::make_shared<molasses::primitive_operation>(
  313. std::string{"syscall2"},
  314. std::vector<std::string>({"i64", "i64", "i64"}),
  315. std::vector<std::string>({"i64"}),
  316. std::vector<std::string>({
  317. " popq %rax\n",
  318. " popq %rdi\n",
  319. " popq %rsi\n",
  320. " syscall\n",
  321. " pushq %rax\n"
  322. }),
  323. [](const generate_context&, interpreter_stack& current_stack){
  324. auto value_0 = current_stack.top();current_stack.pop();
  325. auto value_1 = current_stack.top();current_stack.pop();
  326. auto value_2 = current_stack.top();current_stack.pop();
  327. if(
  328. not std::holds_alternative<int64_t>(value_0) or
  329. not std::holds_alternative<int64_t>(value_1) or
  330. not std::holds_alternative<int64_t>(value_2)
  331. ) {
  332. // TODO: handle errors
  333. }
  334. #ifdef linux
  335. unix_system::syscall2{}(get<int64_t>(value_0), get<int64_t>(value_1), get<int64_t>(value_2));
  336. #endif
  337. }
  338. )
  339. );
  340. ctx.operations.emplace_back(
  341. std::make_shared<molasses::primitive_operation>(
  342. std::string{"syscall3"},
  343. std::vector<std::string>({"i64", "i64", "i64", "i64"}),
  344. std::vector<std::string>({"i64"}),
  345. std::vector<std::string>({
  346. " popq %rax\n",
  347. " popq %rdi\n",
  348. " popq %rsi\n",
  349. " popq %rdx\n",
  350. " syscall\n",
  351. " pushq %rax\n"
  352. }),
  353. [](const generate_context&, interpreter_stack& current_stack){
  354. auto value_0 = current_stack.top();current_stack.pop();
  355. auto value_1 = current_stack.top();current_stack.pop();
  356. auto value_2 = current_stack.top();current_stack.pop();
  357. auto value_3 = current_stack.top();current_stack.pop();
  358. if(
  359. not std::holds_alternative<int64_t>(value_0) or
  360. not std::holds_alternative<int64_t>(value_1) or
  361. not std::holds_alternative<int64_t>(value_2) or
  362. not std::holds_alternative<int64_t>(value_3)
  363. ) {
  364. // TODO: handle errors
  365. }
  366. #ifdef linux
  367. unix_system::syscall3{}(get<int64_t>(value_0), get<int64_t>(value_1), get<int64_t>(value_2), get<int64_t>(value_3));
  368. #endif
  369. }
  370. )
  371. );
  372. ctx.operations.emplace_back(
  373. std::make_shared<molasses::primitive_operation>(
  374. std::string{"syscall4"},
  375. std::vector<std::string>({"i64", "i64", "i64", "i64", "i64"}),
  376. std::vector<std::string>({"i64"}),
  377. std::vector<std::string>({
  378. " popq %rax\n",
  379. " popq %rdi\n",
  380. " popq %rsi\n",
  381. " popq %rdx\n",
  382. " popq %r10\n",
  383. " syscall\n",
  384. " pushq %rax\n"
  385. }),
  386. [](const generate_context&, interpreter_stack& current_stack){
  387. auto value_0 = current_stack.top();current_stack.pop();
  388. auto value_1 = current_stack.top();current_stack.pop();
  389. auto value_2 = current_stack.top();current_stack.pop();
  390. auto value_3 = current_stack.top();current_stack.pop();
  391. auto value_4 = current_stack.top();current_stack.pop();
  392. if(
  393. not std::holds_alternative<int64_t>(value_0) or
  394. not std::holds_alternative<int64_t>(value_1) or
  395. not std::holds_alternative<int64_t>(value_2) or
  396. not std::holds_alternative<int64_t>(value_3) or
  397. not std::holds_alternative<int64_t>(value_4)
  398. ) {
  399. // TODO: handle errors
  400. }
  401. #ifdef linux
  402. unix_system::syscall4{}(get<int64_t>(value_0), get<int64_t>(value_1), get<int64_t>(value_2), get<int64_t>(value_3), get<int64_t>(value_4));
  403. #endif
  404. }
  405. )
  406. );
  407. ctx.operations.emplace_back(
  408. std::make_shared<molasses::primitive_operation>(
  409. std::string{"syscall5"},
  410. std::vector<std::string>({"i64", "i64", "i64", "i64", "i64", "i64"}),
  411. std::vector<std::string>({"i64"}),
  412. std::vector<std::string>({
  413. " popq %rax\n",
  414. " popq %rdi\n",
  415. " popq %rsi\n",
  416. " popq %rdx\n",
  417. " popq %r10\n",
  418. " popq %r8\n",
  419. " syscall\n",
  420. " pushq %rax\n"
  421. }),
  422. [](const generate_context&, interpreter_stack& current_stack){
  423. auto value_0 = current_stack.top();current_stack.pop();
  424. auto value_1 = current_stack.top();current_stack.pop();
  425. auto value_2 = current_stack.top();current_stack.pop();
  426. auto value_3 = current_stack.top();current_stack.pop();
  427. auto value_4 = current_stack.top();current_stack.pop();
  428. auto value_5 = current_stack.top();current_stack.pop();
  429. if(
  430. not std::holds_alternative<int64_t>(value_0) or
  431. not std::holds_alternative<int64_t>(value_1) or
  432. not std::holds_alternative<int64_t>(value_2) or
  433. not std::holds_alternative<int64_t>(value_3) or
  434. not std::holds_alternative<int64_t>(value_4) or
  435. not std::holds_alternative<int64_t>(value_5)
  436. ) {
  437. // TODO: handle errors
  438. }
  439. #ifdef linux
  440. unix_system::syscall5{}(get<int64_t>(value_0), get<int64_t>(value_1), get<int64_t>(value_2), get<int64_t>(value_3), get<int64_t>(value_4), get<int64_t>(value_5));
  441. #endif
  442. }
  443. )
  444. );
  445. ctx.operations.emplace_back(
  446. std::make_shared<molasses::primitive_operation>(
  447. std::string{"syscall6"},
  448. std::vector<std::string>({"i64", "i64", "i64", "i64", "i64", "i64", "i64"}),
  449. std::vector<std::string>({"i64"}),
  450. std::vector<std::string>({
  451. " popq %rax\n",
  452. " popq %rdi\n",
  453. " popq %rsi\n",
  454. " popq %rdx\n",
  455. " popq %r10\n",
  456. " popq %r8\n",
  457. " popq %r9\n",
  458. " syscall\n",
  459. " pushq %rax\n"
  460. }),
  461. [](const generate_context&, interpreter_stack& current_stack){
  462. auto value_0 = current_stack.top();current_stack.pop();
  463. auto value_1 = current_stack.top();current_stack.pop();
  464. auto value_2 = current_stack.top();current_stack.pop();
  465. auto value_3 = current_stack.top();current_stack.pop();
  466. auto value_4 = current_stack.top();current_stack.pop();
  467. auto value_5 = current_stack.top();current_stack.pop();
  468. auto value_6 = current_stack.top();current_stack.pop();
  469. if(
  470. not std::holds_alternative<int64_t>(value_0) or
  471. not std::holds_alternative<int64_t>(value_1) or
  472. not std::holds_alternative<int64_t>(value_2) or
  473. not std::holds_alternative<int64_t>(value_3) or
  474. not std::holds_alternative<int64_t>(value_4) or
  475. not std::holds_alternative<int64_t>(value_5) or
  476. not std::holds_alternative<int64_t>(value_6)
  477. ) {
  478. // TODO: handle errors
  479. }
  480. #ifdef linux
  481. unix_system::syscall6{}(
  482. get<int64_t>(value_0),
  483. get<int64_t>(value_1),
  484. get<int64_t>(value_2),
  485. get<int64_t>(value_3),
  486. get<int64_t>(value_4),
  487. get<int64_t>(value_5),
  488. get<int64_t>(value_6)
  489. );
  490. #endif
  491. }
  492. )
  493. );
  494. return ctx;
  495. }
  496. template<>
  497. std::vector<std::string> generate_call<architecture_t::x86_64_linux>(const std::string& target) {
  498. return {
  499. " call "+marshal(target)+"\n",
  500. };
  501. }
  502. template<>
  503. std::vector<std::string> generate_string<architecture_t::x86_64_linux>(const symbol& representation, const std::string& string_value) {
  504. return {
  505. "__EMITED_STRING_____"+std::to_string(representation.id)+"___:\n",
  506. " .asciz \""+escape(string_value)+"\"\n",
  507. };
  508. }
  509. template<>
  510. std::vector<std::string> generate_push_string_ptr<architecture_t::x86_64_linux>(const symbol& representation) {
  511. return {
  512. " pushq $__EMITED_STRING_____"+std::to_string(representation.id)+"___\n"
  513. };
  514. }
  515. template<>
  516. std::vector<std::string> generate_push_int32<architecture_t::x86_64_linux>(int32_t target) {
  517. return {
  518. " pushq $" +std::to_string(target)+ "\n"
  519. };
  520. }
  521. template<>
  522. std::vector<std::string> generate_push_int64<architecture_t::x86_64_linux>(int64_t target) {
  523. return {
  524. " pushq $" +std::to_string(target)+ "\n"
  525. };
  526. }
  527. template<>
  528. std::vector<std::string> generate_label<architecture_t::x86_64_linux>(const std::string& target) {
  529. return {
  530. marshal(target)+":\n"
  531. };
  532. }
  533. template<>
  534. std::vector<std::string> generate_return<architecture_t::x86_64_linux>() {
  535. return {
  536. " // Return to caller\n",
  537. " addq $-8, %r10\n",
  538. " pushq (%r10)\n",
  539. " retq\n"
  540. };
  541. }
  542. template<>
  543. std::vector<std::string> generate_enter<architecture_t::x86_64_linux>() {
  544. return {
  545. " // Prepare the function stack\n",
  546. " popq (%r10)\n"
  547. " addq $8, %r10\n",
  548. };
  549. }
  550. template<>
  551. std::vector<std::string> initialize_stack<architecture_t::x86_64_linux>() {
  552. std::vector<std::string> operations = {
  553. "code:\n",
  554. " .skip 1000000\n",
  555. ".text\n",
  556. " .globl _start\n",
  557. "initialize_callstack:\n",
  558. " movq $9, %rax\n",
  559. " movq $0, %rdi\n",
  560. " movq $8192, %rsi\n",
  561. " movq $3, %rdx\n",
  562. " movq $34, %r10\n",
  563. " movq $-1, %r8\n",
  564. " movq $0, %r9\n",
  565. " syscall\n",
  566. " movq %rax, %r10\n",
  567. " retq\n",
  568. "_start:\n",
  569. " call initialize_callstack\n"
  570. };
  571. for(const auto& op : generate_call("main")) {
  572. operations.push_back(op);
  573. }
  574. for(const auto& op : std::vector<std::string>{
  575. " movq $0, %rdi\n",
  576. " movq $60, %rax\n",
  577. " syscall\n"
  578. }
  579. ) {
  580. operations.push_back(op);
  581. }
  582. return operations;
  583. }
  584. }