10#include <unordered_map>
11#include <unordered_set>
23 return "%xmm" + std::to_string(index);
26 static const char *ireg[] = {
"%rcx",
"%rdx",
"%r8",
"%r9"};
33 return s ==
"stdin" || s ==
"stdout" || s ==
"stderr";
36 return s ==
"stdin" ? 0 : (s ==
"stdout" ? 1 : 2);
43 std::vector<std::string> available_regs = {
"%rbx",
"%r14",
"%r15",
"%rsi",
"%rdi"};
44 if (!uses_std_module) {
45 available_regs.push_back(
"%r12");
46 available_regs.push_back(
"%r13");
49 std::unordered_map<std::string, int> usage_count;
50 auto countOp = [&](
const Operand &op) {
59 for (
const auto &instr :
inc) {
63 for (
const auto &vop : instr.vop)
67 std::vector<std::pair<std::string, int>> sorted_vars(usage_count.begin(), usage_count.end());
68 std::sort(sorted_vars.begin(), sorted_vars.end(),
69 [](
const auto &a,
const auto &b) { return a.second > b.second; });
71 size_t n = std::min(available_regs.size(), sorted_vars.size());
72 for (
size_t i = 0; i < n; ++i) {
73 if (sorted_vars[i].second >= 2) {
82 out <<
"\tmovq " << reg <<
", " <<
getMangledName(var) <<
"(%rip)\n";
88 out <<
"\tmovq " <<
getMangledName(var) <<
"(%rip), " << reg <<
"\n";
94 out <<
"\tpushq " << reg <<
"\n";
101 out <<
"\tpopq " << *it <<
"\n";
109 if (srcReg != it->second)
110 out <<
"\tmovq " << srcReg <<
", " << it->second <<
"\n";
112 out <<
"\tmovq " << srcReg <<
", " <<
getMangledName(dest) <<
"(%rip)\n";
119 out <<
"\tmovq $" << imm <<
", " << it->second <<
"\n";
121 out <<
"\tmovq $" << imm <<
", " <<
getMangledName(dest) <<
"(%rip)\n";
128 if (dstReg != it->second)
129 out <<
"\tmovq " << it->second <<
", " << dstReg <<
"\n";
131 out <<
"\tmovq " <<
getMangledName(src) <<
"(%rip), " << dstReg <<
"\n";
136 const size_t need = 32 + spill_bytes;
137 size_t aligned_need = (need + 15) & ~15;
142 out <<
"\tsub $" << aligned_need <<
", %rsp\n";
148 out <<
"\tadd $" << total <<
", %rsp\n";
152 out <<
"\tmov $" << index <<
", %ecx\n";
154 out <<
"\tcall __acrt_iob_func\n";
156 if (dstReg !=
"%rax")
157 out <<
"\tmov %rax, " << dstReg <<
"\n";
176 if (ra->second != reg)
177 out <<
"\tmovq " << ra->second <<
", " << reg <<
"\n";
179 out <<
"\tmovq " <<
getMangledName(op) <<
"(%rip), " << reg <<
"\n";
184 out <<
"\tmovzbq " <<
getMangledName(op) <<
"(%rip), " << reg <<
"\n";
187 out <<
"\tmovsd " <<
getMangledName(op) <<
"(%rip), " << reg <<
"\n";
191 out <<
"\tleaq " <<
getMangledName(op) <<
"(%rip), " << reg <<
"\n";
198 out <<
"\tmovq $" << op.
op <<
", " << reg <<
"\n";
222 if (ra->second != reg)
223 out <<
"\tmovq " << ra->second <<
", " << reg <<
"\n";
225 out <<
"\tmovq " <<
getMangledName(op) <<
"(%rip), " << reg <<
"\n";
230 out <<
"\tmovsd " <<
getMangledName(op) <<
"(%rip), " << reg <<
"\n";
234 out <<
"\tleaq " <<
getMangledName(op) <<
"(%rip), " << reg <<
"\n";
237 out <<
"\tmovzbq " <<
getMangledName(op) <<
"(%rip), " << reg <<
"\n";
250 static const std::unordered_set<std::string> funcs = {
254 return funcs.count(funcName) > 0;
258 const std::string &
name,
259 std::vector<Operand> &
args) {
261 const size_t stack_args = (
args.size() > 4) ? (
args.size() - 4) : 0;
262 const size_t spill_bytes = stack_args * 8;
265 static const char *GPR[4] = {
"%rcx",
"%rdx",
"%r8",
"%r9"};
267 for (
size_t i = 0; i <
args.size(); ++i) {
274 std::string xr =
"%xmm" + std::to_string(i);
280 const size_t off = 32 + 8 * (i - 4);
283 out <<
"\tmovsd %xmm7, " << off <<
"(%rsp)\n";
286 out <<
"\tmovq %rax, " << off <<
"(%rsp)\n";
291 out <<
"\txor %eax, %eax\n";
292 out <<
"\tcall " <<
name <<
"\n";
297 if (op.empty() || op[0].op.empty())
299 std::string n = op[0].op;
300 std::vector<Operand> a;
301 for (
size_t i = 1; i < op.size(); ++i)
302 if (!op[i].op.empty())
316 std::unordered_map<int, std::string> labels_;
318 labels_[l.second.first] = l.first;
320 bool uses_std_module =
false;
322 if (e.mod ==
"std") {
323 uses_std_module =
true;
330 out <<
".section .data\n";
332 std::vector<std::string> var_names;
334 var_names.push_back(v.first);
335 std::sort(var_names.begin(), var_names.end());
337 for (
auto &v : var_names) {
341 out <<
"\t" << nm <<
": .quad " <<
vars[v].var_value.int_value <<
"\n";
345 out <<
"\t" << nm <<
": .double " <<
vars[v].var_value.float_value <<
"\n";
349 out <<
"\t" << nm <<
": .byte " <<
vars[v].var_value.int_value <<
"\n";
353 for (
auto &v : var_names) {
357 out <<
"\t" << nm <<
": .asciz \"" <<
escapeNewLines(
vars[v].var_value.str_value) <<
"\"\n";
361 if (
base !=
nullptr) {
362 for (
auto &v : var_names) {
364 if (varx.is_global ||
object) {
378 out <<
".section .bss\n";
379 for (
auto &v : var_names) {
383 out <<
"\t.comm " << nm <<
", 8\n";
385 out <<
"\t.comm " << nm <<
", " << varx.var_value.buffer_size <<
"\n";
388 for (
auto &v : var_names) {
395 out <<
".section .text\n";
397 out <<
"\t.extern __acrt_iob_func\n";
400 if (lbl.second.second ==
true)
401 out <<
"\t.globl " <<
name +
"_" + lbl.first <<
"\n";
405 return a.name < b.name;
406 return a.mod < b.mod;
409 out <<
"\t.extern " << (e.module ? e.name : e.mod +
"_" + e.name) <<
"\n";
412 out <<
"\t.p2align 4, 0x90\n";
413 out <<
".globl main\n";
415 out <<
"\tpush %rbp\n";
416 out <<
"\tmov %rsp, %rbp\n";
418 if (uses_std_module) {
419 out <<
"\tpush %r12\n";
421 out <<
"\tpush %r13\n";
423 out <<
"\tmov %rcx, %r12\n";
424 out <<
"\tmov %rdx, %r13\n";
426 out <<
"\tcall set_program_args\n";
433 bool done_found =
false;
435 for (
size_t i = 0; i <
inc.size(); ++i) {
438 if (l.second.first == i && l.second.second) {
439 out <<
"\t.p2align 4, 0x90\n";
440 out <<
name +
"_" + l.first <<
":\n";
441 out <<
"\tpush %rbp\n";
442 out <<
"\tmov %rsp, %rbp\n";
455 if (l.second.first == i && !l.second.second)
456 out <<
"." << l.first <<
":\n";
463#ifndef __EMSCRIPTEN__
464 if (this->
object ==
false && done_found ==
false)
465 throw mx::Exception(
"Program missing done to signal completion.\n");
470 std::string mainFunc =
" Object";
472 mainFunc =
" Program";
477 bool uses_std_module =
false;
479 if (e.mod ==
"std") {
480 uses_std_module =
true;
485 if (uses_std_module && !this->
object) {
488 out <<
"\tcall free_program_args\n";
492 out <<
"\txor %eax, %eax\n";
494 if (!this->
object && uses_std_module) {
495 out <<
"\tmovq -8(%rbp), %r12\n";
496 out <<
"\tmovq -16(%rbp), %r13\n";
531 out <<
"\tnegq %rcx\n";
535 out <<
"\txorpd %xmm1, %xmm1\n";
536 out <<
"\tsubsd %xmm0, %xmm1\n";
718 if (i.
op1.
op.empty())
720 std::vector<Operand> op;
722 if (!i.
op2.
op.empty())
724 if (!i.
op3.
op.empty())
726 for (
const auto &vx : i.
vop)
758 if (!i.
op3.
op.empty()) {
762 out <<
"\tmovq $" << i.
op3.
op <<
", %rcx\n";
765 out <<
"\tmovq $1, %rcx\n";
768 if (!i.
op2.
op.empty()) {
772 out <<
"\tmovq $" << i.
op2.
op <<
", %rdx\n";
775 out <<
"\tmovq $8, %rdx\n";
779 out <<
"\tcall calloc\n";
782 out <<
"\ttest %rax, %rax\n";
790 out <<
"\t# Handle allocation failure\n";
806 throw mx::Exception(
"REALLOC destination must be a variable");
809 throw mx::Exception(
"REALLOC destination must be a pointer");
817 if (!i.
op3.
op.empty()) {
821 out <<
"\tmovq $" << i.
op3.
op <<
", %rax\n";
824 out <<
"\tmovq $1, %rax\n";
827 if (!i.
op2.
op.empty()) {
831 out <<
"\tmovq $" << i.
op2.
op <<
", %r8\n";
834 out <<
"\tmovq $8, %r8\n";
837 out <<
"\timulq %r8, %rax\n";
838 out <<
"\tmovq %rax, %rdx\n";
841 out <<
"\tcall realloc\n";
844 out <<
"\ttest %rax, %rax\n";
849 out <<
"\t# realloc failed, keep old pointer\n";
864 out <<
"\ttest %rcx, %rcx\n";
867 out <<
"\tcall free\n";
884 out <<
"\tcvttsd2si %xmm0, %rax\n";
888 out <<
"\tcvtsi2sd %rax, %xmm0\n";
907 out <<
"\tcvtsi2sdq %rax, %xmm0\n";
915 out <<
"\tcvttsd2siq %xmm0, %rax\n";
922 throw mx::Exception(
"MOV: unsupported conversion from float");
939 double val = std::stod(i.
op2.
op);
941 std::memcpy(&bits, &val,
sizeof(bits));
942 out <<
"\tmovq $" << bits <<
", %rax\n";
973 out <<
"\ttest %rax, %rax\n";
976 size_t base_offset = 0;
977 if (!i.
op3.
op.empty()) {
981 base_offset = std::stoll(i.
op3.
op);
982 out <<
"\tmovq $" << base_offset <<
", %rcx\n";
985 out <<
"\txorq %rcx, %rcx\n";
988 if (!i.
vop.empty() && !i.
vop[0].op.empty()) {
991 out <<
"\timulq %r8, %rcx\n";
992 out <<
"\taddq %rcx, %rax\n";
998 out <<
"\tmovq (%rax), %rdx\n";
1002 out <<
"\tmovsd (%rax), %xmm0\n";
1006 out <<
"\tmovzbq (%rax), %rdx\n";
1013 size_t stride =
static_cast<size_t>(std::stoll(i.
vop[0].op,
nullptr, 0));
1015 if (stride == 1 || stride == 2 || stride == 4 || stride == 8) {
1016 switch (dest.
type) {
1020 out <<
"\tmovq (%rax,%rcx," << stride <<
"), %rdx\n";
1024 out <<
"\tmovsd (%rax,%rcx," << stride <<
"), %xmm0\n";
1028 out <<
"\tmovzbq (%rax,%rcx," << stride <<
"), %rdx\n";
1035 out <<
"\timulq $" << stride <<
", %rcx\n";
1036 out <<
"\taddq %rcx, %rax\n";
1038 switch (dest.
type) {
1042 out <<
"\tmovq (%rax), %rdx\n";
1046 out <<
"\tmovsd (%rax), %xmm0\n";
1050 out <<
"\tmovzbq (%rax), %rdx\n";
1059 switch (dest.
type) {
1063 out <<
"\tmovq (%rax,%rcx,8), %rdx\n";
1067 out <<
"\tmovsd (%rax,%rcx,8), %xmm0\n";
1071 out <<
"\tmovzbq (%rax,%rcx,8), %rdx\n";
1081 out <<
"\t# Handle null pointer error\n";
1089 throw mx::Exception(
"STORE destination must be a variable");
1095 throw mx::Exception(
"STORE destination must be a pointer or string buffer");
1106 out <<
"\ttest %rcx, %rcx\n";
1109 size_t base_offset = 0;
1110 if (!i.
op3.
op.empty()) {
1114 base_offset = std::stoll(i.
op3.
op);
1115 out <<
"\tmovq $" << base_offset <<
", %rdx\n";
1118 out <<
"\txorq %rdx, %rdx\n";
1127 src_type = src.
type;
1147 if (i.
op1.
op.find(
'.') != std::string::npos ||
1148 i.
op1.
op.find(
'e') != std::string::npos ||
1149 i.
op1.
op.find(
'E') != std::string::npos) {
1150 double val = std::stod(i.
op1.
op);
1152 memcpy(&bits, &val,
sizeof(bits));
1153 out <<
"\tmovq $" << bits <<
", %rax\n";
1155 out <<
"\tmovq $" << i.
op1.
op <<
", %rax\n";
1159 if (!i.
vop.empty() && !i.
vop[0].op.empty()) {
1162 out <<
"\timulq %r8, %rdx\n";
1163 out <<
"\taddq %rdx, %rcx\n";
1166 out <<
"\tmovsd %xmm0, (%rcx)\n";
1168 out <<
"\tmovb %al, (%rcx)\n";
1170 out <<
"\tmovq %rax, (%rcx)\n";
1172 size_t stride =
static_cast<size_t>(std::stoll(i.
vop[0].op,
nullptr, 0));
1174 if (stride == 1 || stride == 2 || stride == 4 || stride == 8) {
1176 out <<
"\tmovsd %xmm0, (%rcx,%rdx," << stride <<
")\n";
1178 out <<
"\tmovb %al, (%rcx,%rdx," << stride <<
")\n";
1180 out <<
"\tmovq %rax, (%rcx,%rdx," << stride <<
")\n";
1182 out <<
"\timulq $" << stride <<
", %rdx\n";
1183 out <<
"\taddq %rdx, %rcx\n";
1186 out <<
"\tmovsd %xmm0, (%rcx)\n";
1188 out <<
"\tmovb %al, (%rcx)\n";
1190 out <<
"\tmovq %rax, (%rcx)\n";
1194 out <<
"\tshlq $3, %rdx\n";
1195 out <<
"\taddq %rdx, %rcx\n";
1198 out <<
"\tmovsd %xmm0, (%rcx)\n";
1200 out <<
"\tmovb %al, (%rcx)\n";
1202 out <<
"\tmovq %rax, (%rcx)\n";
1207 out <<
"\t# Handle null pointer error\n";
1221 out <<
"\tcall atol\n";
1231 out <<
"\tcvtsi2sdq %rax, %xmm0\n";
1236 if (i.
op3.
op.empty()) {
1242 out <<
"\tcmpq $0, %rcx\n";
1245 out <<
"\tidivq %rcx\n";
1246 out <<
"\tjmp 2f\n";
1248 out <<
"\txor %eax, %eax\n";
1254 out <<
"\txorpd %xmm2, %xmm2\n";
1255 out <<
"\tucomisd %xmm2, %xmm1\n";
1257 out <<
"\tdivsd %xmm1, %xmm0\n";
1258 out <<
"\tjmp 2f\n";
1260 out <<
"\txorpd %xmm0, %xmm0\n";
1273 out <<
"\tcmpq $0, %rcx\n";
1276 out <<
"\tidivq %rcx\n";
1277 out <<
"\tjmp 2f\n";
1279 out <<
"\txor %eax, %eax\n";
1285 out <<
"\txorpd %xmm2, %xmm2\n";
1286 out <<
"\tucomisd %xmm2, %xmm1\n";
1288 out <<
"\tdivsd %xmm1, %xmm0\n";
1289 out <<
"\tjmp 2f\n";
1291 out <<
"\txorpd %xmm0, %xmm0\n";
1302 if (i.
op3.
op.empty()) {
1308 out <<
"\tcmpq $0, %rcx\n";
1311 out <<
"\tidivq %rcx\n";
1312 out <<
"\tjmp 2f\n";
1314 out <<
"\txor %edx, %edx\n";
1327 out <<
"\tcmpq $0, %rcx\n";
1330 out <<
"\tidivq %rcx\n";
1331 out <<
"\tjmp 2f\n";
1333 out <<
"\txor %edx, %edx\n";
1350 out <<
"\txor %ecx, %ecx\n";
1352 out <<
"\tcall __acrt_iob_func\n";
1354 out <<
"\tmov %rax, %r8\n";
1359 out <<
"\tcall fgets\n";
1362 static size_t over_count = 0;
1363 out <<
"\ttest %rax, %rax\n";
1364 out <<
"\tje .over" << over_count <<
"\n";
1368 out <<
"\tcall strlen\n";
1371 out <<
"\tmov %rax, %rcx\n";
1372 out <<
"\tcmp $0, %rax\n";
1373 out <<
"\tje .over" << over_count <<
"\n";
1374 out <<
"\tsub $1, %rcx\n";
1376 out <<
"\tmovb $0, (%rax, %rcx, 1)\n";
1377 out <<
".over" << over_count++ <<
":\n";
1381 if (i.
op3.
op.empty()) {
1387 out <<
"\t" << opc <<
"q %rcx, %rax\n";
1399 out <<
"\t" << opc <<
"q %rcx, %rax\n";
1409 if (!i.
op1.
op.empty()) {
1415 out <<
"\ttestq %rax, %rax\n";
1416 out <<
"\tsete %al\n";
1417 out <<
"\tmovzbq %al, %rax\n";
1430 out <<
"\tpushq %rax\n";
1434 out <<
"\tpushq %rax\n";
1440 out <<
"\tmovq $" << i.
op1.
op <<
", %rax\n";
1441 out <<
"\tpushq %rax\n";
1453 out <<
"\tpopq %rax\n";
1462 if (!i.
op1.
op.empty()) {
1466 out <<
"\tmovq $" << i.
op1.
op <<
", %rcx\n";
1468 out <<
"\tmovq $1, %rcx\n";
1470 out <<
"\tshl $3, %rcx\n";
1471 out <<
"\taddq %rcx, %rsp\n";
1474 unsigned long long n = std::stoull(i.
op1.
op,
nullptr, 0);
1483 if (i.
op2.
op.empty())
1486 out <<
"\tmovq (%rsp, %rax, 8), %rcx\n";
1493 if (i.
op2.
op.empty())
1497 out <<
"\tmovq %rcx, (%rsp, %rax, 8)\n";
1502 std::vector<Operand>
args;
1504 if (!i.
op2.
op.empty())
1506 if (!i.
op3.
op.empty())
1508 for (
const auto &v : i.
vop)
1515 if (!i.
op1.
op.empty()) {
1519 const char *m =
nullptr;
1557 out <<
"\t" << m <<
" ." << i.
op1.
op <<
"\n";
1563 if (i.
op2.
op.empty())
1576 out <<
"\tcvtsi2sdq %rax, %xmm0\n";
1583 out <<
"\tcvtsi2sdq %rax, %xmm1\n";
1586 out <<
"\tcomisd %xmm1, %xmm0\n";
1591 out <<
"\tcmpq %rcx, %rax\n";
1596 out <<
"\tcmpq $" << i.
op2.
op <<
", " << ra->second <<
"\n";
1599 out <<
"\tcmpq $" << i.
op2.
op <<
", %rax\n";
1607 out <<
"\tcmpq %rcx, %rax\n";
1618 out <<
"\t" << arth <<
"q $" << rhs.op <<
", " << ra->second <<
"\n";
1620 out <<
"\t" << arth <<
"q $" << rhs.op <<
", " <<
getMangledName(lhs) <<
"(%rip)\n";
1626 out <<
"\t" << arth <<
"q $" << rhs.op <<
", %rax\n";
1629 out <<
"\t" << arth <<
"q %rcx, %rax\n";
1634 if (i.
op3.
op.empty()) {
1643 out <<
"\t" << arth <<
"sd %xmm1, %xmm0\n";
1656 out <<
"\t" << arth <<
"sd %xmm1, %xmm0\n";
1665 bool uses_std_module =
false;
1667 if (e.mod ==
"std") {
1668 uses_std_module =
true;
1673 if (uses_std_module) {
1674 out <<
"\t# Clean up program arguments before exit\n";
1676 out <<
"\tcall free_program_args\n";
1680 if (!i.
op1.
op.empty()) {
1681 std::vector<Operand> opz;
1682 opz.push_back(i.
op1);
1689 if (i.
op2.
op.empty()) {
1695 out <<
"\tcomisd %xmm1, %xmm0\n";
1700 out <<
"\tjae ." << i.
op1.
op <<
"\n";
1704 out <<
"\tjbe ." << i.
op1.
op <<
"\n";
1708 out <<
"\tjc ." << i.
op1.
op <<
"\n";
1712 out <<
"\tjnc ." << i.
op1.
op <<
"\n";
1716 out <<
"\tjp ." << i.
op1.
op <<
"\n";
1720 out <<
"\tjnp ." << i.
op1.
op <<
"\n";
1724 out <<
"\tjo ." << i.
op1.
op <<
"\n";
1728 out <<
"\tjno ." << i.
op1.
op <<
"\n";
1732 out <<
"\tjs ." << i.
op1.
op <<
"\n";
1736 out <<
"\tjns ." << i.
op1.
op <<
"\n";
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
Variable & getVariable(const std::string &name)
Look up a variable by name, searching local scope then global.
void x64_generateInstruction(std::ostream &out, const Instruction &i)
Dispatch a single instruction to its Win64 code generator.
void x64_gen_load(std::ostream &out, const Instruction &i)
void x64_gen_jae(std::ostream &out, const Instruction &i)
void x64_gen_getline(std::ostream &out, const Instruction &i)
void x64_gen_free(std::ostream &out, const Instruction &i)
void x64_gen_pop(std::ostream &out, const Instruction &i)
void x64_gen_print(std::ostream &out, const Instruction &i)
void x64_gen_jc(std::ostream &out, const Instruction &i)
void x64_gen_neg(std::ostream &out, const Instruction &i)
void x64_emitStoreVar(std::ostream &out, const std::string &srcReg, const Operand &dest)
Store a register value into a variable (register-allocated or memory).
bool isFunctionValid(const std::string &label)
Check whether a label exists and is marked as a function entry.
void x64_emitStoreVarImm(std::ostream &out, const std::string &imm, const Operand &dest)
Store an immediate value into a variable.
void x64_gen_to_float(std::ostream &out, const Instruction &i)
void x64_emitSaveRegs(std::ostream &out)
Save callee-saved registers at function prologue.
int x64_generateLoadVar(std::ostream &out, int r, const Operand &op)
Load a variable or constant into a numbered Win64 register.
void x64_gen_mov(std::ostream &out, const Instruction &i)
void x64_gen_jns(std::ostream &out, const Instruction &i)
void x64_emit_iob_func(std::ostream &out, int index, const std::string &dstReg)
Emit an integer-or-byte function argument load for Win64 ABI.
void add_standard()
Register standard library runtime functions.
void x64_gen_ret(std::ostream &out, const Instruction &i)
bool isVariable(const std::string &name)
Check whether a variable exists in local or global scope.
void x64_gen_cmp(std::ostream &out, const Instruction &i)
void x64_gen_jp(std::ostream &out, const Instruction &i)
std::unordered_map< std::string, std::string > x64_reg_vars
Win64 variable-to-register mapping.
void x64_release_call_area(std::ostream &out, size_t frame)
Release a Win64 call frame previously reserved.
void x64_gen_stack_store(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.
bool last_call_returns_owned_ptr
tracks ownership of last call's return value
void x64_gen_to_int(std::ostream &out, const Instruction &i)
void x64_gen_not(std::ostream &out, const Instruction &i)
std::string getMangledName(const std::string &var)
Mangle a variable name with its owning object prefix.
LastCmpType last_cmp_type
void x64_gen_jno(std::ostream &out, const Instruction &i)
void x64_gen_return(std::ostream &out, const Instruction &i)
std::vector< std::string > x64_reg_save_order
void x64_gen_arth(std::ostream &out, std::string arth, const Instruction &i)
void x64_gen_invoke(std::ostream &out, const Instruction &i)
void x64_emitReloadRegs(std::ostream &out)
Reload register-allocated variables from memory.
size_t x64_reserve_call_area(std::ostream &out, size_t spill_bytes)
Reserve stack space for a Win64 call frame including spill area.
void x64_gen_jnp(std::ostream &out, const Instruction &i)
void x64_emitLoadVar(std::ostream &out, const std::string &dstReg, const Operand &src)
Load a variable into a destination register.
std::string x64_getRegisterByIndex(int index, VarType type)
Get the Win64 register name for a given index and variable type.
void x64_gen_call(std::ostream &out, const Instruction &i)
void x64_gen_alloc(std::ostream &out, const Instruction &i)
void x64_gen_jmp(std::ostream &out, const Instruction &i)
void x64_gen_jnc(std::ostream &out, const Instruction &i)
void x64_generateInvokeCall(std::ostream &out, std::vector< Operand > &op)
Generate a Win64 dynamic invoke call.
void x64_gen_jbe(std::ostream &out, const Instruction &i)
void x64_gen_mod(std::ostream &out, const Instruction &i)
void x64_gen_stack_load(std::ostream &out, const Instruction &i)
void x64_gen_stack_sub(std::ostream &out, const Instruction &i)
void x64_gen_done(std::ostream &out, const Instruction &i)
void x64_gen_fcmp(std::ostream &out, const Instruction &i)
int xmm_offset
current XMM register allocation offset
void x64_gen_div(std::ostream &out, const Instruction &i)
void x64_gen_js(std::ostream &out, const Instruction &i)
void x64_gen_jo(std::ostream &out, const Instruction &i)
void x64_gen_push(std::ostream &out, const Instruction &i)
void x64_gen_exit(std::ostream &out, const Instruction &i)
void x64_gen_bitop(std::ostream &out, const std::string &opc, const Instruction &i)
void x64_emitRestoreRegs(std::ostream &out)
Restore callee-saved registers at function epilogue.
void x64_analyzeRegAlloc(bool uses_std_module)
Analyze variable usage and assign Win64 callee-saved registers.
void x64_gen_realloc(std::ostream &out, const Instruction &i)
Generate Win64 x86-64 assembly for the REALLOC instruction (dynamic array resize).
void x64_generateCode(const Platform &platform, bool obj, std::ostream &out)
Top-level Win64 code generation entry point.
void x64_generateFunctionCall(std::ostream &out, const std::string &name, std::vector< Operand > &args)
Generate a Win64 ABI function call with operands as arguments.
void x64_gen_lea(std::ostream &out, const Instruction &i)
void x64_gen_store(std::ostream &out, const Instruction &i)
void x64_emitFlushRegs(std::ostream &out)
Write all register-allocated variables back to memory.
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
static int stdio_index(const std::string &s)
bool isFunctionReturningOwnedPtr(const std::string &funcName)
Check whether a named function returns an owned (heap-allocated) pointer.
static unsigned x64_sp_mod16
Platform
Target platform for native code generation.
static int error_label_count
VarType
MXVM variable type discriminator.
static bool is_stdio_name(const std::string &s)
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.