MXVM 1.8.1
Virtual Machine, Compiler, and Pascal Frontend
Loading...
Searching...
No Matches
icode_gen_x64.cpp
Go to the documentation of this file.
1
6#include "mxvm/icode.hpp"
7
8#include <algorithm>
9#include <iostream>
10#include <unordered_map>
11#include <unordered_set>
12#include <utility>
13
14namespace mxvm {
15
16 static unsigned x64_sp_mod16 = 0;
17 extern size_t xmm_offset;
18 static int error_label_count = 0;
19
20 std::string Program::x64_getRegisterByIndex(int index, VarType type) {
21 if (type == VarType::VAR_FLOAT) {
22 if (index < 4)
23 return "%xmm" + std::to_string(index);
24 return "[stack]";
25 }
26 static const char *ireg[] = {"%rcx", "%rdx", "%r8", "%r9"};
27 if (index < 4)
28 return ireg[index];
29 return "[stack]";
30 }
31
32 static inline bool is_stdio_name(const std::string &s) {
33 return s == "stdin" || s == "stdout" || s == "stderr";
34 }
35 static inline int stdio_index(const std::string &s) {
36 return s == "stdin" ? 0 : (s == "stdout" ? 1 : 2);
37 }
38
39 void Program::x64_analyzeRegAlloc(bool uses_std_module) {
40 x64_reg_vars.clear();
41 x64_reg_save_order.clear();
42
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");
47 }
48
49 std::unordered_map<std::string, int> usage_count;
50 auto countOp = [&](const Operand &op) {
51 if (!op.op.empty() && isVariable(op.op)) {
52 Variable &v = getVariable(op.op);
53 if (v.type == VarType::VAR_INTEGER && !v.is_global && !is_stdio_name(op.op)) {
54 usage_count[op.op]++;
55 }
56 }
57 };
58
59 for (const auto &instr : inc) {
60 countOp(instr.op1);
61 countOp(instr.op2);
62 countOp(instr.op3);
63 for (const auto &vop : instr.vop)
64 countOp(vop);
65 }
66
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; });
70
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) {
74 x64_reg_vars[sorted_vars[i].first] = available_regs[i];
75 x64_reg_save_order.push_back(available_regs[i]);
76 }
77 }
78 }
79
80 void Program::x64_emitFlushRegs(std::ostream &out) {
81 for (const auto &[var, reg] : x64_reg_vars) {
82 out << "\tmovq " << reg << ", " << getMangledName(var) << "(%rip)\n";
83 }
84 }
85
86 void Program::x64_emitReloadRegs(std::ostream &out) {
87 for (const auto &[var, reg] : x64_reg_vars) {
88 out << "\tmovq " << getMangledName(var) << "(%rip), " << reg << "\n";
89 }
90 }
91
92 void Program::x64_emitSaveRegs(std::ostream &out) {
93 for (const auto &reg : x64_reg_save_order) {
94 out << "\tpushq " << reg << "\n";
95 x64_sp_mod16 ^= 8;
96 }
97 }
98
99 void Program::x64_emitRestoreRegs(std::ostream &out) {
100 for (auto it = x64_reg_save_order.rbegin(); it != x64_reg_save_order.rend(); ++it) {
101 out << "\tpopq " << *it << "\n";
102 x64_sp_mod16 ^= 8;
103 }
104 }
105
106 void Program::x64_emitStoreVar(std::ostream &out, const std::string &srcReg, const Operand &dest) {
107 auto it = x64_reg_vars.find(dest.op);
108 if (it != x64_reg_vars.end()) {
109 if (srcReg != it->second)
110 out << "\tmovq " << srcReg << ", " << it->second << "\n";
111 } else {
112 out << "\tmovq " << srcReg << ", " << getMangledName(dest) << "(%rip)\n";
113 }
114 }
115
116 void Program::x64_emitStoreVarImm(std::ostream &out, const std::string &imm, const Operand &dest) {
117 auto it = x64_reg_vars.find(dest.op);
118 if (it != x64_reg_vars.end()) {
119 out << "\tmovq $" << imm << ", " << it->second << "\n";
120 } else {
121 out << "\tmovq $" << imm << ", " << getMangledName(dest) << "(%rip)\n";
122 }
123 }
124
125 void Program::x64_emitLoadVar(std::ostream &out, const std::string &dstReg, const Operand &src) {
126 auto it = x64_reg_vars.find(src.op);
127 if (it != x64_reg_vars.end()) {
128 if (dstReg != it->second)
129 out << "\tmovq " << it->second << ", " << dstReg << "\n";
130 } else {
131 out << "\tmovq " << getMangledName(src) << "(%rip), " << dstReg << "\n";
132 }
133 }
134
135 size_t Program::x64_reserve_call_area(std::ostream &out, size_t spill_bytes) {
136 const size_t need = 32 + spill_bytes;
137 size_t aligned_need = (need + 15) & ~15;
138 // Ensure RSP will be 16-byte aligned before the call instruction.
139 // If the stack is currently misaligned (x64_sp_mod16 == 8), add 8 more bytes.
140 if ((x64_sp_mod16 + aligned_need) % 16 != 0)
141 aligned_need += 8;
142 out << "\tsub $" << aligned_need << ", %rsp\n";
143 x64_sp_mod16 = (x64_sp_mod16 + aligned_need) % 16;
144 return aligned_need;
145 }
146
147 void Program::x64_release_call_area(std::ostream &out, size_t total) {
148 out << "\tadd $" << total << ", %rsp\n";
149 x64_sp_mod16 = (x64_sp_mod16 + (16 - (total % 16))) % 16;
150 }
151 void Program::x64_emit_iob_func(std::ostream &out, int index, const std::string &dstReg) {
152 out << "\tmov $" << index << ", %ecx\n";
153 size_t total = x64_reserve_call_area(out, 0);
154 out << "\tcall __acrt_iob_func\n";
155 x64_release_call_area(out, total);
156 if (dstReg != "%rax")
157 out << "\tmov %rax, " << dstReg << "\n";
158 }
159
160 int Program::x64_generateLoadVar(std::ostream &out, VarType type, std::string reg, const Operand &op) {
161 int count = 0;
162 if (isVariable(op.op)) {
163 Variable &v = getVariable(op.op);
164
166 x64_emit_iob_func(out, stdio_index(op.op), reg);
167 return 0;
168 }
169
170 switch (v.type) {
173 case VarType::VAR_EXTERN: {
174 auto ra = x64_reg_vars.find(op.op);
175 if (ra != x64_reg_vars.end()) {
176 if (ra->second != reg)
177 out << "\tmovq " << ra->second << ", " << reg << "\n";
178 } else {
179 out << "\tmovq " << getMangledName(op) << "(%rip), " << reg << "\n";
180 }
181 break;
182 }
184 out << "\tmovzbq " << getMangledName(op) << "(%rip), " << reg << "\n";
185 break;
187 out << "\tmovsd " << getMangledName(op) << "(%rip), " << reg << "\n";
188 count = 1;
189 break;
191 out << "\tleaq " << getMangledName(op) << "(%rip), " << reg << "\n";
192 break;
193 default:
194 break;
195 }
196 } else {
198 out << "\tmovq $" << op.op << ", " << reg << "\n";
199 else if (op.type == OperandType::OP_CONSTANT && type == VarType::VAR_FLOAT)
200 throw mx::Exception("Constant to float not supported");
201 }
202 return count;
203 }
204
205 int Program::x64_generateLoadVar(std::ostream &out, int r, const Operand &op) {
206 int count = 0;
207 if (isVariable(op.op)) {
208 Variable &v = getVariable(op.op);
209 std::string reg = x64_getRegisterByIndex(r, v.type);
210
212 x64_emit_iob_func(out, stdio_index(op.op), reg);
213 return 0;
214 }
215
216 switch (v.type) {
219 case VarType::VAR_EXTERN: {
220 auto ra = x64_reg_vars.find(op.op);
221 if (ra != x64_reg_vars.end()) {
222 if (ra->second != reg)
223 out << "\tmovq " << ra->second << ", " << reg << "\n";
224 } else {
225 out << "\tmovq " << getMangledName(op) << "(%rip), " << reg << "\n";
226 }
227 break;
228 }
230 out << "\tmovsd " << getMangledName(op) << "(%rip), " << reg << "\n";
231 count = 1;
232 break;
234 out << "\tleaq " << getMangledName(op) << "(%rip), " << reg << "\n";
235 break;
237 out << "\tmovzbq " << getMangledName(op) << "(%rip), " << reg << "\n";
238 break;
239 default:
240 break;
241 }
242 } else {
244 out << "\tmovq $" << op.op << ", " << x64_getRegisterByIndex(r, VarType::VAR_INTEGER) << "\n";
245 }
246 return count;
247 }
248
249 bool isFunctionReturningOwnedPtr(const std::string &funcName) {
250 static const std::unordered_set<std::string> funcs = {
251 "inttostr",
252 "floattostr",
253 };
254 return funcs.count(funcName) > 0;
255 }
256
257 void Program::x64_generateFunctionCall(std::ostream &out,
258 const std::string &name,
259 std::vector<Operand> &args) {
260 xmm_offset = 0;
261 const size_t stack_args = (args.size() > 4) ? (args.size() - 4) : 0;
262 const size_t spill_bytes = stack_args * 8;
263 size_t frame = x64_reserve_call_area(out, spill_bytes);
264
265 static const char *GPR[4] = {"%rcx", "%rdx", "%r8", "%r9"};
266
267 for (size_t i = 0; i < args.size(); ++i) {
269 if (isVariable(args[i].op))
270 t = getVariable(args[i].op).type;
271
272 if (i < 4) {
273 if (t == VarType::VAR_FLOAT) {
274 std::string xr = "%xmm" + std::to_string(i);
276 } else {
278 }
279 } else {
280 const size_t off = 32 + 8 * (i - 4);
281 if (t == VarType::VAR_FLOAT) {
283 out << "\tmovsd %xmm7, " << off << "(%rsp)\n";
284 } else {
286 out << "\tmovq %rax, " << off << "(%rsp)\n";
287 }
288 }
289 }
290
291 out << "\txor %eax, %eax\n";
292 out << "\tcall " << name << "\n";
293 x64_release_call_area(out, frame);
294 }
295
296 void Program::x64_generateInvokeCall(std::ostream &out, std::vector<Operand> &op) {
297 if (op.empty() || op[0].op.empty())
298 throw mx::Exception("invoke requires instruction name");
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())
303 a.push_back(op[i]);
304
306 this->last_call_returns_owned_ptr = true;
307 } else {
308 this->last_call_returns_owned_ptr = false;
309 }
310
311 x64_generateFunctionCall(out, n, a);
312 }
313
314 void Program::x64_generateCode(const Platform &, bool, std::ostream &out) {
315 this->add_standard();
316 std::unordered_map<int, std::string> labels_;
317 for (auto &l : labels)
318 labels_[l.second.first] = l.first;
319
320 bool uses_std_module = false;
321 for (const auto &e : external) {
322 if (e.mod == "std") {
323 uses_std_module = true;
324 break;
325 }
326 }
327
328 x64_analyzeRegAlloc(uses_std_module);
329
330 out << ".section .data\n";
331
332 std::vector<std::string> var_names;
333 for (auto &v : vars)
334 var_names.push_back(v.first);
335 std::sort(var_names.begin(), var_names.end());
336
337 for (auto &v : var_names) {
338 auto varx = getVariable(v);
339 const std::string nm = getMangledName(v);
340 if (varx.type == VarType::VAR_INTEGER) {
341 out << "\t" << nm << ": .quad " << vars[v].var_value.int_value << "\n";
342 continue;
343 }
344 if (varx.type == VarType::VAR_FLOAT) {
345 out << "\t" << nm << ": .double " << vars[v].var_value.float_value << "\n";
346 continue;
347 }
348 if (varx.type == VarType::VAR_BYTE) {
349 out << "\t" << nm << ": .byte " << vars[v].var_value.int_value << "\n";
350 }
351 }
352
353 for (auto &v : var_names) {
354 auto varx = getVariable(v);
355 if (varx.type == VarType::VAR_STRING && varx.var_value.buffer_size == 0) {
356 const std::string nm = getMangledName(v);
357 out << "\t" << nm << ": .asciz \"" << escapeNewLines(vars[v].var_value.str_value) << "\"\n";
358 }
359 }
360
361 if (base != nullptr) {
362 for (auto &v : var_names) {
363 auto varx = getVariable(v);
364 if (varx.is_global || object) {
365 switch (varx.type) {
369 out << "\t.globl " << getMangledName(v) << "\n";
370 break;
371 default:
372 break;
373 }
374 }
375 }
376 }
377
378 out << ".section .bss\n";
379 for (auto &v : var_names) {
380 const std::string nm = getMangledName(v);
381 auto varx = getVariable(v);
382 if (varx.type == VarType::VAR_POINTER)
383 out << "\t.comm " << nm << ", 8\n";
384 else if (varx.type == VarType::VAR_STRING && varx.var_value.buffer_size > 0)
385 out << "\t.comm " << nm << ", " << varx.var_value.buffer_size << "\n";
386 }
387 if (object) {
388 for (auto &v : var_names) {
389 auto varx = getVariable(v);
390 if (varx.type == VarType::VAR_STRING || varx.type == VarType::VAR_POINTER)
391 out << "\t.globl " << getMangledName(v) << "\n";
392 }
393 }
394
395 out << ".section .text\n";
396
397 out << "\t.extern __acrt_iob_func\n";
398
399 for (auto &lbl : labels)
400 if (lbl.second.second == true)
401 out << "\t.globl " << name + "_" + lbl.first << "\n";
402
403 std::sort(external.begin(), external.end(), [](const ExternalFunction &a, const ExternalFunction &b) {
404 if (a.mod == b.mod)
405 return a.name < b.name;
406 return a.mod < b.mod;
407 });
408 for (auto &e : external)
409 out << "\t.extern " << (e.module ? e.name : e.mod + "_" + e.name) << "\n";
410
411 if (!this->object) {
412 out << "\t.p2align 4, 0x90\n";
413 out << ".globl main\n";
414 out << "main:\n";
415 out << "\tpush %rbp\n";
416 out << "\tmov %rsp, %rbp\n";
417 x64_sp_mod16 = 0;
418 if (uses_std_module) {
419 out << "\tpush %r12\n";
420 x64_sp_mod16 ^= 8;
421 out << "\tpush %r13\n";
422 x64_sp_mod16 ^= 8;
423 out << "\tmov %rcx, %r12\n";
424 out << "\tmov %rdx, %r13\n";
425 size_t total = x64_reserve_call_area(out, 0);
426 out << "\tcall set_program_args\n";
427 x64_release_call_area(out, total);
428 }
429 x64_emitSaveRegs(out);
431 }
432
433 bool done_found = false;
434
435 for (size_t i = 0; i < inc.size(); ++i) {
436 const Instruction &instr = inc[i];
437 for (auto l : labels) {
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";
443 x64_sp_mod16 = 0;
444 x64_emitSaveRegs(out);
446 if (isVariable("rax") && getVariable("rax").type == VarType::VAR_INTEGER) {
447 Operand rax_op;
448 rax_op.op = "rax";
449 x64_emitStoreVar(out, "%rax", rax_op);
450 }
451 break;
452 }
453 }
454 for (auto l : labels) {
455 if (l.second.first == i && !l.second.second)
456 out << "." << l.first << ":\n";
457 }
458 if (instr.instruction == DONE)
459 done_found = true;
460 x64_generateInstruction(out, instr);
461 }
462
463#ifndef __EMSCRIPTEN__
464 if (this->object == false && done_found == false)
465 throw mx::Exception("Program missing done to signal completion.\n");
466#endif
467
468 out << "\n\n";
469
470 std::string mainFunc = " Object";
471 if (root_name == name)
472 mainFunc = " Program";
473 std::cout << Col("MXVM: Compiled: ", mx::Color::BRIGHT_BLUE) << name << ".s" << mainFunc << Col(" platform: ", mx::Color::BRIGHT_CYAN) << "Windows" << "\n";
474 }
475
476 void Program::x64_gen_done(std::ostream &out, const Instruction &) {
477 bool uses_std_module = false;
478 for (const auto &e : external) {
479 if (e.mod == "std") {
480 uses_std_module = true;
481 break;
482 }
483 }
484
485 if (uses_std_module && !this->object) {
487 size_t total = x64_reserve_call_area(out, 0);
488 out << "\tcall free_program_args\n";
489 x64_release_call_area(out, total);
490 }
491
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";
497 }
498 out << "\tleave\n";
499 out << "\tret\n";
500 }
501
502 void Program::x64_gen_ret(std::ostream &out, const Instruction &) {
504 if (isVariable("rax") && getVariable("rax").type == VarType::VAR_INTEGER) {
505 Operand rax_op;
506 rax_op.op = "rax";
507 x64_emitLoadVar(out, "%rax", rax_op);
508 }
510 out << "\tleave\n";
511 out << "\tret\n";
512 }
513
514 void Program::x64_gen_return(std::ostream &out, const Instruction &i) {
515 if (isVariable(i.op1.op)) {
516 if (this->last_call_returns_owned_ptr) {
517 Variable &v = getVariable(i.op1.op);
518 v.var_value.owns = true;
519 this->last_call_returns_owned_ptr = false;
520 }
521 x64_emitStoreVar(out, "%rax", i.op1);
522 } else
523 throw mx::Exception("return requires variable");
524 }
525
526 void Program::x64_gen_neg(std::ostream &out, const Instruction &i) {
527 if (isVariable(i.op1.op)) {
528 Variable &v = getVariable(i.op1.op);
529 if (v.type == VarType::VAR_INTEGER) {
531 out << "\tnegq %rcx\n";
532 x64_emitStoreVar(out, "%rcx", i.op1);
533 } else if (v.type == VarType::VAR_FLOAT) {
535 out << "\txorpd %xmm1, %xmm1\n";
536 out << "\tsubsd %xmm0, %xmm1\n";
537 out << "\tmovsd %xmm1, " << getMangledName(i.op1) << "(%rip)\n";
538 } else
539 throw mx::Exception("neg requires float or integer");
540 } else
541 throw mx::Exception("neg requires variable");
542 }
543
544 void Program::x64_gen_lea(std::ostream &out, const Instruction &i) {
545 if (!isVariable(i.op1.op))
546 throw mx::Exception("LEA destination must be a variable");
547 if (!isVariable(i.op2.op))
548 throw mx::Exception("LEA source must be a variable");
549
550 // Flush source register to memory before taking its address
551 auto it = x64_reg_vars.find(i.op2.op);
552 if (it != x64_reg_vars.end()) {
553 out << "\tmovq " << it->second << ", " << getMangledName(i.op2) << "(%rip)\n";
554 }
555
556 out << "\tleaq " << getMangledName(i.op2) << "(%rip), %rax\n";
557 out << "\tmovq %rax, " << getMangledName(i.op1) << "(%rip)\n";
558 getVariable(i.op1.op).var_value.owns = false;
559 }
560
561 void Program::x64_generateInstruction(std::ostream &out, const Instruction &i) {
562
563 if (i.instruction < JMP || i.instruction > JNS) {
565 }
566 switch (i.instruction) {
567 case ADD:
568 x64_gen_arth(out, "add", i);
569 break;
570 case SUB:
571 x64_gen_arth(out, "sub", i);
572 break;
573 case MUL:
574 x64_gen_arth(out, "mul", i);
575 break;
576 case PRINT:
577 x64_gen_print(out, i);
578 break;
579 case EXIT:
580 x64_gen_exit(out, i);
581 break;
582 case MOV:
583 x64_gen_mov(out, i);
584 break;
585 case JMP:
586 case JE:
587 case JNE:
588 case JL:
589 case JLE:
590 case JG:
591 case JGE:
592 case JZ:
593 case JNZ:
594 case JA:
595 case JB:
596 x64_gen_jmp(out, i);
597 break;
598 case CMP:
599 x64_gen_cmp(out, i);
600 break;
601 case ALLOC:
602 x64_gen_alloc(out, i);
603 break;
604 case FREE:
605 x64_gen_free(out, i);
606 break;
607 case REALLOC:
608 x64_gen_realloc(out, i);
609 break;
610 case LOAD:
611 x64_gen_load(out, i);
612 break;
613 case STORE:
614 x64_gen_store(out, i);
615 break;
616 case RET:
617 x64_gen_ret(out, i);
618 break;
619 case CALL:
620 x64_gen_call(out, i);
621 break;
622 case DONE:
623 x64_gen_done(out, i);
624 break;
625 case AND:
626 x64_gen_bitop(out, "and", i);
627 break;
628 case OR:
629 x64_gen_bitop(out, "or", i);
630 break;
631 case XOR:
632 x64_gen_bitop(out, "xor", i);
633 break;
634 case NOT:
635 x64_gen_not(out, i);
636 break;
637 case DIV:
638 x64_gen_div(out, i);
639 break;
640 case MOD:
641 x64_gen_mod(out, i);
642 break;
643 case PUSH:
644 x64_gen_push(out, i);
645 break;
646 case POP:
647 x64_gen_pop(out, i);
648 break;
649 case STACK_LOAD:
650 x64_gen_stack_load(out, i);
651 break;
652 case STACK_STORE:
653 x64_gen_stack_store(out, i);
654 break;
655 case STACK_SUB:
656 x64_gen_stack_sub(out, i);
657 break;
658 case GETLINE:
659 x64_gen_getline(out, i);
660 break;
661 case TO_INT:
662 x64_gen_to_int(out, i);
663 break;
664 case TO_FLOAT:
665 x64_gen_to_float(out, i);
666 break;
667 case INVOKE:
668 x64_gen_invoke(out, i);
669 break;
670 case RETURN:
671 x64_gen_return(out, i);
672 break;
673 case NEG:
674 x64_gen_neg(out, i);
675 break;
676 case FCMP:
677 x64_gen_fcmp(out, i);
678 break;
679 case JAE:
680 x64_gen_jae(out, i);
681 break;
682 case JBE:
683 x64_gen_jbe(out, i);
684 break;
685 case JC:
686 x64_gen_jc(out, i);
687 break;
688 case JNC:
689 x64_gen_jnc(out, i);
690 break;
691 case JP:
692 x64_gen_jp(out, i);
693 break;
694 case JNP:
695 x64_gen_jnp(out, i);
696 break;
697 case JO:
698 x64_gen_jo(out, i);
699 break;
700 case JNO:
701 x64_gen_jno(out, i);
702 break;
703 case JS:
704 x64_gen_js(out, i);
705 break;
706 case JNS:
707 x64_gen_jns(out, i);
708 break;
709 case LEA:
710 x64_gen_lea(out, i);
711 break;
712 default:
713 throw mx::Exception("Invalid or unsupported instruction");
714 }
715 }
716
717 void Program::x64_gen_invoke(std::ostream &out, const Instruction &i) {
718 if (i.op1.op.empty())
719 throw mx::Exception("invoke requires instruction name");
720 std::vector<Operand> op;
721 op.push_back(i.op1);
722 if (!i.op2.op.empty())
723 op.push_back(i.op2);
724 if (!i.op3.op.empty())
725 op.push_back(i.op3);
726 for (const auto &vx : i.vop)
727 if (!vx.op.empty())
728 op.push_back(vx);
729 x64_generateInvokeCall(out, op);
730 }
731
732 void Program::x64_gen_call(std::ostream &out, const Instruction &i) {
733 if (!isFunctionValid(i.op1.op))
734 throw mx::Exception("Function not found");
736 if (isVariable("rax") && getVariable("rax").type == VarType::VAR_INTEGER) {
737 Operand rax_op;
738 rax_op.op = "rax";
739 x64_emitLoadVar(out, "%rax", rax_op);
740 }
741 size_t total = x64_reserve_call_area(out, 0);
742 out << "\tcall " << getMangledName(i.op1) << "\n";
743 x64_release_call_area(out, total);
744 if (isVariable("rax") && getVariable("rax").type == VarType::VAR_INTEGER) {
745 Operand rax_op;
746 rax_op.op = "rax";
747 out << "\tmovq %rax, " << getMangledName(rax_op) << "(%rip)\n";
748 }
750 }
751 void Program::x64_gen_alloc(std::ostream &out, const Instruction &i) {
752 if (!isVariable(i.op1.op))
753 throw mx::Exception("ALLOC destination must be a variable");
754 Variable &v = getVariable(i.op1.op);
755 if (v.type != VarType::VAR_POINTER)
756 throw mx::Exception("ALLOC destination must be a pointer");
757
758 if (!i.op3.op.empty()) {
759 if (isVariable(i.op3.op)) {
760 x64_emitLoadVar(out, "%rcx", i.op3);
761 } else {
762 out << "\tmovq $" << i.op3.op << ", %rcx\n";
763 }
764 } else {
765 out << "\tmovq $1, %rcx\n";
766 }
767
768 if (!i.op2.op.empty()) {
769 if (isVariable(i.op2.op)) {
770 x64_emitLoadVar(out, "%rdx", i.op2);
771 } else {
772 out << "\tmovq $" << i.op2.op << ", %rdx\n";
773 }
774 } else {
775 out << "\tmovq $8, %rdx\n";
776 }
777
778 size_t total = x64_reserve_call_area(out, 0);
779 out << "\tcall calloc\n";
780 x64_release_call_area(out, total);
781
782 out << "\ttest %rax, %rax\n";
783 out << "\tjz .alloc_failed_" << error_label_count << "\n";
784
785 out << "\tmovq %rax, " << getMangledName(i.op1) << "(%rip)\n";
786 getVariable(i.op1.op).var_value.owns = true;
787
788 out << "\tjmp .alloc_done_" << error_label_count << "\n";
789 out << ".alloc_failed_" << error_label_count << ":\n";
790 out << "\t# Handle allocation failure\n";
791 out << "\tmovq $0, " << getMangledName(i.op1) << "(%rip)\n";
792 out << ".alloc_done_" << error_label_count << ":\n";
793
795 }
796
804 void Program::x64_gen_realloc(std::ostream &out, const Instruction &i) {
805 if (!isVariable(i.op1.op))
806 throw mx::Exception("REALLOC destination must be a variable");
807 Variable &v = getVariable(i.op1.op);
808 if (v.type != VarType::VAR_POINTER)
809 throw mx::Exception("REALLOC destination must be a pointer");
810
812
813 // %rcx = existing ptr (first arg to realloc on Win x64)
814 out << "\tmovq " << getMangledName(i.op1) << "(%rip), %rcx\n";
815
816 // Compute new total size = count * elemSize
817 if (!i.op3.op.empty()) {
818 if (isVariable(i.op3.op)) {
819 x64_emitLoadVar(out, "%rax", i.op3);
820 } else {
821 out << "\tmovq $" << i.op3.op << ", %rax\n";
822 }
823 } else {
824 out << "\tmovq $1, %rax\n";
825 }
826
827 if (!i.op2.op.empty()) {
828 if (isVariable(i.op2.op)) {
829 x64_emitLoadVar(out, "%r8", i.op2);
830 } else {
831 out << "\tmovq $" << i.op2.op << ", %r8\n";
832 }
833 } else {
834 out << "\tmovq $8, %r8\n";
835 }
836
837 out << "\timulq %r8, %rax\n";
838 out << "\tmovq %rax, %rdx\n"; // %rdx = new size (second arg)
839
840 size_t total = x64_reserve_call_area(out, 0);
841 out << "\tcall realloc\n";
842 x64_release_call_area(out, total);
843
844 out << "\ttest %rax, %rax\n";
845 out << "\tjz .realloc_failed_" << error_label_count << "\n";
846 out << "\tmovq %rax, " << getMangledName(i.op1) << "(%rip)\n";
847 out << "\tjmp .realloc_done_" << error_label_count << "\n";
848 out << ".realloc_failed_" << error_label_count << ":\n";
849 out << "\t# realloc failed, keep old pointer\n";
850 out << ".realloc_done_" << error_label_count << ":\n";
852 v.var_value.owns = true;
854 }
855
856 void Program::x64_gen_free(std::ostream &out, const Instruction &i) {
857 if (!isVariable(i.op1.op))
858 throw mx::Exception("FREE argument must be a variable");
859 Variable &v = getVariable(i.op1.op);
860 if (v.type != VarType::VAR_POINTER)
861 throw mx::Exception("FREE argument must be a pointer");
862
863 out << "\tmovq " << getMangledName(i.op1) << "(%rip), %rcx\n";
864 out << "\ttest %rcx, %rcx\n";
865 out << "\tjz 1f\n";
866 size_t total = x64_reserve_call_area(out, 0);
867 out << "\tcall free\n";
868 x64_release_call_area(out, total);
869 out << "\tmovq $0, " << getMangledName(i.op1) << "(%rip)\n";
870 out << "1:\n";
871 v.var_value.owns = false;
872 }
873
874 void Program::x64_gen_mov(std::ostream &out, const Instruction &i) {
875 if (!isVariable(i.op1.op)) {
876 throw mx::Exception("MOV first operand must be a variable");
877 }
878 Variable &dest = getVariable(i.op1.op);
879
880 if (isVariable(i.op2.op)) {
881 Variable &src = getVariable(i.op2.op);
882 if (dest.type == VarType::VAR_INTEGER && src.type == VarType::VAR_FLOAT) {
883 out << "\tmovsd " << getMangledName(i.op2) << "(%rip), %xmm0\n";
884 out << "\tcvttsd2si %xmm0, %rax\n";
885 x64_emitStoreVar(out, "%rax", i.op1);
886 } else if (dest.type == VarType::VAR_FLOAT && src.type == VarType::VAR_INTEGER) {
887 x64_emitLoadVar(out, "%rax", i.op2);
888 out << "\tcvtsi2sd %rax, %xmm0\n";
889 out << "\tmovsd %xmm0, " << getMangledName(i.op1) << "(%rip)\n";
890 } else if (dest.type == src.type) {
891
892 if (dest.type == VarType::VAR_FLOAT) {
894 out << "\tmovsd %xmm0, " << getMangledName(i.op1) << "(%rip)\n";
895 } else if (dest.type == VarType::VAR_BYTE) {
897 out << "\tmovb %al, " << getMangledName(i.op1) << "(%rip)\n";
898 } else {
899 x64_generateLoadVar(out, dest.type, "%rax", i.op2);
900 x64_emitStoreVar(out, "%rax", i.op1);
901 }
902 } else {
903
904 if (dest.type == VarType::VAR_FLOAT) {
905 if (src.type == VarType::VAR_INTEGER || src.type == VarType::VAR_BYTE) {
906 x64_generateLoadVar(out, src.type, "%rax", i.op2);
907 out << "\tcvtsi2sdq %rax, %xmm0\n";
908 out << "\tmovsd %xmm0, " << getMangledName(i.op1) << "(%rip)\n";
909 } else {
910 throw mx::Exception("MOV: unsupported conversion to float");
911 }
912 } else if (src.type == VarType::VAR_FLOAT) {
913 if (dest.type == VarType::VAR_INTEGER || dest.type == VarType::VAR_BYTE) {
915 out << "\tcvttsd2siq %xmm0, %rax\n";
916 if (dest.type == VarType::VAR_BYTE) {
917 out << "\tmovb %al, " << getMangledName(i.op1) << "(%rip)\n";
918 } else {
919 x64_emitStoreVar(out, "%rax", i.op1);
920 }
921 } else {
922 throw mx::Exception("MOV: unsupported conversion from float");
923 }
924 } else if (dest.type == VarType::VAR_POINTER && src.type == VarType::VAR_STRING) {
925 out << "\tleaq " << getMangledName(i.op2.op) << "(%rip), %rax\n";
926 out << "\tmovq %rax, " << getMangledName(i.op1.op) << "(%rip)\n";
927 getVariable(i.op1.op).var_value.owns = false;
928 } else {
929 x64_generateLoadVar(out, src.type, "%rax", i.op2);
930 if (dest.type == VarType::VAR_BYTE) {
931 out << "\tmovb %al, " << getMangledName(i.op1) << "(%rip)\n";
932 } else {
933 x64_emitStoreVar(out, "%rax", i.op1);
934 }
935 }
936 }
937 } else {
938 if (dest.type == VarType::VAR_FLOAT) {
939 double val = std::stod(i.op2.op);
940 uint64_t bits;
941 std::memcpy(&bits, &val, sizeof(bits));
942 out << "\tmovq $" << bits << ", %rax\n";
943 out << "\tmovq %rax, " << getMangledName(i.op1) << "(%rip)\n";
944 } else if (dest.type == VarType::VAR_BYTE) {
945 out << "\tmovb $" << i.op2.op << ", " << getMangledName(i.op1) << "(%rip)\n";
946 } else {
947 x64_emitStoreVarImm(out, i.op2.op, i.op1);
948 }
949 }
950 }
951
952 void Program::x64_gen_load(std::ostream &out, const Instruction &i) {
953 if (!isVariable(i.op1.op))
954 throw mx::Exception("LOAD dest must be variable");
955 Variable &dest = getVariable(i.op1.op);
956 if (!isVariable(i.op2.op))
957 throw mx::Exception("LOAD source must be pointer variable");
958 Variable &ptrVar = getVariable(i.op2.op);
959
960 if (ptrVar.type != VarType::VAR_POINTER &&
961 !(ptrVar.type == VarType::VAR_STRING && ptrVar.var_value.buffer_size > 0)) {
962 throw mx::Exception("LOAD: invalid base type");
963 }
964
965 if (ptrVar.type == VarType::VAR_POINTER) {
966 // Flush all cached registers so pointer reads see current values
968 out << "\tmovq " << getMangledName(i.op2) << "(%rip), %rax\n";
969 } else {
970 out << "\tleaq " << getMangledName(i.op2) << "(%rip), %rax\n";
971 }
972
973 out << "\ttest %rax, %rax\n";
974 out << "\tjz .null_ptr_error_" << error_label_count << "\n";
975
976 size_t base_offset = 0;
977 if (!i.op3.op.empty()) {
978 if (isVariable(i.op3.op)) {
979 x64_emitLoadVar(out, "%rcx", i.op3);
980 } else {
981 base_offset = std::stoll(i.op3.op);
982 out << "\tmovq $" << base_offset << ", %rcx\n";
983 }
984 } else {
985 out << "\txorq %rcx, %rcx\n";
986 }
987
988 if (!i.vop.empty() && !i.vop[0].op.empty()) {
989 if (isVariable(i.vop[0].op)) {
990 x64_emitLoadVar(out, "%r8", i.vop[0]);
991 out << "\timulq %r8, %rcx\n";
992 out << "\taddq %rcx, %rax\n";
993
994 switch (dest.type) {
998 out << "\tmovq (%rax), %rdx\n";
999 x64_emitStoreVar(out, "%rdx", i.op1);
1000 break;
1001 case VarType::VAR_FLOAT:
1002 out << "\tmovsd (%rax), %xmm0\n";
1003 out << "\tmovsd %xmm0, " << getMangledName(i.op1) << "(%rip)\n";
1004 break;
1005 case VarType::VAR_BYTE:
1006 out << "\tmovzbq (%rax), %rdx\n";
1007 out << "\tmovb %dl, " << getMangledName(i.op1) << "(%rip)\n";
1008 break;
1009 default:
1010 throw mx::Exception("LOAD: unsupported destination type");
1011 }
1012 } else {
1013 size_t stride = static_cast<size_t>(std::stoll(i.vop[0].op, nullptr, 0));
1014
1015 if (stride == 1 || stride == 2 || stride == 4 || stride == 8) {
1016 switch (dest.type) {
1020 out << "\tmovq (%rax,%rcx," << stride << "), %rdx\n";
1021 x64_emitStoreVar(out, "%rdx", i.op1);
1022 break;
1023 case VarType::VAR_FLOAT:
1024 out << "\tmovsd (%rax,%rcx," << stride << "), %xmm0\n";
1025 out << "\tmovsd %xmm0, " << getMangledName(i.op1) << "(%rip)\n";
1026 break;
1027 case VarType::VAR_BYTE:
1028 out << "\tmovzbq (%rax,%rcx," << stride << "), %rdx\n";
1029 out << "\tmovb %dl, " << getMangledName(i.op1) << "(%rip)\n";
1030 break;
1031 default:
1032 throw mx::Exception("LOAD: unsupported destination type");
1033 }
1034 } else {
1035 out << "\timulq $" << stride << ", %rcx\n";
1036 out << "\taddq %rcx, %rax\n";
1037
1038 switch (dest.type) {
1042 out << "\tmovq (%rax), %rdx\n";
1043 x64_emitStoreVar(out, "%rdx", i.op1);
1044 break;
1045 case VarType::VAR_FLOAT:
1046 out << "\tmovsd (%rax), %xmm0\n";
1047 out << "\tmovsd %xmm0, " << getMangledName(i.op1) << "(%rip)\n";
1048 break;
1049 case VarType::VAR_BYTE:
1050 out << "\tmovzbq (%rax), %rdx\n";
1051 out << "\tmovb %dl, " << getMangledName(i.op1) << "(%rip)\n";
1052 break;
1053 default:
1054 throw mx::Exception("LOAD: unsupported destination type");
1055 }
1056 }
1057 }
1058 } else {
1059 switch (dest.type) {
1063 out << "\tmovq (%rax,%rcx,8), %rdx\n";
1064 x64_emitStoreVar(out, "%rdx", i.op1);
1065 break;
1066 case VarType::VAR_FLOAT:
1067 out << "\tmovsd (%rax,%rcx,8), %xmm0\n";
1068 out << "\tmovsd %xmm0, " << getMangledName(i.op1) << "(%rip)\n";
1069 break;
1070 case VarType::VAR_BYTE:
1071 out << "\tmovzbq (%rax,%rcx,8), %rdx\n";
1072 out << "\tmovb %dl, " << getMangledName(i.op1) << "(%rip)\n";
1073 break;
1074 default:
1075 throw mx::Exception("LOAD: unsupported destination type");
1076 }
1077 }
1078
1079 out << "\tjmp .load_done_" << error_label_count << "\n";
1080 out << ".null_ptr_error_" << error_label_count << ":\n";
1081 out << "\t# Handle null pointer error\n";
1082 out << ".load_done_" << error_label_count << ":\n";
1083
1085 }
1086
1087 void Program::x64_gen_store(std::ostream &out, const Instruction &i) {
1088 if (!isVariable(i.op2.op)) {
1089 throw mx::Exception("STORE destination must be a variable");
1090 }
1091
1092 Variable &ptrVar = getVariable(i.op2.op);
1093 if (ptrVar.type != VarType::VAR_POINTER &&
1094 !(ptrVar.type == VarType::VAR_STRING && ptrVar.var_value.buffer_size > 0)) {
1095 throw mx::Exception("STORE destination must be a pointer or string buffer");
1096 }
1097
1098 if (ptrVar.type == VarType::VAR_POINTER) {
1099 // Flush all cached registers before pointer store
1100 x64_emitFlushRegs(out);
1101 out << "\tmovq " << getMangledName(i.op2) << "(%rip), %rcx\n";
1102 } else {
1103 out << "\tleaq " << getMangledName(i.op2) << "(%rip), %rcx\n";
1104 }
1105
1106 out << "\ttest %rcx, %rcx\n";
1107 out << "\tjz .null_ptr_error_" << error_label_count << "\n";
1108
1109 size_t base_offset = 0;
1110 if (!i.op3.op.empty()) {
1111 if (isVariable(i.op3.op)) {
1112 x64_emitLoadVar(out, "%rdx", i.op3);
1113 } else {
1114 base_offset = std::stoll(i.op3.op);
1115 out << "\tmovq $" << base_offset << ", %rdx\n";
1116 }
1117 } else {
1118 out << "\txorq %rdx, %rdx\n";
1119 }
1120
1121 // Load source value into %rax / %xmm0 / %dl before computing final address
1122 // (so we don't clobber %rcx/%rdx during source load)
1123 bool src_is_var = isVariable(i.op1.op);
1124 VarType src_type = VarType::VAR_INTEGER;
1125 if (src_is_var) {
1126 Variable &src = getVariable(i.op1.op);
1127 src_type = src.type;
1128 switch (src.type) {
1132 x64_emitLoadVar(out, "%rax", i.op1);
1133 break;
1135 out << "\tleaq " << getMangledName(i.op1) << "(%rip), %rax\n";
1136 break;
1137 case VarType::VAR_BYTE:
1138 out << "\tmovzbq " << getMangledName(i.op1) << "(%rip), %rax\n";
1139 break;
1140 case VarType::VAR_FLOAT:
1141 out << "\tmovsd " << getMangledName(i.op1) << "(%rip), %xmm0\n";
1142 break;
1143 default:
1144 throw mx::Exception("STORE: unsupported source type");
1145 }
1146 } else {
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);
1151 uint64_t bits;
1152 memcpy(&bits, &val, sizeof(bits));
1153 out << "\tmovq $" << bits << ", %rax\n";
1154 } else {
1155 out << "\tmovq $" << i.op1.op << ", %rax\n";
1156 }
1157 }
1158
1159 if (!i.vop.empty() && !i.vop[0].op.empty()) {
1160 if (isVariable(i.vop[0].op)) {
1161 x64_emitLoadVar(out, "%r8", i.vop[0]);
1162 out << "\timulq %r8, %rdx\n";
1163 out << "\taddq %rdx, %rcx\n";
1164
1165 if (src_is_var && src_type == VarType::VAR_FLOAT)
1166 out << "\tmovsd %xmm0, (%rcx)\n";
1167 else if (src_is_var && src_type == VarType::VAR_BYTE)
1168 out << "\tmovb %al, (%rcx)\n";
1169 else
1170 out << "\tmovq %rax, (%rcx)\n";
1171 } else {
1172 size_t stride = static_cast<size_t>(std::stoll(i.vop[0].op, nullptr, 0));
1173
1174 if (stride == 1 || stride == 2 || stride == 4 || stride == 8) {
1175 if (src_is_var && src_type == VarType::VAR_FLOAT)
1176 out << "\tmovsd %xmm0, (%rcx,%rdx," << stride << ")\n";
1177 else if (src_is_var && src_type == VarType::VAR_BYTE)
1178 out << "\tmovb %al, (%rcx,%rdx," << stride << ")\n";
1179 else
1180 out << "\tmovq %rax, (%rcx,%rdx," << stride << ")\n";
1181 } else {
1182 out << "\timulq $" << stride << ", %rdx\n";
1183 out << "\taddq %rdx, %rcx\n";
1184
1185 if (src_is_var && src_type == VarType::VAR_FLOAT)
1186 out << "\tmovsd %xmm0, (%rcx)\n";
1187 else if (src_is_var && src_type == VarType::VAR_BYTE)
1188 out << "\tmovb %al, (%rcx)\n";
1189 else
1190 out << "\tmovq %rax, (%rcx)\n";
1191 }
1192 }
1193 } else {
1194 out << "\tshlq $3, %rdx\n";
1195 out << "\taddq %rdx, %rcx\n";
1196
1197 if (src_is_var && src_type == VarType::VAR_FLOAT)
1198 out << "\tmovsd %xmm0, (%rcx)\n";
1199 else if (src_is_var && src_type == VarType::VAR_BYTE)
1200 out << "\tmovb %al, (%rcx)\n";
1201 else
1202 out << "\tmovq %rax, (%rcx)\n";
1203 }
1204
1205 out << "\tjmp .store_done_" << error_label_count << "\n";
1206 out << ".null_ptr_error_" << error_label_count << ":\n";
1207 out << "\t# Handle null pointer error\n";
1208 out << ".store_done_" << error_label_count << ":\n";
1209
1210 // Reload cached registers after pointer store (memory may have changed)
1211 if (ptrVar.type == VarType::VAR_POINTER) {
1212 x64_emitReloadRegs(out);
1213 }
1214
1216 }
1217
1218 void Program::x64_gen_to_int(std::ostream &out, const Instruction &i) {
1219 out << "\tleaq " << getMangledName(i.op2) << "(%rip), %rcx\n";
1220 size_t total = x64_reserve_call_area(out, 0);
1221 out << "\tcall atol\n";
1222 x64_release_call_area(out, total);
1223 x64_emitStoreVar(out, "%rax", i.op1);
1224 }
1225
1226 void Program::x64_gen_to_float(std::ostream &out, const Instruction &i) {
1227 if (!isVariable(i.op1.op) || !isVariable(i.op2.op)) {
1228 throw mx::Exception("TO_FLOAT requires variable operands");
1229 }
1231 out << "\tcvtsi2sdq %rax, %xmm0\n";
1232 out << "\tmovsd %xmm0, " << getMangledName(i.op1) << "(%rip)\n";
1233 }
1234
1235 void Program::x64_gen_div(std::ostream &out, const Instruction &i) {
1236 if (i.op3.op.empty()) {
1237 if (isVariable(i.op1.op)) {
1238 Variable &v = getVariable(i.op1.op);
1239 if (v.type == VarType::VAR_INTEGER) {
1242 out << "\tcmpq $0, %rcx\n";
1243 out << "\tje 1f\n";
1244 out << "\tcqto\n";
1245 out << "\tidivq %rcx\n";
1246 out << "\tjmp 2f\n";
1247 out << "1:\n";
1248 out << "\txor %eax, %eax\n";
1249 out << "2:\n";
1250 x64_emitStoreVar(out, "%rax", i.op1);
1251 } else if (v.type == VarType::VAR_FLOAT) {
1252 x64_generateLoadVar(out, VarType::VAR_FLOAT, "%xmm0", i.op1);
1253 x64_generateLoadVar(out, VarType::VAR_FLOAT, "%xmm1", i.op2);
1254 out << "\txorpd %xmm2, %xmm2\n";
1255 out << "\tucomisd %xmm2, %xmm1\n";
1256 out << "\tje 1f\n";
1257 out << "\tdivsd %xmm1, %xmm0\n";
1258 out << "\tjmp 2f\n";
1259 out << "1:\n";
1260 out << "\txorpd %xmm0, %xmm0\n";
1261 out << "2:\n";
1262 out << "\tmovsd %xmm0, " << getMangledName(i.op1) << "(%rip)\n";
1263 } else
1264 throw mx::Exception("DIV unsupported type");
1265 } else
1266 throw mx::Exception("DIV: first must be variable");
1267 } else {
1268 if (isVariable(i.op1.op)) {
1269 Variable &v = getVariable(i.op1.op);
1270 if (v.type == VarType::VAR_INTEGER) {
1273 out << "\tcmpq $0, %rcx\n";
1274 out << "\tje 1f\n";
1275 out << "\tcqto\n";
1276 out << "\tidivq %rcx\n";
1277 out << "\tjmp 2f\n";
1278 out << "1:\n";
1279 out << "\txor %eax, %eax\n";
1280 out << "2:\n";
1281 x64_emitStoreVar(out, "%rax", i.op1);
1282 } else if (v.type == VarType::VAR_FLOAT) {
1283 x64_generateLoadVar(out, VarType::VAR_FLOAT, "%xmm0", i.op2);
1284 x64_generateLoadVar(out, VarType::VAR_FLOAT, "%xmm1", i.op3);
1285 out << "\txorpd %xmm2, %xmm2\n";
1286 out << "\tucomisd %xmm2, %xmm1\n";
1287 out << "\tje 1f\n";
1288 out << "\tdivsd %xmm1, %xmm0\n";
1289 out << "\tjmp 2f\n";
1290 out << "1:\n";
1291 out << "\txorpd %xmm0, %xmm0\n";
1292 out << "2:\n";
1293 out << "\tmovsd %xmm0, " << getMangledName(i.op1) << "(%rip)\n";
1294 } else
1295 throw mx::Exception("DIV unsupported type");
1296 } else
1297 throw mx::Exception("DIV: first must be variable");
1298 }
1299 }
1300
1301 void Program::x64_gen_mod(std::ostream &out, const Instruction &i) {
1302 if (i.op3.op.empty()) {
1303 if (isVariable(i.op1.op)) {
1304 Variable &v = getVariable(i.op1.op);
1305 if (v.type == VarType::VAR_INTEGER) {
1308 out << "\tcmpq $0, %rcx\n";
1309 out << "\tje 1f\n";
1310 out << "\tcqto\n";
1311 out << "\tidivq %rcx\n";
1312 out << "\tjmp 2f\n";
1313 out << "1:\n";
1314 out << "\txor %edx, %edx\n";
1315 out << "2:\n";
1316 x64_emitStoreVar(out, "%rdx", i.op1);
1317 } else
1318 throw mx::Exception("MOD int only");
1319 } else
1320 throw mx::Exception("MOD: first must be variable");
1321 } else {
1322 if (isVariable(i.op1.op)) {
1323 Variable &v = getVariable(i.op1.op);
1324 if (v.type == VarType::VAR_INTEGER) {
1327 out << "\tcmpq $0, %rcx\n";
1328 out << "\tje 1f\n";
1329 out << "\tcqto\n";
1330 out << "\tidivq %rcx\n";
1331 out << "\tjmp 2f\n";
1332 out << "1:\n";
1333 out << "\txor %edx, %edx\n";
1334 out << "2:\n";
1335 x64_emitStoreVar(out, "%rdx", i.op1);
1336 } else
1337 throw mx::Exception("MOD int only");
1338 } else
1339 throw mx::Exception("MOD: first must be variable");
1340 }
1341 }
1342
1343 void Program::x64_gen_getline(std::ostream &out, const Instruction &i) {
1344 if (!isVariable(i.op1.op))
1345 throw mx::Exception("GETLINE: dest must be var");
1346 Variable &dest = getVariable(i.op1.op);
1347 if (dest.type != VarType::VAR_STRING || dest.var_value.buffer_size == 0)
1348 throw mx::Exception("GETLINE: needs string buffer");
1349
1350 out << "\txor %ecx, %ecx\n";
1351 size_t t0 = x64_reserve_call_area(out, 0);
1352 out << "\tcall __acrt_iob_func\n";
1353 x64_release_call_area(out, t0);
1354 out << "\tmov %rax, %r8\n";
1355
1356 out << "\tleaq " << getMangledName(i.op1) << "(%rip), %rcx\n";
1357 out << "\tmovq $" << dest.var_value.buffer_size << ", %rdx\n";
1358 size_t total = x64_reserve_call_area(out, 0);
1359 out << "\tcall fgets\n";
1360 x64_release_call_area(out, total);
1361
1362 static size_t over_count = 0;
1363 out << "\ttest %rax, %rax\n";
1364 out << "\tje .over" << over_count << "\n";
1365
1366 out << "\tleaq " << getMangledName(i.op1) << "(%rip), %rcx\n";
1367 size_t tlen = x64_reserve_call_area(out, 0);
1368 out << "\tcall strlen\n";
1369 x64_release_call_area(out, tlen);
1370
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";
1375 out << "\tleaq " << getMangledName(i.op1) << "(%rip), %rax\n";
1376 out << "\tmovb $0, (%rax, %rcx, 1)\n";
1377 out << ".over" << over_count++ << ":\n";
1378 }
1379
1380 void Program::x64_gen_bitop(std::ostream &out, const std::string &opc, const Instruction &i) {
1381 if (i.op3.op.empty()) {
1382 if (isVariable(i.op1.op)) {
1383 Variable &v = getVariable(i.op1.op);
1384 if (v.type == VarType::VAR_INTEGER) {
1387 out << "\t" << opc << "q %rcx, %rax\n";
1388 x64_emitStoreVar(out, "%rax", i.op1);
1389 } else
1390 throw mx::Exception("bitop int only");
1391 } else
1392 throw mx::Exception("bitop first must be variable");
1393 } else {
1394 if (isVariable(i.op1.op)) {
1395 Variable &v = getVariable(i.op1.op);
1396 if (v.type == VarType::VAR_INTEGER) {
1399 out << "\t" << opc << "q %rcx, %rax\n";
1400 x64_emitStoreVar(out, "%rax", i.op1);
1401 } else
1402 throw mx::Exception("bitop int only");
1403 } else
1404 throw mx::Exception("bitop first must be variable");
1405 }
1406 }
1407
1408 void Program::x64_gen_not(std::ostream &out, const Instruction &i) {
1409 if (!i.op1.op.empty()) {
1410 if (isVariable(i.op1.op)) {
1411 Variable &v = getVariable(i.op1.op);
1412 if (v.type != VarType::VAR_INTEGER)
1413 throw mx::Exception("NOT int only");
1415 out << "\ttestq %rax, %rax\n";
1416 out << "\tsete %al\n";
1417 out << "\tmovzbq %al, %rax\n";
1418 x64_emitStoreVar(out, "%rax", i.op1);
1419 } else
1420 throw mx::Exception("NOT requires variable");
1421 } else
1422 throw mx::Exception("NOT requires operand");
1423 }
1424
1425 void Program::x64_gen_push(std::ostream &out, const Instruction &i) {
1426 if (isVariable(i.op1.op)) {
1427 Variable &v = getVariable(i.op1.op);
1429 x64_emitLoadVar(out, "%rax", i.op1);
1430 out << "\tpushq %rax\n";
1431 x64_sp_mod16 ^= 8;
1432 } else if (v.type == VarType::VAR_STRING) {
1433 out << "\tleaq " << getMangledName(i.op1) << "(%rip), %rax\n";
1434 out << "\tpushq %rax\n";
1435 x64_sp_mod16 ^= 8;
1436 } else {
1437 throw mx::Exception("PUSH supports int/pointer/string");
1438 }
1439 } else if (i.op1.type == OperandType::OP_CONSTANT) {
1440 out << "\tmovq $" << i.op1.op << ", %rax\n";
1441 out << "\tpushq %rax\n";
1442 x64_sp_mod16 ^= 8;
1443 } else {
1444 throw mx::Exception("PUSH requires var or const");
1445 }
1446 }
1447
1448 void Program::x64_gen_pop(std::ostream &out, const Instruction &i) {
1449 if (!isVariable(i.op1.op))
1450 throw mx::Exception("POP dest must be a variable");
1451 Variable &v = getVariable(i.op1.op);
1453 out << "\tpopq %rax\n";
1454 x64_sp_mod16 ^= 8;
1455 x64_emitStoreVar(out, "%rax", i.op1);
1456 } else {
1457 throw mx::Exception("POP supports int/pointer");
1458 }
1459 }
1460
1461 void Program::x64_gen_stack_sub(std::ostream &out, const Instruction &i) {
1462 if (!i.op1.op.empty()) {
1463 if (isVariable(i.op1.op))
1464 x64_emitLoadVar(out, "%rcx", i.op1);
1465 else /* constant */
1466 out << "\tmovq $" << i.op1.op << ", %rcx\n";
1467 } else {
1468 out << "\tmovq $1, %rcx\n";
1469 }
1470 out << "\tshl $3, %rcx\n";
1471 out << "\taddq %rcx, %rsp\n";
1472
1473 if (!i.op1.op.empty() && !isVariable(i.op1.op)) {
1474 unsigned long long n = std::stoull(i.op1.op, nullptr, 0);
1475 if (n & 1ull)
1476 x64_sp_mod16 ^= 8;
1477 }
1478 }
1479
1480 void Program::x64_gen_stack_load(std::ostream &out, const Instruction &i) {
1481 if (!isVariable(i.op1.op))
1482 throw mx::Exception("stack_load first arg variable");
1483 if (i.op2.op.empty())
1484 throw mx::Exception("stack_load requires index");
1486 out << "\tmovq (%rsp, %rax, 8), %rcx\n";
1487 x64_emitStoreVar(out, "%rcx", i.op1);
1488 }
1489
1490 void Program::x64_gen_stack_store(std::ostream &out, const Instruction &i) {
1491 if (!isVariable(i.op1.op))
1492 throw mx::Exception("stack_store first arg variable");
1493 if (i.op2.op.empty())
1494 throw mx::Exception("stack_store requires index");
1496 x64_emitLoadVar(out, "%rcx", i.op1);
1497 out << "\tmovq %rcx, (%rsp, %rax, 8)\n";
1498 }
1499
1500 void Program::x64_gen_print(std::ostream &out, const Instruction &i) {
1501 xmm_offset = 0;
1502 std::vector<Operand> args;
1503 args.push_back(i.op1);
1504 if (!i.op2.op.empty())
1505 args.push_back(i.op2);
1506 if (!i.op3.op.empty())
1507 args.push_back(i.op3);
1508 for (const auto &v : i.vop)
1509 if (!v.op.empty())
1510 args.push_back(v);
1511 x64_generateFunctionCall(out, "printf", args);
1512 }
1513
1514 void Program::x64_gen_jmp(std::ostream &out, const Instruction &i) {
1515 if (!i.op1.op.empty()) {
1516 auto pos = labels.find(i.op1.op);
1517 if (pos == labels.end())
1518 throw mx::Exception("Jump must have valid label: " + i.op1.op);
1519 const char *m = nullptr;
1520 switch (i.instruction) {
1521 case JMP:
1522 m = "jmp";
1523 break;
1524 case JE:
1525 m = "je";
1526 break;
1527 case JNE:
1528 m = "jne";
1529 break;
1530 case JL:
1531 m = (last_cmp_type == CMP_FLOAT) ? "jb" : "jl";
1532 break;
1533 case JLE:
1534 m = (last_cmp_type == CMP_FLOAT) ? "jbe" : "jle";
1535 break;
1536 case JG:
1537 m = (last_cmp_type == CMP_FLOAT) ? "ja" : "jg";
1538 break;
1539 case JGE:
1540 m = (last_cmp_type == CMP_FLOAT) ? "jae" : "jge";
1541 break;
1542 case JZ:
1543 m = "jz";
1544 break;
1545 case JNZ:
1546 m = "jnz";
1547 break;
1548 case JA:
1549 m = "ja";
1550 break;
1551 case JB:
1552 m = "jb";
1553 break;
1554 default:
1555 break;
1556 }
1557 out << "\t" << m << " ." << i.op1.op << "\n";
1558 } else
1559 throw mx::Exception("Jump requires label");
1560 }
1561
1562 void Program::x64_gen_cmp(std::ostream &out, const Instruction &i) {
1563 if (i.op2.op.empty())
1564 throw mx::Exception("CMP requires two operands");
1566 if (isVariable(i.op1.op))
1567 t1 = getVariable(i.op1.op).type;
1568 if (isVariable(i.op2.op))
1569 t2 = getVariable(i.op2.op).type;
1570
1571 if (t1 == VarType::VAR_FLOAT || t2 == VarType::VAR_FLOAT) {
1572 if (t1 == VarType::VAR_FLOAT) {
1573 x64_generateLoadVar(out, VarType::VAR_FLOAT, "%xmm0", i.op1);
1574 } else {
1576 out << "\tcvtsi2sdq %rax, %xmm0\n";
1577 }
1578
1579 if (t2 == VarType::VAR_FLOAT) {
1580 x64_generateLoadVar(out, VarType::VAR_FLOAT, "%xmm1", i.op2);
1581 } else {
1583 out << "\tcvtsi2sdq %rax, %xmm1\n";
1584 }
1585
1586 out << "\tcomisd %xmm1, %xmm0\n";
1588 } else if (t1 == VarType::VAR_POINTER && (t2 == VarType::VAR_INTEGER || t2 == VarType::VAR_BYTE)) {
1589 x64_generateLoadVar(out, t1, "%rax", i.op1);
1590 x64_generateLoadVar(out, t2, "%rcx", i.op2);
1591 out << "\tcmpq %rcx, %rax\n";
1593 } else if (!isVariable(i.op2.op) && i.op2.type == OperandType::OP_CONSTANT && isVariable(i.op1.op)) {
1594 auto ra = x64_reg_vars.find(i.op1.op);
1595 if (ra != x64_reg_vars.end()) {
1596 out << "\tcmpq $" << i.op2.op << ", " << ra->second << "\n";
1597 } else if (t1 == VarType::VAR_BYTE) {
1598 out << "\tmovzbq " << getMangledName(i.op1) << "(%rip), %rax\n";
1599 out << "\tcmpq $" << i.op2.op << ", %rax\n";
1600 } else {
1601 out << "\tcmpq $" << i.op2.op << ", " << getMangledName(i.op1) << "(%rip)\n";
1602 }
1604 } else {
1605 x64_generateLoadVar(out, t1, "%rax", i.op1);
1606 x64_generateLoadVar(out, t2, "%rcx", i.op2);
1607 out << "\tcmpq %rcx, %rax\n";
1609 }
1610 }
1611 void Program::x64_gen_arth(std::ostream &out, std::string arth, const Instruction &i) {
1612 auto emitIntegerOp = [&](const Operand &lhs, const Operand &rhs, const Operand &dest) {
1613 if (arth == "mul")
1614 arth = "imul";
1615 if (!isVariable(rhs.op) && rhs.type == OperandType::OP_CONSTANT && arth != "imul" && lhs.op == dest.op && isVariable(lhs.op)) {
1616 auto ra = x64_reg_vars.find(lhs.op);
1617 if (ra != x64_reg_vars.end()) {
1618 out << "\t" << arth << "q $" << rhs.op << ", " << ra->second << "\n";
1619 } else {
1620 out << "\t" << arth << "q $" << rhs.op << ", " << getMangledName(lhs) << "(%rip)\n";
1621 }
1622 return;
1623 }
1624 x64_generateLoadVar(out, VarType::VAR_INTEGER, "%rax", lhs);
1625 if (!isVariable(rhs.op) && rhs.type == OperandType::OP_CONSTANT) {
1626 out << "\t" << arth << "q $" << rhs.op << ", %rax\n";
1627 } else {
1628 x64_generateLoadVar(out, VarType::VAR_INTEGER, "%rcx", rhs);
1629 out << "\t" << arth << "q %rcx, %rax\n";
1630 }
1631 x64_emitStoreVar(out, "%rax", dest);
1632 };
1633
1634 if (i.op3.op.empty()) {
1635 if (!isVariable(i.op1.op))
1636 throw mx::Exception("arth first must be variable");
1637 Variable &v = getVariable(i.op1.op);
1639 emitIntegerOp(i.op1, i.op2, i.op1);
1640 } else if (v.type == VarType::VAR_FLOAT) {
1641 x64_generateLoadVar(out, VarType::VAR_FLOAT, "%xmm0", i.op1);
1642 x64_generateLoadVar(out, VarType::VAR_FLOAT, "%xmm1", i.op2);
1643 out << "\t" << arth << "sd %xmm1, %xmm0\n";
1644 out << "\tmovsd %xmm0, " << getMangledName(i.op1) << "(%rip)\n";
1645 } else
1646 throw mx::Exception("arth unsupported type");
1647 } else {
1648 if (!isVariable(i.op1.op))
1649 throw mx::Exception("arth first must be variable");
1650 Variable &v = getVariable(i.op1.op);
1652 emitIntegerOp(i.op2, i.op3, i.op1);
1653 } else if (v.type == VarType::VAR_FLOAT) {
1654 x64_generateLoadVar(out, VarType::VAR_FLOAT, "%xmm0", i.op2);
1655 x64_generateLoadVar(out, VarType::VAR_FLOAT, "%xmm1", i.op3);
1656 out << "\t" << arth << "sd %xmm1, %xmm0\n";
1657 out << "\tmovsd %xmm0, " << getMangledName(i.op1) << "(%rip)\n";
1658 } else
1659 throw mx::Exception("arth unsupported type");
1660 }
1661 }
1662
1663 void Program::x64_gen_exit(std::ostream &out, const Instruction &i) {
1664
1665 bool uses_std_module = false;
1666 for (const auto &e : external) {
1667 if (e.mod == "std") {
1668 uses_std_module = true;
1669 break;
1670 }
1671 }
1672
1673 if (uses_std_module) {
1674 out << "\t# Clean up program arguments before exit\n";
1675 size_t total = x64_reserve_call_area(out, 0);
1676 out << "\tcall free_program_args\n";
1677 x64_release_call_area(out, total);
1678 }
1679
1680 if (!i.op1.op.empty()) {
1681 std::vector<Operand> opz;
1682 opz.push_back(i.op1);
1683 x64_generateFunctionCall(out, "exit", opz);
1684 } else
1685 throw mx::Exception("exit requires argument");
1686 }
1687
1688 void Program::x64_gen_fcmp(std::ostream &out, const Instruction &i) {
1689 if (i.op2.op.empty()) {
1690 throw mx::Exception("FCMP requires two operands");
1691 }
1692
1693 x64_generateLoadVar(out, VarType::VAR_FLOAT, "%xmm0", i.op1);
1694 x64_generateLoadVar(out, VarType::VAR_FLOAT, "%xmm1", i.op2);
1695 out << "\tcomisd %xmm1, %xmm0\n";
1697 }
1698
1699 void Program::x64_gen_jae(std::ostream &out, const Instruction &i) {
1700 out << "\tjae ." << i.op1.op << "\n";
1701 }
1702
1703 void Program::x64_gen_jbe(std::ostream &out, const Instruction &i) {
1704 out << "\tjbe ." << i.op1.op << "\n";
1705 }
1706
1707 void Program::x64_gen_jc(std::ostream &out, const Instruction &i) {
1708 out << "\tjc ." << i.op1.op << "\n";
1709 }
1710
1711 void Program::x64_gen_jnc(std::ostream &out, const Instruction &i) {
1712 out << "\tjnc ." << i.op1.op << "\n";
1713 }
1714
1715 void Program::x64_gen_jp(std::ostream &out, const Instruction &i) {
1716 out << "\tjp ." << i.op1.op << "\n";
1717 }
1718
1719 void Program::x64_gen_jnp(std::ostream &out, const Instruction &i) {
1720 out << "\tjnp ." << i.op1.op << "\n";
1721 }
1722
1723 void Program::x64_gen_jo(std::ostream &out, const Instruction &i) {
1724 out << "\tjo ." << i.op1.op << "\n";
1725 }
1726
1727 void Program::x64_gen_jno(std::ostream &out, const Instruction &i) {
1728 out << "\tjno ." << i.op1.op << "\n";
1729 }
1730
1731 void Program::x64_gen_js(std::ostream &out, const Instruction &i) {
1732 out << "\tjs ." << i.op1.op << "\n";
1733 }
1734
1735 void Program::x64_gen_jns(std::ostream &out, const Instruction &i) {
1736 out << "\tjns ." << i.op1.op << "\n";
1737 }
1738} // namespace mxvm
General-purpose exception with errno-aware factory method.
Definition exception.hpp:38
std::unordered_map< std::string, std::pair< uint64_t, bool > > labels
label -> (address, is_function)
Definition icode.hpp:194
std::vector< Instruction > inc
instruction stream
Definition icode.hpp:192
std::unordered_map< std::string, Variable > vars
variable symbol table
Definition icode.hpp:193
static Base * base
pointer to the main program base
Definition icode.hpp:197
static std::string root_name
Definition icode.hpp:198
std::vector< ExternalFunction > external
declared external function imports
Definition icode.hpp:195
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.
Definition icode.cpp:175
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.
Definition icode.cpp:107
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.
Definition icode.hpp:633
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
Definition icode.hpp:407
static std::string escapeNewLines(const std::string &text)
Escape newline characters in a string literal for assembly output.
Definition icode.cpp:296
bool last_call_returns_owned_ptr
tracks ownership of last call's return value
Definition icode.hpp:418
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.
Definition icode.cpp:22
LastCmpType last_cmp_type
Definition icode.hpp:417
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
Definition icode.hpp:634
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.
std::string name
Definition icode.hpp:306
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
Definition icode.hpp:406
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)
Definition cstring.c:51
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).
@ JLE
Definition instruct.hpp:40
@ NOT
Definition instruct.hpp:33
@ POP
Definition instruct.hpp:53
@ JNO
Definition instruct.hpp:74
@ MUL
Definition instruct.hpp:28
@ JNZ
Definition instruct.hpp:44
@ SUB
Definition instruct.hpp:27
@ MOD
Definition instruct.hpp:34
@ ALLOC
Definition instruct.hpp:49
@ JMP
Definition instruct.hpp:36
@ GETLINE
Definition instruct.hpp:51
@ JBE
Definition instruct.hpp:68
@ JE
Definition instruct.hpp:37
@ LEA
Definition instruct.hpp:77
@ RETURN
Definition instruct.hpp:64
@ JNP
Definition instruct.hpp:72
@ JO
Definition instruct.hpp:73
@ JA
Definition instruct.hpp:45
@ JS
Definition instruct.hpp:75
@ STACK_LOAD
Definition instruct.hpp:54
@ EXIT
Definition instruct.hpp:48
@ CMP
Definition instruct.hpp:35
@ DIV
Definition instruct.hpp:29
@ AND
Definition instruct.hpp:31
@ JGE
Definition instruct.hpp:42
@ JL
Definition instruct.hpp:39
@ OR
Definition instruct.hpp:30
@ LOAD
Definition instruct.hpp:24
@ DONE
Definition instruct.hpp:60
@ JZ
Definition instruct.hpp:43
@ STACK_SUB
Definition instruct.hpp:56
@ MOV
Definition instruct.hpp:23
@ JNC
Definition instruct.hpp:70
@ JC
Definition instruct.hpp:69
@ PRINT
Definition instruct.hpp:47
@ RET
Definition instruct.hpp:58
@ JAE
Definition instruct.hpp:67
@ STACK_STORE
Definition instruct.hpp:55
@ CALL
Definition instruct.hpp:57
@ XOR
Definition instruct.hpp:32
@ INVOKE
Definition instruct.hpp:63
@ FREE
Definition instruct.hpp:50
@ TO_INT
Definition instruct.hpp:61
@ ADD
Definition instruct.hpp:26
@ PUSH
Definition instruct.hpp:52
@ JNS
Definition instruct.hpp:76
@ JG
Definition instruct.hpp:41
@ JB
Definition instruct.hpp:46
@ REALLOC
Reallocate a dynamic memory block: realloc dest, elemSize, count.
Definition instruct.hpp:78
@ TO_FLOAT
Definition instruct.hpp:62
@ JP
Definition instruct.hpp:71
@ JNE
Definition instruct.hpp:38
@ NEG
Definition instruct.hpp:65
@ FCMP
Definition instruct.hpp:66
@ STORE
Definition instruct.hpp:25
const std::string BRIGHT_BLUE
Definition exception.hpp:80
const std::string BRIGHT_CYAN
Definition exception.hpp:82
Definition ast.hpp:14
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.
Definition parser.hpp:42
static int error_label_count
Definition icode_gen.cpp:12
VarType
MXVM variable type discriminator.
Definition instruct.hpp:180
static bool is_stdio_name(const std::string &s)
size_t xmm_offset
Describes an external function imported from a module or object.
Definition parser.hpp:162
A complete MXVM instruction with opcode, operands, and optional label.
Definition instruct.hpp:168
std::vector< Operand > vop
variable-length operand list (for print, invoke, etc.)
Definition instruct.hpp:171
Inc instruction
the opcode
Definition instruct.hpp:169
Operand op3
up to three fixed operands
Definition instruct.hpp:170
A single instruction operand (constant value or variable reference).
Definition instruct.hpp:159
std::string op
textual operand value
Definition instruct.hpp:161
OperandType type
Definition instruct.hpp:163
uint64_t buffer_size
declared buffer size
Definition instruct.hpp:221
bool owns
true if this value owns the allocated memory
Definition instruct.hpp:222
A named variable with type, value, and optional object association.
Definition instruct.hpp:292
Variable_Value var_value
Definition instruct.hpp:295