30 std::vector<std::string> available_regs = {
"%r14",
"%r15"};
31 if (!uses_std_module) {
32 available_regs.push_back(
"%r12");
33 available_regs.push_back(
"%r13");
36 std::unordered_map<std::string, int> usage_count;
37 auto countOp = [&](
const Operand &op) {
46 for (
const auto &instr :
inc) {
50 for (
const auto &vop : instr.vop)
54 std::vector<std::pair<std::string, int>> sorted_vars(usage_count.begin(), usage_count.end());
55 std::sort(sorted_vars.begin(), sorted_vars.end(),
56 [](
const auto &a,
const auto &b) { return a.second > b.second; });
58 size_t n = std::min(available_regs.size(), sorted_vars.size());
59 for (
size_t i = 0; i < n; ++i) {
60 if (sorted_vars[i].second >= 2) {
69 out <<
"\tmovq " << reg <<
", " <<
getMangledName(var) <<
"(%rip)\n";
75 out <<
"\tmovq " <<
getMangledName(var) <<
"(%rip), " << reg <<
"\n";
81 out <<
"\tpushq " << reg <<
"\n";
87 out <<
"\tpopq " << *it <<
"\n";
94 if (srcReg != it->second)
95 out <<
"\tmovq " << srcReg <<
", " << it->second <<
"\n";
97 out <<
"\tmovq " << srcReg <<
", " <<
getMangledName(dest) <<
"(%rip)\n";
104 out <<
"\tmovq $" << imm <<
", " << it->second <<
"\n";
106 out <<
"\tmovq $" << imm <<
", " <<
getMangledName(dest) <<
"(%rip)\n";
113 if (dstReg != it->second)
114 out <<
"\tmovq " << it->second <<
", " << dstReg <<
"\n";
116 out <<
"\tmovq " <<
getMangledName(src) <<
"(%rip), " << dstReg <<
"\n";
128 std::unordered_map<int, std::string> labels_;
130 labels_[l.second.first] = l.first;
133 bool uses_std_module =
false;
135 if (e.mod ==
"std") {
136 uses_std_module =
true;
142 out <<
".section __DATA, __data\n";
144 out <<
".section .data\n";
146 std::vector<std::string> var_names;
147 for (
auto &v :
vars) {
148 var_names.push_back(v.first);
150 std::sort(var_names.begin(), var_names.end());
152 for (
auto &v : var_names) {
156 out <<
"\t" << var_out_name <<
": .quad " <<
vars[v].var_value.int_value <<
"\n";
160 out <<
"\t" << var_out_name <<
": .double " <<
vars[v].var_value.float_value <<
"\n";
164 out <<
"\t" << var_out_name <<
": .byte " <<
vars[v].var_value.int_value <<
"\n";
167 for (
auto &v : var_names) {
177 for (
auto &v : var_names) {
181 out <<
"\t" << var_out_name <<
": .asciz " <<
"\"" <<
escapeNewLines(
vars[v].var_value.str_value) <<
"\"\n";
186 if (
base !=
nullptr) {
188 for (
auto &v : var_names) {
191 if (varx.is_global ||
object) {
206 out <<
".section __DATA,__bss\n";
208 out <<
".section .bss\n";
210 for (
auto &v : var_names) {
214 out <<
"\t.comm " << var_out_name <<
", 8\n";
216 out <<
"\t.comm " << var_out_name <<
", " << varx.var_value.buffer_size <<
"\n";
220 for (
auto &v : var_names) {
228 out <<
".section __TEXT, __text\n";
230 out <<
".section .text\n";
232 for (
auto &lbl :
labels) {
233 if (lbl.second.second ==
true) {
234 out <<
"\t.global " <<
name +
"_" + lbl.first <<
"\n";
239 return a.name < b.name;
240 return a.mod < b.mod;
244 if (e.module ==
true)
253 out <<
"\t.p2align 4, 0x90\n";
254 out <<
".global " <<
"main" <<
"\n";
255 out <<
"main" <<
":\n";
256 out <<
"\tpush %rbp\n";
257 out <<
"\tmov %rsp, %rbp\n";
258 out <<
"\tpush %rbx\n";
259 if (uses_std_module) {
260 out <<
"\tpush %r12\n";
261 out <<
"\tpush %r13\n";
262 out <<
"\tsub $8, %rsp\n";
263 out <<
"\tmov %rdi, %r12\n";
264 out <<
"\tmov %rsi, %r13\n";
265 out <<
"\tmovq %rsp, %rbx\n";
266 out <<
"\tandq $-16, %rsp\n";
268 out <<
"\tmovq %rbx, %rsp\n";
270 out <<
"\tsub $8, %rsp\n";
276 bool done_found =
false;
278 for (
size_t i = 0; i <
inc.size(); ++i) {
281 if (l.second.first == i && l.second.second) {
282 out <<
"\t.p2align 4, 0x90\n";
284 out <<
"\tpush %rbp\n";
285 out <<
"\tmov %rsp, %rbp\n";
286 out <<
"\tpush %rbx\n";
287 out <<
"\tsub $8, %rsp\n";
299 if (l.second.first == i && !l.second.second) {
300 out <<
"." << l.first <<
":\n";
308#ifndef __EMSCRIPTEN__
309 if (this->
object ==
false && done_found ==
false)
310 throw mx::Exception(
"Program missing done to signal completion.\n");
313 out <<
"\n\n\n.section .note.GNU-stack,\"\",@progbits\n\n";
315 std::string mainFunc =
" Object";
317 mainFunc =
" Program";
472 throw mx::Exception(
"Invalid or unsupported instruction: " + std::to_string(
static_cast<unsigned int>(i.
instruction)));
476 bool uses_std_module =
false;
478 if (e.mod ==
"std") {
479 uses_std_module =
true;
484 if (uses_std_module && !this->
object) {
486 out <<
"\tmovq %rsp, %rbx\n";
487 out <<
"\tandq $-16, %rsp\n";
489 out <<
"\tmovq %rbx, %rsp\n";
494 out <<
"\txor %eax, %eax\n";
496 if (!this->
object && uses_std_module) {
497 out <<
"\tmovq -24(%rbp), %r13\n";
498 out <<
"\tmovq -16(%rbp), %r12\n";
500 out <<
"\tmovq -8(%rbp), %rbx\n";
513 out <<
"\tmovq -8(%rbp), %rbx\n";
542 out <<
"\tnegq %rcx\n";
546 out <<
"\txorpd %xmm1, %xmm1\n";
547 out <<
"\tsubsd %xmm0, %xmm1\n";
575 if (i.
op1.
op.empty()) {
576 throw mx::Exception(
"invoke instruction requires operand of instruction name");
578 std::vector<Operand> op;
580 if (!i.
op2.
op.empty())
582 if (!i.
op3.
op.empty())
584 for (
const auto &vx : i.
vop) {
592 if (op.empty() || op[0].op.empty()) {
593 throw mx::Exception(
"invoke instruction requires operand of instruction name");
595 std::string
name = op[0].op;
596 std::vector<Operand>
args;
597 for (
size_t i = 1; i < op.size(); ++i) {
598 if (!op[i].op.empty())
599 args.push_back(op[i]);
611 const std::vector<Operand> &
args = op;
613 size_t stack_args = (
args.size() > 6) ? (
args.size() - 6) : 0;
615 out <<
"\tmovq %rsp, %rbx\n";
616 out <<
"\tandq $-16, %rsp\n";
619 size_t stack_bytes = stack_args * 8;
620 size_t aligned = (stack_bytes + 15) & ~(
size_t)15;
621 out <<
"\tsubq $" << aligned <<
", %rsp\n";
622 for (
size_t idx = 6, slot = 0; idx <
args.size(); ++idx, ++slot) {
624 out <<
"\tmovq %rax, " << (slot * 8) <<
"(%rsp)\n";
629 for (
size_t z = 0; z <
args.size() && reg_count < 6; ++z, ++reg_count) {
633 out <<
"\txor %eax, %eax\n";
635 out <<
"\tmovq %rbx, %rsp\n";
646 out <<
"\tmovq %rsp, %rbx\n";
647 out <<
"\tandq $-16, %rsp\n";
649 out <<
"\tmovq %rbx, %rsp\n";
668 throw mx::Exception(
"ALLOC destination must be a pointer\n");
671 if (!i.
op2.
op.empty()) {
675 out <<
"\tmovq $" << i.
op2.
op <<
", %rsi\n";
678 out <<
"\tmovq $8, %rsi\n";
681 if (!i.
op3.
op.empty()) {
685 out <<
"\tmovq $" << i.
op3.
op <<
", %rdi\n";
690 out <<
"\tmovq $1, %rdi\n";
692 out <<
"\tmovq %rsp, %rbx\n";
693 out <<
"\tandq $-16, %rsp\n";
695 out <<
"\tmovq %rbx, %rsp\n";
696 out <<
"\ttest %rax, %rax\n";
717 throw mx::Exception(
"REALLOC destination must be a variable");
721 throw mx::Exception(
"REALLOC destination must be a pointer");
730 if (!i.
op3.
op.empty()) {
734 out <<
"\tmovq $" << i.
op3.
op <<
", %rax\n";
737 out <<
"\tmovq $1, %rax\n";
740 if (!i.
op2.
op.empty()) {
744 out <<
"\tmovq $" << i.
op2.
op <<
", %rcx\n";
747 out <<
"\tmovq $8, %rcx\n";
750 out <<
"\timulq %rcx, %rax\n";
751 out <<
"\tmovq %rax, %rsi\n";
752 out <<
"\tmovq %rsp, %rbx\n";
753 out <<
"\tandq $-16, %rsp\n";
755 out <<
"\tmovq %rbx, %rsp\n";
756 out <<
"\ttest %rax, %rax\n";
761 out <<
"\t# realloc failed, keep old pointer\n";
789 if (!i.
op3.
op.empty()) {
793 out <<
"\tmovq $" << i.
op3.
op <<
", %rcx\n";
796 out <<
"\txorq %rcx, %rcx\n";
799 if (!i.
vop.empty() && !i.
vop[0].op.empty()) {
802 out <<
"\timulq %rdx, %rcx\n";
803 out <<
"\taddq %rcx, %rax\n";
809 out <<
"\tmovq (%rax), %rdx\n";
813 out <<
"\tmovsd (%rax), %xmm0\n";
817 out <<
"\tmovzbq (%rax), %rdx\n";
824 size_t stride =
static_cast<size_t>(std::stoll(i.
vop[0].op,
nullptr, 0));
826 if (stride == 1 || stride == 2 || stride == 4 || stride == 8) {
831 out <<
"\tmovq (%rax,%rcx," << stride <<
"), %rdx\n";
835 out <<
"\tmovsd (%rax,%rcx," << stride <<
"), %xmm0\n";
839 out <<
"\tmovzbq (%rax,%rcx," << stride <<
"), %rdx\n";
846 out <<
"\timulq $" << stride <<
", %rcx\n";
847 out <<
"\taddq %rcx, %rax\n";
853 out <<
"\tmovq (%rax), %rdx\n";
857 out <<
"\tmovsd (%rax), %xmm0\n";
861 out <<
"\tmovzbq (%rax), %rdx\n";
874 out <<
"\tmovq (%rax,%rcx,8), %rdx\n";
878 out <<
"\tmovsd (%rax,%rcx,8), %xmm0\n";
882 out <<
"\tmovzbq (%rax,%rcx,8), %rdx\n";
904 throw mx::Exception(
"STORE must target pointer or string buffer");
907 if (!i.
op3.
op.empty()) {
911 out <<
"\tmovq $" << i.
op3.
op <<
", %rcx\n";
914 out <<
"\txorq %rcx, %rcx\n";
938 out <<
"\tmovq $" << i.
op1.
op <<
", %rdx\n";
941 if (!i.
vop.empty() && !i.
vop[0].op.empty()) {
944 out <<
"\timulq %r8, %rcx\n";
945 out <<
"\taddq %rcx, %rax\n";
950 out <<
"\tmovsd %xmm0, (%rax)\n";
952 out <<
"\tmovb %dl, (%rax)\n";
954 out <<
"\tmovq %rdx, (%rax)\n";
957 out <<
"\tmovq %rdx, (%rax)\n";
960 size_t stride =
static_cast<size_t>(std::stoll(i.
vop[0].op,
nullptr, 0));
962 if (stride == 1 || stride == 2 || stride == 4 || stride == 8) {
966 out <<
"\tmovsd %xmm0, (%rax,%rcx," << stride <<
")\n";
968 out <<
"\tmovb %dl, (%rax,%rcx," << stride <<
")\n";
970 out <<
"\tmovq %rdx, (%rax,%rcx," << stride <<
")\n";
973 out <<
"\tmovq %rdx, (%rax,%rcx," << stride <<
")\n";
976 out <<
"\timulq $" << stride <<
", %rcx\n";
977 out <<
"\taddq %rcx, %rax\n";
982 out <<
"\tmovsd %xmm0, (%rax)\n";
984 out <<
"\tmovb %dl, (%rax)\n";
986 out <<
"\tmovq %rdx, (%rax)\n";
989 out <<
"\tmovq %rdx, (%rax)\n";
997 out <<
"\tmovsd %xmm0, (%rax,%rcx,8)\n";
999 out <<
"\tmovb %dl, (%rax,%rcx,8)\n";
1001 out <<
"\tmovq %rdx, (%rax,%rcx,8)\n";
1004 out <<
"\tmovq %rdx, (%rax,%rcx,8)\n";
1023 out <<
"\ttest %rdi, %rdi\n";
1025 out <<
"\tmovq %rsp, %rbx\n";
1026 out <<
"\tandq $-16, %rsp\n";
1028 out <<
"\tmovq %rbx, %rsp\n";
1036 throw mx::Exception(
"to_int requires destination variable (dest int)");
1038 if (i.
op2.
op.empty()) {
1044 throw mx::Exception(
"to_int: first argument must be an integer variable");
1055 out <<
"\tmovq " << ra_src->second <<
", %rax\n";
1070 out <<
"\tcvttsd2si %xmm0, %rax\n";
1076 out <<
"\tmovq %rsp, %rbx\n";
1077 out <<
"\tandq $-16, %rsp\n";
1079 out <<
"\tmovq %rbx, %rsp\n";
1084 throw mx::Exception(
"to_int: unsupported source variable type");
1087 out <<
"\tmovq $" << i.
op2.
op <<
", %rax\n";
1095 throw mx::Exception(
"to_float requires destination variable (dest float)");
1097 if (i.
op2.
op.empty()) {
1103 throw mx::Exception(
"to_float: first argument must be a float variable");
1119 out <<
"\tmovq " << ra_src->second <<
", %rax\n";
1123 out <<
"\tcvtsi2sd %rax, %xmm0\n";
1130 out <<
"\tcvtsi2sd %rax, %xmm0\n";
1136 out <<
"\tmovq %rsp, %rbx\n";
1137 out <<
"\tandq $-16, %rsp\n";
1139 out <<
"\tmovq %rbx, %rsp\n";
1144 throw mx::Exception(
"to_float: unsupported source variable type");
1147 out <<
"\tmovq $" << i.
op2.
op <<
", %rax\n";
1148 out <<
"\tcvtsi2sd %rax, %xmm0\n";
1151 throw mx::Exception(
"to_float: unsupported source operand");
1156 if (i.
op3.
op.empty()) {
1162 out <<
"\tcmpq $0, %rcx\n";
1165 out <<
"\tidivq %rcx\n";
1166 out <<
"\tjmp 2f\n";
1168 out <<
"\txor %eax, %eax\n";
1174 out <<
"\txorpd %xmm2, %xmm2\n";
1175 out <<
"\tucomisd %xmm2, %xmm1\n";
1177 out <<
"\tdivsd %xmm1, %xmm0\n";
1178 out <<
"\tjmp 2f\n";
1180 out <<
"\txorpd %xmm0, %xmm0\n";
1187 throw mx::Exception(
"DIV: first argument must be a variable");
1195 out <<
"\tcmpq $0, %rcx\n";
1198 out <<
"\tidivq %rcx\n";
1199 out <<
"\tjmp 2f\n";
1201 out <<
"\txor %eax, %eax\n";
1207 out <<
"\txorpd %xmm2, %xmm2\n";
1208 out <<
"\tucomisd %xmm2, %xmm1\n";
1210 out <<
"\tdivsd %xmm1, %xmm0\n";
1211 out <<
"\tjmp 2f\n";
1213 out <<
"\txorpd %xmm0, %xmm0\n";
1220 throw mx::Exception(
"DIV: first argument must be a variable");
1226 if (i.
op3.
op.empty()) {
1232 out <<
"\tcmpq $0, %rcx\n";
1235 out <<
"\tidivq %rcx\n";
1236 out <<
"\tjmp 2f\n";
1238 out <<
"\txor %edx, %edx\n";
1245 throw mx::Exception(
"MOD: first argument must be a variable");
1253 out <<
"\tcmpq $0, %rcx\n";
1256 out <<
"\tidivq %rcx\n";
1257 out <<
"\tjmp 2f\n";
1259 out <<
"\txor %edx, %edx\n";
1266 throw mx::Exception(
"MOD: first argument must be a variable");
1273 throw mx::Exception(
"GETLINE destination must be a variable");
1277 throw mx::Exception(
"GETLINE destination must be a string buffer variable");
1279 static int over_count = 0;
1283 out <<
"\tmovq %rsp, %rbx\n";
1284 out <<
"\tandq $-16, %rsp\n";
1286 out <<
"\tmovq %rbx, %rsp\n";
1288 out <<
"\tmovq %rsp, %rbx\n";
1289 out <<
"\tandq $-16, %rsp\n";
1291 out <<
"\tmovq %rbx, %rsp\n";
1292 out <<
"\tmovq %rax, %rcx\n";
1293 out <<
"\tcmp $0, %rax\n";
1294 out <<
"\tje .over" << over_count <<
"\n";
1295 out <<
"\tsub $1, %rcx\n";
1297 out <<
"\tmovb $0, (%rdi, %rcx, 1)\n";
1298 out <<
".over" << over_count <<
": \n";
1303 if (i.
op3.
op.empty()) {
1309 out <<
"\t" << opc <<
"q %rcx, %rax\n";
1312 throw mx::Exception(
"Bitwise operations only supported for integer variables");
1315 throw mx::Exception(
"First argument of bitop instruction must be a variable.");
1323 out <<
"\t" << opc <<
"q %rcx, %rax\n";
1326 throw mx::Exception(
"Bitwise operations only supported for integer variables");
1329 throw mx::Exception(
"First argument of bitop instruction must be a variable.");
1335 if (!i.
op1.
op.empty()) {
1339 throw mx::Exception(
"NOT instruction expect integer variable");
1342 out <<
"\ttestq %rax, %rax\n";
1343 out <<
"\tsete %al\n";
1344 out <<
"\tmovzbq %al, %rax\n";
1370 out <<
"\tpushq %rax\n";
1373 out <<
"\tpushq %rax\n";
1376 out <<
"\tsubq $8, %rsp\n";
1377 out <<
"\tmovsd %xmm0, (%rsp)\n";
1379 throw mx::Exception(
"PUSH only supports integer, pointer, or float variables");
1382 out <<
"\tmovq $" << i.
op1.
op <<
", %rax\n";
1383 out <<
"\tpushq %rax\n";
1385 throw mx::Exception(
"PUSH argument must be a variable or integer constant");
1406 out <<
"\tpopq %rax\n";
1409 out <<
"\tpopq %rax\n";
1412 out <<
"\tmovsd (%rsp), %xmm0\n";
1413 out <<
"\taddq $8, %rsp\n";
1416 throw mx::Exception(
"POP only supports integer, pointer, or float variables");
1422 throw mx::Exception(
"stack_load, requires first argument to be a variable.");
1425 if (i.
op2.
op.empty()) {
1426 throw mx::Exception(
"stack_load, requires second argument");
1429 out <<
"\tmovq (%rsp, %rax, 8), " <<
"%rcx\n";
1435 throw mx::Exception(
"stack_store, requires first argument to be a variable.");
1438 if (i.
op2.
op.empty()) {
1439 throw mx::Exception(
"stack_store, requires second argument");
1443 out <<
"\tmovq %rcx, (%rsp, %rax, 8)\n";
1447 if (!i.
op1.
op.empty()) {
1451 out <<
"\tmovq $" << i.
op1.
op <<
", %rcx\n";
1454 out <<
"\tmovq $1, %rcx\n";
1456 out <<
"\tshl $3, %rcx\n";
1457 out <<
"\taddq %rcx, %rsp\n";
1465 throw mx::Exception(
"Operand expected variable instead I found: " + op.
op);
1476 if (ra->second != reg)
1477 out <<
"\tmovq " << ra->second <<
", " << reg <<
"\n";
1511 throw mx::Exception(
"Operand expected variable instead I found: " + op.
op);
1521 out <<
"\tmovq " << ra->second <<
", %rax\n";
1525 out <<
"\tcvtsi2sd %rax, " << reg <<
"\n";
1529 out <<
"\tcvtsi2sd %rax, " << reg <<
"\n";
1533 out <<
"\tmovq %rsp, %rbx\n";
1534 out <<
"\tandq $-16, %rsp\n";
1536 out <<
"\tmovq %rbx, %rsp\n";
1537 out <<
"\tmovsd %xmm0, " << reg <<
"\n";
1540 throw mx::Exception(
"LoadVar: unsupported source type for float");
1549 if (ra->second != reg)
1550 out <<
"\tmovq " << ra->second <<
", " << reg <<
"\n";
1558 out <<
"\tcvttsd2si %xmm0, " << reg <<
"\n";
1561 out <<
"\tmovq %rsp, %rbx\n";
1562 out <<
"\tandq $-16, %rsp\n";
1564 out <<
"\tmovq %rbx, %rsp\n";
1565 out <<
"\tmovq %rax, " << reg <<
"\n";
1567 throw mx::Exception(
"LoadVar: unsupported source type for integer");
1578 if (ra->second != reg)
1579 out <<
"\tmovq " << ra->second <<
", " << reg <<
"\n";
1584 throw mx::Exception(
"LoadVar: unsupported source type for string");
1595 if (ra->second != reg)
1596 out <<
"\tmovq " << ra->second <<
", " << reg <<
"\n";
1619 out <<
"\tmovq $" << op.
op <<
", " << reg <<
"\n";
1621 out <<
"\tmovq $" << op.
op <<
", %rax\n";
1622 out <<
"\tcvtsi2sd %rax, " << reg <<
"\n";
1625 out <<
"\tmovq $" << op.
op <<
", " << reg <<
"\n";
1627 out <<
"\tmovq $" << op.
op <<
", " << reg <<
"\n";
1636 static const char *reg[] = {
"%rdi",
"%rsi",
"%rdx",
"%rcx",
"%r8",
"%r9"};
1639 return "%xmm" + std::to_string(
xmm_offset++);
1647 std::vector<Operand>
args;
1648 if (!i.
op2.
op.empty())
1650 if (!i.
op3.
op.empty())
1652 for (
const auto &v : i.
vop) {
1658 const size_t stack_args = (
args.size() > 5) ? (
args.size() - 5) : 0;
1660 out <<
"\tmovq %rsp, %rbx\n";
1661 out <<
"\tandq $-16, %rsp\n";
1664 size_t stack_bytes = stack_args * 8;
1665 size_t aligned = (stack_bytes + 15) & ~(
size_t)15;
1666 out <<
"\tsubq $" << aligned <<
", %rsp\n";
1668 for (
size_t idx = 6; idx <=
args.size(); ++idx, ++slot) {
1669 size_t aidx = idx - 1;
1674 out <<
"\tmovsd %xmm0, " << (slot * 8) <<
"(%rsp)\n";
1677 out <<
"\tmovq %rax, " << (slot * 8) <<
"(%rsp)\n";
1680 out <<
"\tmovq $" <<
args[aidx].op <<
", " << (slot * 8) <<
"(%rsp)\n";
1686 for (
size_t z = 0; z <
args.size() && reg_count < 6; ++z, ++reg_count) {
1690 out <<
"\txor %eax, %eax\n";
1692 out <<
"\tmovb $" << total <<
", %al\n";
1695 out <<
"\tmovq %rbx, %rsp\n";
1699 if (!i.
op1.
op.empty()) {
1704 const char *mnem =
nullptr;
1743 out <<
"\t" << mnem <<
" ." << i.
op1.
op <<
"\n";
1750 if (i.
op2.
op.empty()) {
1766 out <<
"\tcomisd %xmm1, %xmm0\n";
1772 out <<
"\tcmpq %rcx, %rax\n";
1777 out <<
"\tcmpq $" << i.
op2.
op <<
", " << ra->second <<
"\n";
1780 out <<
"\tcmpq $" << i.
op2.
op <<
", %rax\n";
1788 out <<
"\tcmpq %rcx, %rax\n";
1794 if (i.
op2.
op.empty()) {
1800 out <<
"\tcomisd %xmm1, %xmm0\n";
1805 out <<
"\tjae ." << i.
op1.
op <<
"\n";
1809 out <<
"\tjbe ." << i.
op1.
op <<
"\n";
1813 out <<
"\tjc ." << i.
op1.
op <<
"\n";
1817 out <<
"\tjnc ." << i.
op1.
op <<
"\n";
1821 out <<
"\tjp ." << i.
op1.
op <<
"\n";
1825 out <<
"\tjnp ." << i.
op1.
op <<
"\n";
1829 out <<
"\tjo ." << i.
op1.
op <<
"\n";
1833 out <<
"\tjno ." << i.
op1.
op <<
"\n";
1837 out <<
"\tjs ." << i.
op1.
op <<
"\n";
1841 out <<
"\tjns ." << i.
op1.
op <<
"\n";
1858 out <<
"\tcvtsi2sd %rax, %xmm0\n";
1861 out <<
"\tmovq %rsp, %rbx\n";
1862 out <<
"\tandq $-16, %rsp\n";
1864 out <<
"\tmovq %rbx, %rsp\n";
1867 out <<
"\tcvtsi2sd %rax, %xmm0\n";
1872 out <<
"\tcvttsd2si %xmm0, %rax\n";
1883 out <<
"\tmovq %rsp, %rbx\n";
1884 out <<
"\tandq $-16, %rsp\n";
1886 out <<
"\tmovq %rbx, %rsp\n";
1891 out <<
"\tcvttsd2si %xmm0, %rax\n";
1900 out <<
"\tcvtsi2sd %rax, %xmm0\n";
1906 switch (dest.
type) {
1928 double val = std::stod(i.
op2.
op);
1930 std::memcpy(&bits, &val,
sizeof(bits));
1931 out <<
"\tmovq $" << bits <<
", %rax\n";
1939 throw mx::Exception(
"MOV: unsupported source operand type");
1951 out <<
"\t" << arth <<
"q $" << rhs.op <<
", " << ra->second <<
"\n";
1953 out <<
"\t" << arth <<
"q $" << rhs.op <<
", " <<
getMangledName(lhs) <<
"(%rip)\n";
1959 out <<
"\t" << arth <<
"q $" << rhs.op <<
", %r10\n";
1962 out <<
"\t" << arth <<
"q %r11, %r10\n";
1967 if (i.
op3.
op.empty()) {
1978 out <<
"\t" << arth <<
"sd %xmm1, %xmm0\n";
1981 throw mx::Exception(
"Unsupported variable type for arithmetic");
1994 out <<
"\t" << arth <<
"sd %xmm1, %xmm0\n";
1997 throw mx::Exception(
"Unsupported variable type for arithmetic");
2003 bool uses_std_module =
false;
2005 if (e.mod ==
"std") {
2006 uses_std_module =
true;
2010 if (uses_std_module) {
2011 out <<
"\tmovq %rsp, %rbx\n";
2012 out <<
"\tandq $-16, %rsp\n";
2014 out <<
"\tmovq %rbx, %rsp\n";
2017 if (!i.
op1.
op.empty()) {
2018 std::vector<Operand> opz;
2019 opz.push_back(i.
op1);
General-purpose exception with errno-aware factory method.
std::unordered_map< std::string, std::pair< uint64_t, bool > > labels
label -> (address, is_function)
std::vector< Instruction > inc
instruction stream
std::unordered_map< std::string, Variable > vars
variable symbol table
static Base * base
pointer to the main program base
static std::string root_name
std::vector< ExternalFunction > external
declared external function imports
void gen_jp(std::ostream &out, const Instruction &i)
void gen_lea(std::ostream &out, const Instruction &i)
Variable & getVariable(const std::string &name)
Look up a variable by name, searching local scope then global.
void sysv_emitSaveRegs(std::ostream &out)
Save callee-saved registers at function prologue.
void sysv_emitFlushRegs(std::ostream &out)
Write all register-allocated variables back to memory.
void gen_cmp(std::ostream &out, const Instruction &i)
std::vector< std::string > sysv_reg_save_order
bool isFunctionValid(const std::string &label)
Check whether a label exists and is marked as a function entry.
void gen_jc(std::ostream &out, const Instruction &i)
void sysv_emitRestoreRegs(std::ostream &out)
Restore callee-saved registers at function epilogue.
void add_standard()
Register standard library runtime functions.
bool isVariable(const std::string &name)
Check whether a variable exists in local or global scope.
void sysv_emitStoreVar(std::ostream &out, const std::string &srcReg, const Operand &dest)
Store a register value into a variable (register-allocated or memory).
void gen_pop(std::ostream &out, const Instruction &i)
Emit x86-64 POP into a variable.
void gen_not(std::ostream &out, const Instruction &i)
void gen_return(std::ostream &out, const Instruction &i)
void gen_jno(std::ostream &out, const Instruction &i)
void gen_realloc(std::ostream &out, const Instruction &i)
Generate System V x86-64 assembly for the REALLOC instruction (dynamic array resize).
void gen_jns(std::ostream &out, const Instruction &i)
std::vector< std::string > args
static std::string escapeNewLines(const std::string &text)
Escape newline characters in a string literal for assembly output.
void gen_call(std::ostream &out, const Instruction &i)
std::string getPlatformSymbolName(const std::string &name)
Mangle a symbol name with the platform prefix (e.g. _name for Darwin).
bool last_call_returns_owned_ptr
tracks ownership of last call's return value
void gen_push(std::ostream &out, const Instruction &i)
Emit x86-64 PUSH for a variable or constant.
void sysv_analyzeRegAlloc(bool uses_std_module)
Analyze variable usage and assign System V callee-saved registers.
std::unordered_map< std::string, std::string > sysv_reg_vars
SysV variable-to-register mapping.
void gen_invoke(std::ostream &out, const Instruction &i)
void sysv_emitLoadVar(std::ostream &out, const std::string &dstReg, const Operand &src)
Load a variable into a destination register.
void gen_done(std::ostream &out, const Instruction &i)
std::string getMangledName(const std::string &var)
Mangle a variable name with its owning object prefix.
void generateCode(const Platform &platform, bool obj, std::ostream &out)
Generate native x64 assembly for the entire program.
LastCmpType last_cmp_type
void gen_to_int(std::ostream &out, const Instruction &i)
void generateInstruction(std::ostream &out, const Instruction &i)
Dispatch a single instruction to its System V code generator.
void gen_stack_load(std::ostream &out, const Instruction &i)
void gen_jnc(std::ostream &out, const Instruction &i)
void gen_mov(std::ostream &out, const Instruction &i)
void gen_mod(std::ostream &out, const Instruction &i)
void gen_jmp(std::ostream &out, const Instruction &i)
void sysv_emitReloadRegs(std::ostream &out)
Reload register-allocated variables from memory.
void gen_arth(std::ostream &out, std::string arth, const Instruction &i)
void gen_jo(std::ostream &out, const Instruction &i)
void gen_free(std::ostream &out, const Instruction &i)
void gen_js(std::ostream &out, const Instruction &i)
void gen_div(std::ostream &out, const Instruction &i)
void gen_jbe(std::ostream &out, const Instruction &i)
void gen_stack_store(std::ostream &out, const Instruction &i)
void gen_jae(std::ostream &out, const Instruction &i)
void generateInvokeCall(std::ostream &out, std::vector< Operand > &op)
Generate a dynamic invoke call (runtime-resolved external function).
void generateFunctionCall(std::ostream &out, const std::string &name, std::vector< Operand > &op)
Generate a System V ABI function call with operands as arguments.
void gen_bitop(std::ostream &out, const std::string &opc, const Instruction &i)
void gen_store(std::ostream &out, const Instruction &i)
void gen_getline(std::ostream &out, const Instruction &i)
int xmm_offset
current XMM register allocation offset
void gen_to_float(std::ostream &out, const Instruction &i)
void gen_stack_sub(std::ostream &out, const Instruction &i)
void gen_alloc(std::ostream &out, const Instruction &i)
void gen_jnp(std::ostream &out, const Instruction &i)
void gen_exit(std::ostream &out, const Instruction &i)
void gen_load(std::ostream &out, const Instruction &i)
std::string getRegisterByIndex(int index, VarType type)
Get the register name for a given index and variable type.
void gen_print(std::ostream &out, const Instruction &i)
void gen_ret(std::ostream &out, const Instruction &i)
void x64_generateCode(const Platform &platform, bool obj, std::ostream &out)
Top-level Win64 code generation entry point.
void gen_fcmp(std::ostream &out, const Instruction &i)
void sysv_emitStoreVarImm(std::ostream &out, const std::string &imm, const Operand &dest)
Store an immediate value into a variable.
void gen_neg(std::ostream &out, const Instruction &i)
int generateLoadVar(std::ostream &out, int reg, const Operand &op)
Load a variable or constant into a numbered register.
int64_t pos(const char *substr, const char *s)
std::string Col(const std::string &col, std::string color)
Wrap a string with ANSI color codes if terminal supports color.
Intermediate code representation, execution engine, and native x64 code generation (SysV + Win64).
@ REALLOC
Reallocate a dynamic memory block: realloc dest, elemSize, count.
const std::string BRIGHT_BLUE
const std::string BRIGHT_CYAN
bool isFunctionReturningOwnedPtr(const std::string &funcName)
Check whether a named function returns an owned (heap-allocated) pointer.
Platform
Target platform for native code generation.
static int error_label_count
VarType
MXVM variable type discriminator.
Describes an external function imported from a module or object.
A complete MXVM instruction with opcode, operands, and optional label.
std::vector< Operand > vop
variable-length operand list (for print, invoke, etc.)
Inc instruction
the opcode
Operand op3
up to three fixed operands
A single instruction operand (constant value or variable reference).
std::string op
textual operand value
uint64_t buffer_size
declared buffer size
bool owns
true if this value owns the allocated memory
A named variable with type, value, and optional object association.