MXVM 1.8.1
Virtual Machine, Compiler, and Pascal Frontend
Loading...
Searching...
No Matches
icode_gen.cpp
Go to the documentation of this file.
1
6#include "mxvm/icode.hpp"
7
8#include <algorithm>
9
10namespace mxvm {
11
12 static int error_label_count = 0;
13
14 std::string Program::getPlatformSymbolName(const std::string &name) {
16 if (!name.empty() && name[0] != '_')
17 return "_" + name;
18 else
19 return name;
20 }
21 return name;
22 }
23
24 void Program::sysv_analyzeRegAlloc(bool uses_std_module) {
25 sysv_reg_vars.clear();
26 sysv_reg_save_order.clear();
27
28 // %rbx is reserved for stack alignment scratch
29 // %rsi, %rdi are caller-saved on System V (not available)
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");
34 }
35
36 std::unordered_map<std::string, int> usage_count;
37 auto countOp = [&](const Operand &op) {
38 if (!op.op.empty() && isVariable(op.op)) {
39 Variable &v = getVariable(op.op);
40 if (v.type == VarType::VAR_INTEGER && !v.is_global) {
41 usage_count[op.op]++;
42 }
43 }
44 };
45
46 for (const auto &instr : inc) {
47 countOp(instr.op1);
48 countOp(instr.op2);
49 countOp(instr.op3);
50 for (const auto &vop : instr.vop)
51 countOp(vop);
52 }
53
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; });
57
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) {
61 sysv_reg_vars[sorted_vars[i].first] = available_regs[i];
62 sysv_reg_save_order.push_back(available_regs[i]);
63 }
64 }
65 }
66
67 void Program::sysv_emitFlushRegs(std::ostream &out) {
68 for (const auto &[var, reg] : sysv_reg_vars) {
69 out << "\tmovq " << reg << ", " << getMangledName(var) << "(%rip)\n";
70 }
71 }
72
73 void Program::sysv_emitReloadRegs(std::ostream &out) {
74 for (const auto &[var, reg] : sysv_reg_vars) {
75 out << "\tmovq " << getMangledName(var) << "(%rip), " << reg << "\n";
76 }
77 }
78
79 void Program::sysv_emitSaveRegs(std::ostream &out) {
80 for (const auto &reg : sysv_reg_save_order) {
81 out << "\tpushq " << reg << "\n";
82 }
83 }
84
85 void Program::sysv_emitRestoreRegs(std::ostream &out) {
86 for (auto it = sysv_reg_save_order.rbegin(); it != sysv_reg_save_order.rend(); ++it) {
87 out << "\tpopq " << *it << "\n";
88 }
89 }
90
91 void Program::sysv_emitStoreVar(std::ostream &out, const std::string &srcReg, const Operand &dest) {
92 auto it = sysv_reg_vars.find(dest.op);
93 if (it != sysv_reg_vars.end()) {
94 if (srcReg != it->second)
95 out << "\tmovq " << srcReg << ", " << it->second << "\n";
96 } else {
97 out << "\tmovq " << srcReg << ", " << getMangledName(dest) << "(%rip)\n";
98 }
99 }
100
101 void Program::sysv_emitStoreVarImm(std::ostream &out, const std::string &imm, const Operand &dest) {
102 auto it = sysv_reg_vars.find(dest.op);
103 if (it != sysv_reg_vars.end()) {
104 out << "\tmovq $" << imm << ", " << it->second << "\n";
105 } else {
106 out << "\tmovq $" << imm << ", " << getMangledName(dest) << "(%rip)\n";
107 }
108 }
109
110 void Program::sysv_emitLoadVar(std::ostream &out, const std::string &dstReg, const Operand &src) {
111 auto it = sysv_reg_vars.find(src.op);
112 if (it != sysv_reg_vars.end()) {
113 if (dstReg != it->second)
114 out << "\tmovq " << it->second << ", " << dstReg << "\n";
115 } else {
116 out << "\tmovq " << getMangledName(src) << "(%rip), " << dstReg << "\n";
117 }
118 }
119
120 void Program::generateCode(const Platform &platform, bool obj, std::ostream &out) {
121
122 if (platform == Platform::WINX64) {
123 x64_generateCode(platform, obj, out);
124 return;
125 }
126
127 this->add_standard();
128 std::unordered_map<int, std::string> labels_;
129 for (auto &l : labels) {
130 labels_[l.second.first] = l.first;
131 }
132
133 bool uses_std_module = false;
134 for (const auto &e : external) {
135 if (e.mod == "std") {
136 uses_std_module = true;
137 break;
138 }
139 }
140
142 out << ".section __DATA, __data\n";
143 else
144 out << ".section .data\n";
145
146 std::vector<std::string> var_names;
147 for (auto &v : vars) {
148 var_names.push_back(v.first);
149 }
150 std::sort(var_names.begin(), var_names.end());
151
152 for (auto &v : var_names) {
153 const std::string var_out_name = getPlatformSymbolName(getMangledName(v));
154 auto varx = getVariable(v);
155 if (varx.type == VarType::VAR_INTEGER) {
156 out << "\t" << var_out_name << ": .quad " << vars[v].var_value.int_value << "\n";
157 continue;
158 }
159 if (varx.type == VarType::VAR_FLOAT) {
160 out << "\t" << var_out_name << ": .double " << vars[v].var_value.float_value << "\n";
161 continue;
162 }
163 if (varx.type == VarType::VAR_BYTE) {
164 out << "\t" << var_out_name << ": .byte " << vars[v].var_value.int_value << "\n";
165 }
166 }
167 for (auto &v : var_names) {
168 auto varx = getVariable(v);
169 }
170
171 if (platform == Platform::LINUX) {
172 out << "\t.extern " << getPlatformSymbolName("stderr") << "\n";
173 out << "\t.extern " << getPlatformSymbolName("stdin") << "\n";
174 out << "\t.extern " << getPlatformSymbolName("stdout") << "\n";
175 }
176
177 for (auto &v : var_names) {
178 auto varx = getVariable(v);
179 if (varx.type == VarType::VAR_STRING && varx.var_value.buffer_size == 0) {
180 const std::string var_out_name = getPlatformSymbolName(getMangledName(v));
181 out << "\t" << var_out_name << ": .asciz " << "\"" << escapeNewLines(vars[v].var_value.str_value) << "\"\n";
182 continue;
183 }
184 }
185
186 if (base != nullptr) {
187
188 for (auto &v : var_names) {
189 auto varx = getVariable(v);
190 auto t = varx.type;
191 if (varx.is_global || object) {
192 switch (t) {
196 out << "\t.global " << getMangledName(v) << "\n";
197 break;
198 default:
199 break;
200 }
201 }
202 }
203 }
204
206 out << ".section __DATA,__bss\n";
207 else
208 out << ".section .bss\n";
209
210 for (auto &v : var_names) {
211 const std::string var_out_name = getPlatformSymbolName(getMangledName(v));
212 auto varx = getVariable(v);
213 if (varx.type == VarType::VAR_POINTER) {
214 out << "\t.comm " << var_out_name << ", 8\n";
215 } else if (varx.type == VarType::VAR_STRING && varx.var_value.buffer_size > 0) {
216 out << "\t.comm " << var_out_name << ", " << varx.var_value.buffer_size << "\n";
217 }
218 }
219 if (object) {
220 for (auto &v : var_names) {
221 auto varx = getVariable(v);
222 if (varx.type == VarType::VAR_STRING || varx.type == VarType::VAR_POINTER) {
223 out << "\t.global " << getMangledName(v) << "\n";
224 }
225 }
226 }
228 out << ".section __TEXT, __text\n";
229 else
230 out << ".section .text\n";
231
232 for (auto &lbl : labels) {
233 if (lbl.second.second == true) {
234 out << "\t.global " << name + "_" + lbl.first << "\n";
235 }
236 }
237 std::sort(external.begin(), external.end(), [](const ExternalFunction &a, const ExternalFunction &b) {
238 if (a.mod == b.mod)
239 return a.name < b.name;
240 return a.mod < b.mod;
241 });
242
243 for (auto &e : external) {
244 if (e.module == true)
245 out << "\t.extern " << getPlatformSymbolName(e.name) << "\n";
246 else
247 out << "\t.extern " << getPlatformSymbolName(e.mod + "_" + e.name) << "\n";
248 }
249
250 sysv_analyzeRegAlloc(uses_std_module);
251
252 if (!this->object) {
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";
267 out << "\tcall " << getPlatformSymbolName("set_program_args") << "\n";
268 out << "\tmovq %rbx, %rsp\n";
269 } else {
270 out << "\tsub $8, %rsp\n";
271 }
274 }
275
276 bool done_found = false;
277
278 for (size_t i = 0; i < inc.size(); ++i) {
279 const Instruction &instr = inc[i];
280 for (auto l : labels) {
281 if (l.second.first == i && l.second.second) {
282 out << "\t.p2align 4, 0x90\n";
283 out << getPlatformSymbolName(name + "_" + l.first) << ":\n";
284 out << "\tpush %rbp\n";
285 out << "\tmov %rsp, %rbp\n";
286 out << "\tpush %rbx\n";
287 out << "\tsub $8, %rsp\n";
290 if (isVariable("rax") && getVariable("rax").type == VarType::VAR_INTEGER) {
291 Operand rax_op;
292 rax_op.op = "rax";
293 sysv_emitStoreVar(out, "%rax", rax_op);
294 }
295 break;
296 }
297 }
298 for (auto l : labels) {
299 if (l.second.first == i && !l.second.second) {
300 out << "." << l.first << ":\n";
301 }
302 }
303 if (instr.instruction == DONE)
304 done_found = true;
305 generateInstruction(out, instr);
306 }
307
308#ifndef __EMSCRIPTEN__
309 if (this->object == false && done_found == false)
310 throw mx::Exception("Program missing done to signal completion.\n");
311#endif
313 out << "\n\n\n.section .note.GNU-stack,\"\",@progbits\n\n";
314
315 std::string mainFunc = " Object";
316 if (root_name == name)
317 mainFunc = " Program";
318 std::cout << Col("MXVM: Compiled: ", mx::Color::BRIGHT_BLUE) << name << ".s" << mainFunc << Col(" platform: ", mx::Color::BRIGHT_CYAN) << ((platform == Platform::LINUX) ? "Linux" : "macOS") << "\n";
319 }
320
321 void Program::generateInstruction(std::ostream &out, const Instruction &i) {
322 if (i.instruction < JMP || i.instruction > JNS) {
324 }
325 switch (i.instruction) {
326 case ADD:
327 gen_arth(out, "add", i);
328 break;
329 case SUB:
330 gen_arth(out, "sub", i);
331 break;
332 case MUL:
333 gen_arth(out, "mul", i);
334 break;
335 case PRINT:
336 gen_print(out, i);
337 break;
338 case EXIT:
339 gen_exit(out, i);
340 break;
341 case MOV:
342 gen_mov(out, i);
343 break;
344 case JMP:
345 case JE:
346 case JNE:
347 case JL:
348 case JLE:
349 case JG:
350 case JGE:
351 case JZ:
352 case JNZ:
353 case JA:
354 case JB:
355 gen_jmp(out, i);
356 break;
357 case CMP:
358 gen_cmp(out, i);
359 break;
360 case ALLOC:
361 gen_alloc(out, i);
362 break;
363 case FREE:
364 gen_free(out, i);
365 break;
366 case REALLOC:
367 gen_realloc(out, i);
368 break;
369 case LOAD:
370 gen_load(out, i);
371 break;
372 case STORE:
373 gen_store(out, i);
374 break;
375 case RET:
376 gen_ret(out, i);
377 break;
378 case CALL:
379 gen_call(out, i);
380 break;
381 case DONE:
382 gen_done(out, i);
383 break;
384 case AND:
385 gen_bitop(out, "and", i);
386 break;
387 case OR:
388 gen_bitop(out, "or", i);
389 break;
390 case XOR:
391 gen_bitop(out, "xor", i);
392 break;
393 case NOT:
394 gen_not(out, i);
395 break;
396 case DIV:
397 gen_div(out, i);
398 break;
399 case MOD:
400 gen_mod(out, i);
401 break;
402 case PUSH:
403 gen_push(out, i);
404 break;
405 case POP:
406 gen_pop(out, i);
407 break;
408 case STACK_LOAD:
409 gen_stack_load(out, i);
410 break;
411 case STACK_STORE:
412 gen_stack_store(out, i);
413 break;
414 case STACK_SUB:
415 gen_stack_sub(out, i);
416 break;
417 case GETLINE:
418 gen_getline(out, i);
419 break;
420 case TO_INT:
421 gen_to_int(out, i);
422 break;
423 case TO_FLOAT:
424 gen_to_float(out, i);
425 break;
426 case INVOKE:
427 gen_invoke(out, i);
428 break;
429 case RETURN:
430 gen_return(out, i);
431 break;
432 case NEG:
433 gen_neg(out, i);
434 break;
435 case FCMP:
436 gen_fcmp(out, i);
437 break;
438 case JAE:
439 gen_jae(out, i);
440 break;
441 case JBE:
442 gen_jbe(out, i);
443 break;
444 case JC:
445 gen_jc(out, i);
446 break;
447 case JNC:
448 gen_jnc(out, i);
449 break;
450 case JP:
451 gen_jp(out, i);
452 break;
453 case JNP:
454 gen_jnp(out, i);
455 break;
456 case JO:
457 gen_jo(out, i);
458 break;
459 case JNO:
460 gen_jno(out, i);
461 break;
462 case JS:
463 gen_js(out, i);
464 break;
465 case JNS:
466 gen_jns(out, i);
467 break;
468 case LEA:
469 gen_lea(out, i);
470 break;
471 default:
472 throw mx::Exception("Invalid or unsupported instruction: " + std::to_string(static_cast<unsigned int>(i.instruction)));
473 }
474 }
475 void Program::gen_done(std::ostream &out, const Instruction &i) {
476 bool uses_std_module = false;
477 for (const auto &e : external) {
478 if (e.mod == "std") {
479 uses_std_module = true;
480 break;
481 }
482 }
483
484 if (uses_std_module && !this->object) {
486 out << "\tmovq %rsp, %rbx\n";
487 out << "\tandq $-16, %rsp\n";
488 out << "\tcall " << getPlatformSymbolName("free_program_args") << "\n";
489 out << "\tmovq %rbx, %rsp\n";
490 } else {
492 }
493
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";
499 }
500 out << "\tmovq -8(%rbp), %rbx\n";
501 out << "\tleave\n";
502 out << "\tret\n";
503 }
504
505 void Program::gen_ret(std::ostream &out, const Instruction &i) {
507 if (isVariable("rax") && getVariable("rax").type == VarType::VAR_INTEGER) {
508 Operand rax_op;
509 rax_op.op = "rax";
510 sysv_emitLoadVar(out, "%rax", rax_op);
511 }
513 out << "\tmovq -8(%rbp), %rbx\n";
514 out << "\tleave\n";
515 out << "\tret\n";
516 }
517
518 void Program::gen_return(std::ostream &out, const Instruction &i) {
519 if (isVariable(i.op1.op)) {
520 Variable &v = getVariable(i.op1.op);
521 if (this->last_call_returns_owned_ptr) {
522 v.var_value.owns = true;
523 }
524 switch (v.type) {
526 out << "\tmovsd %xmm0, " << getMangledName(i.op1) << "(%rip)\n";
527 break;
528 default:
529 sysv_emitStoreVar(out, "%rax", i.op1);
530 }
531
532 } else {
533 throw mx::Exception("return: " + i.op1.op + " requires variable\n");
534 }
535 }
536
537 void Program::gen_neg(std::ostream &out, const Instruction &i) {
538 if (isVariable(i.op1.op)) {
539 Variable &v = getVariable(i.op1.op);
540 if (v.type == VarType::VAR_INTEGER) {
541 generateLoadVar(out, v.type, "%rcx", i.op1);
542 out << "\tnegq %rcx\n";
543 sysv_emitStoreVar(out, "%rcx", i.op1);
544 } else if (v.type == VarType::VAR_FLOAT) {
545 generateLoadVar(out, VarType::VAR_FLOAT, "%xmm0", i.op1);
546 out << "\txorpd %xmm1, %xmm1\n";
547 out << "\tsubsd %xmm0, %xmm1\n";
548 out << "\tmovsd %xmm1, " << getMangledName(i.op1) << "(%rip)\n";
549 } else {
550 throw mx::Exception("neg requires float or integer.");
551 }
552 } else {
553 throw mx::Exception("neg requires variable");
554 }
555 }
556
557 void Program::gen_lea(std::ostream &out, const Instruction &i) {
558 if (!isVariable(i.op1.op))
559 throw mx::Exception("LEA destination must be a variable");
560 if (!isVariable(i.op2.op))
561 throw mx::Exception("LEA source must be a variable");
562
563 // Flush source register to memory before taking its address
564 auto it = sysv_reg_vars.find(i.op2.op);
565 if (it != sysv_reg_vars.end()) {
566 out << "\tmovq " << it->second << ", " << getMangledName(i.op2) << "(%rip)\n";
567 }
568
569 out << "\tleaq " << getMangledName(i.op2) << "(%rip), %rax\n";
570 out << "\tmovq %rax, " << getMangledName(i.op1) << "(%rip)\n";
571 getVariable(i.op1.op).var_value.owns = false;
572 }
573
574 void Program::gen_invoke(std::ostream &out, const Instruction &i) {
575 if (i.op1.op.empty()) {
576 throw mx::Exception("invoke instruction requires operand of instruction name");
577 }
578 std::vector<Operand> op;
579 op.push_back(i.op1);
580 if (!i.op2.op.empty())
581 op.push_back(i.op2);
582 if (!i.op3.op.empty())
583 op.push_back(i.op3);
584 for (const auto &vx : i.vop) {
585 if (!vx.op.empty())
586 op.push_back(vx);
587 }
588 generateInvokeCall(out, op);
589 }
590
591 void Program::generateInvokeCall(std::ostream &out, std::vector<Operand> &op) {
592 if (op.empty() || op[0].op.empty()) {
593 throw mx::Exception("invoke instruction requires operand of instruction name");
594 }
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]);
600 }
601
603 this->last_call_returns_owned_ptr = true;
604 } else {
605 this->last_call_returns_owned_ptr = false;
606 }
608 }
609
610 void Program::generateFunctionCall(std::ostream &out, const std::string &name, std::vector<Operand> &op) {
611 const std::vector<Operand> &args = op;
612 xmm_offset = 0;
613 size_t stack_args = (args.size() > 6) ? (args.size() - 6) : 0;
614
615 out << "\tmovq %rsp, %rbx\n";
616 out << "\tandq $-16, %rsp\n";
617
618 if (stack_args) {
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) {
623 generateLoadVar(out, VarType::VAR_INTEGER, "%rax", args[idx]);
624 out << "\tmovq %rax, " << (slot * 8) << "(%rsp)\n";
625 }
626 }
627
628 int reg_count = 0;
629 for (size_t z = 0; z < args.size() && reg_count < 6; ++z, ++reg_count) {
630 generateLoadVar(out, reg_count, args[z]);
631 }
632
633 out << "\txor %eax, %eax\n";
634 out << "\tcall " << getPlatformSymbolName(name) << "\n";
635 out << "\tmovq %rbx, %rsp\n";
636 }
637
638 void Program::gen_call(std::ostream &out, const Instruction &i) {
639 if (isFunctionValid(i.op1.op)) {
641 if (isVariable("rax") && getVariable("rax").type == VarType::VAR_INTEGER) {
642 Operand rax_op;
643 rax_op.op = "rax";
644 sysv_emitLoadVar(out, "%rax", rax_op);
645 }
646 out << "\tmovq %rsp, %rbx\n";
647 out << "\tandq $-16, %rsp\n";
648 out << "\tcall " << getPlatformSymbolName(getMangledName(i.op1)) << "\n";
649 out << "\tmovq %rbx, %rsp\n";
650 if (isVariable("rax") && getVariable("rax").type == VarType::VAR_INTEGER) {
651 Operand rax_op;
652 rax_op.op = "rax";
653 out << "\tmovq %rax, " << getMangledName(rax_op) << "(%rip)\n";
654 }
656 } else {
657 throw mx::Exception("Function " + i.op1.op + " not found!");
658 }
659 }
660
661 void Program::gen_alloc(std::ostream &out, const Instruction &i) {
662 if (!isVariable(i.op1.op)) {
663 throw mx::Exception("ALLOC destination must be a variable");
664 }
665
666 Variable &v = getVariable(i.op1.op);
667 if (v.type != VarType::VAR_POINTER) {
668 throw mx::Exception("ALLOC destination must be a pointer\n");
669 }
670
671 if (!i.op2.op.empty()) {
672 if (isVariable(i.op2.op)) {
673 sysv_emitLoadVar(out, "%rsi", i.op2);
674 } else {
675 out << "\tmovq $" << i.op2.op << ", %rsi\n";
676 }
677 } else {
678 out << "\tmovq $8, %rsi\n";
679 }
680
681 if (!i.op3.op.empty()) {
682 if (isVariable(i.op3.op)) {
683 sysv_emitLoadVar(out, "%rdi", i.op3);
684 } else if (i.op3.type == OperandType::OP_CONSTANT) {
685 out << "\tmovq $" << i.op3.op << ", %rdi\n";
686 } else {
687 throw mx::Exception("ALLOC: invalid count operand " + i.op3.op);
688 }
689 } else {
690 out << "\tmovq $1, %rdi\n";
691 }
692 out << "\tmovq %rsp, %rbx\n";
693 out << "\tandq $-16, %rsp\n";
694 out << "\tcall " << getPlatformSymbolName("calloc") << "\n";
695 out << "\tmovq %rbx, %rsp\n";
696 out << "\ttest %rax, %rax\n";
697 out << "\tjz .alloc_failed_" << error_label_count << "\n";
698 out << "\tmovq %rax, " << getMangledName(i.op1) << "(%rip)\n";
699 out << "\tjmp .alloc_done_" << error_label_count << "\n";
700 out << ".alloc_failed_" << error_label_count << ":\n";
701 out << "\tmovq $0, " << getMangledName(i.op1) << "(%rip)\n";
702 out << ".alloc_done_" << error_label_count << ":\n";
704 v.var_value.owns = true;
705 }
706
715 void Program::gen_realloc(std::ostream &out, const Instruction &i) {
716 if (!isVariable(i.op1.op)) {
717 throw mx::Exception("REALLOC destination must be a variable");
718 }
719 Variable &v = getVariable(i.op1.op);
720 if (v.type != VarType::VAR_POINTER) {
721 throw mx::Exception("REALLOC destination must be a pointer");
722 }
723
725
726 // Compute new total size = count * elemSize into %rsi
727 // %rdi = existing ptr (for realloc)
728 out << "\tmovq " << getMangledName(i.op1) << "(%rip), %rdi\n";
729
730 if (!i.op3.op.empty()) {
731 if (isVariable(i.op3.op)) {
732 sysv_emitLoadVar(out, "%rax", i.op3);
733 } else {
734 out << "\tmovq $" << i.op3.op << ", %rax\n";
735 }
736 } else {
737 out << "\tmovq $1, %rax\n";
738 }
739
740 if (!i.op2.op.empty()) {
741 if (isVariable(i.op2.op)) {
742 sysv_emitLoadVar(out, "%rcx", i.op2);
743 } else {
744 out << "\tmovq $" << i.op2.op << ", %rcx\n";
745 }
746 } else {
747 out << "\tmovq $8, %rcx\n";
748 }
749
750 out << "\timulq %rcx, %rax\n";
751 out << "\tmovq %rax, %rsi\n";
752 out << "\tmovq %rsp, %rbx\n";
753 out << "\tandq $-16, %rsp\n";
754 out << "\tcall " << getPlatformSymbolName("realloc") << "\n";
755 out << "\tmovq %rbx, %rsp\n";
756 out << "\ttest %rax, %rax\n";
757 out << "\tjz .realloc_failed_" << error_label_count << "\n";
758 out << "\tmovq %rax, " << getMangledName(i.op1) << "(%rip)\n";
759 out << "\tjmp .realloc_done_" << error_label_count << "\n";
760 out << ".realloc_failed_" << error_label_count << ":\n";
761 out << "\t# realloc failed, keep old pointer\n";
762 out << ".realloc_done_" << error_label_count << ":\n";
764 v.var_value.owns = true;
766 }
767
768 void Program::gen_load(std::ostream &out, const Instruction &i) {
769 if (!isVariable(i.op1.op)) {
770 throw mx::Exception("LOAD destination: " + i.op1.op + " must be a variable");
771 }
772 Variable &dest = getVariable(i.op1.op);
773
774 if (!isVariable(i.op2.op)) {
775 throw mx::Exception("LOAD source: " + i.op2.op + " must be a pointer variable");
776 }
777 Variable &ptrVar = getVariable(i.op2.op);
778
779 if (ptrVar.type == VarType::VAR_POINTER) {
780 // Flush all cached registers so pointer reads see current values
782 out << "\tmovq " << getMangledName(i.op2) << "(%rip), %rax\n";
783 } else if (ptrVar.type == VarType::VAR_STRING) {
784 out << "\tleaq " << getMangledName(i.op2) << "(%rip), %rax\n";
785 } else {
786 throw mx::Exception("LOAD source must be a pointer or string buffer: " + i.op2.op);
787 }
788
789 if (!i.op3.op.empty()) {
790 if (isVariable(i.op3.op)) {
791 sysv_emitLoadVar(out, "%rcx", i.op3);
792 } else {
793 out << "\tmovq $" << i.op3.op << ", %rcx\n";
794 }
795 } else {
796 out << "\txorq %rcx, %rcx\n";
797 }
798
799 if (!i.vop.empty() && !i.vop[0].op.empty()) {
800 if (isVariable(i.vop[0].op)) {
801 sysv_emitLoadVar(out, "%rdx", i.vop[0]);
802 out << "\timulq %rdx, %rcx\n";
803 out << "\taddq %rcx, %rax\n";
804
805 switch (dest.type) {
809 out << "\tmovq (%rax), %rdx\n";
810 sysv_emitStoreVar(out, "%rdx", i.op1);
811 break;
813 out << "\tmovsd (%rax), %xmm0\n";
814 out << "\tmovsd %xmm0, " << getMangledName(i.op1) << "(%rip)\n";
815 break;
817 out << "\tmovzbq (%rax), %rdx\n";
818 out << "\tmovb %dl, " << getMangledName(i.op1) << "(%rip)\n";
819 break;
820 default:
821 throw mx::Exception("LOAD: unsupported destination type");
822 }
823 } else {
824 size_t stride = static_cast<size_t>(std::stoll(i.vop[0].op, nullptr, 0));
825
826 if (stride == 1 || stride == 2 || stride == 4 || stride == 8) {
827 switch (dest.type) {
831 out << "\tmovq (%rax,%rcx," << stride << "), %rdx\n";
832 sysv_emitStoreVar(out, "%rdx", i.op1);
833 break;
835 out << "\tmovsd (%rax,%rcx," << stride << "), %xmm0\n";
836 out << "\tmovsd %xmm0, " << getMangledName(i.op1) << "(%rip)\n";
837 break;
839 out << "\tmovzbq (%rax,%rcx," << stride << "), %rdx\n";
840 out << "\tmovb %dl, " << getMangledName(i.op1) << "(%rip)\n";
841 break;
842 default:
843 throw mx::Exception("LOAD: unsupported destination type");
844 }
845 } else {
846 out << "\timulq $" << stride << ", %rcx\n";
847 out << "\taddq %rcx, %rax\n";
848
849 switch (dest.type) {
853 out << "\tmovq (%rax), %rdx\n";
854 sysv_emitStoreVar(out, "%rdx", i.op1);
855 break;
857 out << "\tmovsd (%rax), %xmm0\n";
858 out << "\tmovsd %xmm0, " << getMangledName(i.op1) << "(%rip)\n";
859 break;
861 out << "\tmovzbq (%rax), %rdx\n";
862 out << "\tmovb %dl, " << getMangledName(i.op1) << "(%rip)\n";
863 break;
864 default:
865 throw mx::Exception("LOAD: unsupported destination type");
866 }
867 }
868 }
869 } else {
870 switch (dest.type) {
874 out << "\tmovq (%rax,%rcx,8), %rdx\n";
875 sysv_emitStoreVar(out, "%rdx", i.op1);
876 break;
878 out << "\tmovsd (%rax,%rcx,8), %xmm0\n";
879 out << "\tmovsd %xmm0, " << getMangledName(i.op1) << "(%rip)\n";
880 break;
882 out << "\tmovzbq (%rax,%rcx,8), %rdx\n";
883 out << "\tmovb %dl, " << getMangledName(i.op1) << "(%rip)\n";
884 break;
885 default:
886 throw mx::Exception("LOAD: unsupported destination type");
887 }
888 }
889 }
890
891 void Program::gen_store(std::ostream &out, const Instruction &i) {
892 if (!isVariable(i.op2.op)) {
893 throw mx::Exception("STORE destination must be a pointer variable: " + i.op2.op);
894 }
895 Variable &ptrVar = getVariable(i.op2.op);
896
897 if (ptrVar.type == VarType::VAR_POINTER) {
898 // Flush all cached registers before pointer store
900 out << "\tmovq " << getMangledName(i.op2) << "(%rip), %rax\n";
901 } else if (ptrVar.type == VarType::VAR_STRING && ptrVar.var_value.buffer_size > 0) {
902 out << "\tleaq " << getMangledName(i.op2) << "(%rip), %rax\n";
903 } else {
904 throw mx::Exception("STORE must target pointer or string buffer");
905 }
906
907 if (!i.op3.op.empty()) {
908 if (isVariable(i.op3.op)) {
909 sysv_emitLoadVar(out, "%rcx", i.op3);
910 } else {
911 out << "\tmovq $" << i.op3.op << ", %rcx\n";
912 }
913 } else {
914 out << "\txorq %rcx, %rcx\n";
915 }
916
917 if (isVariable(i.op1.op)) {
918 Variable &src = getVariable(i.op1.op);
919 switch (src.type) {
923 sysv_emitLoadVar(out, "%rdx", i.op1);
924 break;
926 out << "\tleaq " << getMangledName(i.op1) << "(%rip), %rdx\n";
927 break;
929 out << "\tmovzbq " << getMangledName(i.op1) << "(%rip), %rdx\n";
930 break;
932 out << "\tmovsd " << getMangledName(i.op1) << "(%rip), %xmm0\n";
933 break;
934 default:
935 throw mx::Exception("STORE: unsupported source type");
936 }
937 } else {
938 out << "\tmovq $" << i.op1.op << ", %rdx\n";
939 }
940
941 if (!i.vop.empty() && !i.vop[0].op.empty()) {
942 if (isVariable(i.vop[0].op)) {
943 sysv_emitLoadVar(out, "%r8", i.vop[0]);
944 out << "\timulq %r8, %rcx\n";
945 out << "\taddq %rcx, %rax\n";
946
947 if (isVariable(i.op1.op)) {
948 Variable &src = getVariable(i.op1.op);
949 if (src.type == VarType::VAR_FLOAT) {
950 out << "\tmovsd %xmm0, (%rax)\n";
951 } else if (src.type == VarType::VAR_BYTE) {
952 out << "\tmovb %dl, (%rax)\n";
953 } else {
954 out << "\tmovq %rdx, (%rax)\n";
955 }
956 } else {
957 out << "\tmovq %rdx, (%rax)\n";
958 }
959 } else {
960 size_t stride = static_cast<size_t>(std::stoll(i.vop[0].op, nullptr, 0));
961
962 if (stride == 1 || stride == 2 || stride == 4 || stride == 8) {
963 if (isVariable(i.op1.op)) {
964 Variable &src = getVariable(i.op1.op);
965 if (src.type == VarType::VAR_FLOAT) {
966 out << "\tmovsd %xmm0, (%rax,%rcx," << stride << ")\n";
967 } else if (src.type == VarType::VAR_BYTE) {
968 out << "\tmovb %dl, (%rax,%rcx," << stride << ")\n";
969 } else {
970 out << "\tmovq %rdx, (%rax,%rcx," << stride << ")\n";
971 }
972 } else {
973 out << "\tmovq %rdx, (%rax,%rcx," << stride << ")\n";
974 }
975 } else {
976 out << "\timulq $" << stride << ", %rcx\n";
977 out << "\taddq %rcx, %rax\n";
978
979 if (isVariable(i.op1.op)) {
980 Variable &src = getVariable(i.op1.op);
981 if (src.type == VarType::VAR_FLOAT) {
982 out << "\tmovsd %xmm0, (%rax)\n";
983 } else if (src.type == VarType::VAR_BYTE) {
984 out << "\tmovb %dl, (%rax)\n";
985 } else {
986 out << "\tmovq %rdx, (%rax)\n";
987 }
988 } else {
989 out << "\tmovq %rdx, (%rax)\n";
990 }
991 }
992 }
993 } else {
994 if (isVariable(i.op1.op)) {
995 Variable &src = getVariable(i.op1.op);
996 if (src.type == VarType::VAR_FLOAT) {
997 out << "\tmovsd %xmm0, (%rax,%rcx,8)\n";
998 } else if (src.type == VarType::VAR_BYTE) {
999 out << "\tmovb %dl, (%rax,%rcx,8)\n";
1000 } else {
1001 out << "\tmovq %rdx, (%rax,%rcx,8)\n";
1002 }
1003 } else {
1004 out << "\tmovq %rdx, (%rax,%rcx,8)\n";
1005 }
1006 }
1007 // Reload cached registers after pointer store (memory may have changed)
1008 if (ptrVar.type == VarType::VAR_POINTER) {
1010 }
1011 }
1012
1013 void Program::gen_free(std::ostream &out, const Instruction &i) {
1014 if (!isVariable(i.op1.op)) {
1015 throw mx::Exception("FREE argument must be a variable");
1016 }
1017 Variable &v = getVariable(i.op1.op);
1018 if (v.type != VarType::VAR_POINTER) {
1019 throw mx::Exception("FREE argument must be a pointer");
1020 }
1021
1022 out << "\tmovq " << getMangledName(i.op1) << "(%rip), %rdi\n";
1023 out << "\ttest %rdi, %rdi\n";
1024 out << "\tjz 1f\n";
1025 out << "\tmovq %rsp, %rbx\n";
1026 out << "\tandq $-16, %rsp\n";
1027 out << "\tcall " << getPlatformSymbolName("free") << "\n";
1028 out << "\tmovq %rbx, %rsp\n";
1029 out << "\tmovq $0, " << getMangledName(i.op1) << "(%rip)\n";
1030 out << "1:\n";
1031 v.var_value.owns = false;
1032 }
1033
1034 void Program::gen_to_int(std::ostream &out, const Instruction &i) {
1035 if (!isVariable(i.op1.op)) {
1036 throw mx::Exception("to_int requires destination variable (dest int)");
1037 }
1038 if (i.op2.op.empty()) {
1039 throw mx::Exception("to_int requires source operand");
1040 }
1041
1042 Variable &dest = getVariable(i.op1.op);
1043 if (dest.type != VarType::VAR_INTEGER) {
1044 throw mx::Exception("to_int: first argument must be an integer variable");
1045 }
1046
1047 if (isVariable(i.op2.op)) {
1048 Variable &src = getVariable(i.op2.op);
1049 switch (src.type) {
1052 case VarType::VAR_EXTERN: {
1053 auto ra_src = sysv_reg_vars.find(i.op2.op);
1054 if (ra_src != sysv_reg_vars.end()) {
1055 out << "\tmovq " << ra_src->second << ", %rax\n";
1056 } else {
1057 out << "\tmovq " << getPlatformSymbolName(getMangledName(i.op2)) << "(%rip), %rax\n";
1058 }
1059 sysv_emitStoreVar(out, "%rax", i.op1);
1060 break;
1061 }
1062
1063 case VarType::VAR_BYTE:
1064 out << "\tmovzbq " << getPlatformSymbolName(getMangledName(i.op2)) << "(%rip), %rax\n";
1065 sysv_emitStoreVar(out, "%rax", i.op1);
1066 break;
1067
1068 case VarType::VAR_FLOAT:
1069 out << "\tmovsd " << getPlatformSymbolName(getMangledName(i.op2)) << "(%rip), %xmm0\n";
1070 out << "\tcvttsd2si %xmm0, %rax\n";
1071 sysv_emitStoreVar(out, "%rax", i.op1);
1072 break;
1073
1075 out << "\tleaq " << getPlatformSymbolName(getMangledName(i.op2)) << "(%rip), %rdi\n";
1076 out << "\tmovq %rsp, %rbx\n";
1077 out << "\tandq $-16, %rsp\n";
1078 out << "\tcall " << getPlatformSymbolName("atol") << "\n";
1079 out << "\tmovq %rbx, %rsp\n";
1080 sysv_emitStoreVar(out, "%rax", i.op1);
1081 break;
1082
1083 default:
1084 throw mx::Exception("to_int: unsupported source variable type");
1085 }
1086 } else if (i.op2.type == OperandType::OP_CONSTANT) {
1087 out << "\tmovq $" << i.op2.op << ", %rax\n";
1088 sysv_emitStoreVar(out, "%rax", i.op1);
1089 } else {
1090 throw mx::Exception("to_int: unsupported source operand");
1091 }
1092 }
1093 void Program::gen_to_float(std::ostream &out, const Instruction &i) {
1094 if (!isVariable(i.op1.op)) {
1095 throw mx::Exception("to_float requires destination variable (dest float)");
1096 }
1097 if (i.op2.op.empty()) {
1098 throw mx::Exception("to_float requires source operand");
1099 }
1100
1101 Variable &dest = getVariable(i.op1.op);
1102 if (dest.type != VarType::VAR_FLOAT) {
1103 throw mx::Exception("to_float: first argument must be a float variable");
1104 }
1105
1106 if (isVariable(i.op2.op)) {
1107 Variable &src = getVariable(i.op2.op);
1108 switch (src.type) {
1109 case VarType::VAR_FLOAT:
1110 out << "\tmovsd " << getPlatformSymbolName(getMangledName(i.op2)) << "(%rip), %xmm0\n";
1111 out << "\tmovsd %xmm0, " << getPlatformSymbolName(getMangledName(i.op1)) << "(%rip)\n";
1112 break;
1113
1116 case VarType::VAR_EXTERN: {
1117 auto ra_src = sysv_reg_vars.find(i.op2.op);
1118 if (ra_src != sysv_reg_vars.end()) {
1119 out << "\tmovq " << ra_src->second << ", %rax\n";
1120 } else {
1121 out << "\tmovq " << getPlatformSymbolName(getMangledName(i.op2)) << "(%rip), %rax\n";
1122 }
1123 out << "\tcvtsi2sd %rax, %xmm0\n";
1124 out << "\tmovsd %xmm0, " << getPlatformSymbolName(getMangledName(i.op1)) << "(%rip)\n";
1125 break;
1126 }
1127
1128 case VarType::VAR_BYTE:
1129 out << "\tmovzbq " << getPlatformSymbolName(getMangledName(i.op2)) << "(%rip), %rax\n";
1130 out << "\tcvtsi2sd %rax, %xmm0\n";
1131 out << "\tmovsd %xmm0, " << getPlatformSymbolName(getMangledName(i.op1)) << "(%rip)\n";
1132 break;
1133
1135 out << "\tleaq " << getPlatformSymbolName(getMangledName(i.op2)) << "(%rip), %rdi\n";
1136 out << "\tmovq %rsp, %rbx\n";
1137 out << "\tandq $-16, %rsp\n";
1138 out << "\tcall " << getPlatformSymbolName("atof") << "\n";
1139 out << "\tmovq %rbx, %rsp\n";
1140 out << "\tmovsd %xmm0, " << getPlatformSymbolName(getMangledName(i.op1)) << "(%rip)\n";
1141 break;
1142
1143 default:
1144 throw mx::Exception("to_float: unsupported source variable type");
1145 }
1146 } else if (i.op2.type == OperandType::OP_CONSTANT) {
1147 out << "\tmovq $" << i.op2.op << ", %rax\n";
1148 out << "\tcvtsi2sd %rax, %xmm0\n";
1149 out << "\tmovsd %xmm0, " << getPlatformSymbolName(getMangledName(i.op1)) << "(%rip)\n";
1150 } else {
1151 throw mx::Exception("to_float: unsupported source operand");
1152 }
1153 }
1154
1155 void Program::gen_div(std::ostream &out, const Instruction &i) {
1156 if (i.op3.op.empty()) {
1157 if (isVariable(i.op1.op)) {
1158 Variable &v = getVariable(i.op1.op);
1159 if (v.type == VarType::VAR_INTEGER) {
1160 generateLoadVar(out, VarType::VAR_INTEGER, "%rax", i.op1);
1161 generateLoadVar(out, VarType::VAR_INTEGER, "%rcx", i.op2);
1162 out << "\tcmpq $0, %rcx\n";
1163 out << "\tje 1f\n";
1164 out << "\tcqto\n";
1165 out << "\tidivq %rcx\n";
1166 out << "\tjmp 2f\n";
1167 out << "1:\n";
1168 out << "\txor %eax, %eax\n";
1169 out << "2:\n";
1170 sysv_emitStoreVar(out, "%rax", i.op1);
1171 } else if (v.type == VarType::VAR_FLOAT) {
1172 generateLoadVar(out, VarType::VAR_FLOAT, "%xmm0", i.op1);
1173 generateLoadVar(out, VarType::VAR_FLOAT, "%xmm1", i.op2);
1174 out << "\txorpd %xmm2, %xmm2\n";
1175 out << "\tucomisd %xmm2, %xmm1\n";
1176 out << "\tje 1f\n";
1177 out << "\tdivsd %xmm1, %xmm0\n";
1178 out << "\tjmp 2f\n";
1179 out << "1:\n";
1180 out << "\txorpd %xmm0, %xmm0\n";
1181 out << "2:\n";
1182 out << "\tmovsd %xmm0, " << getMangledName(i.op1) << "(%rip)\n";
1183 } else {
1184 throw mx::Exception("DIV: unsupported variable type");
1185 }
1186 } else {
1187 throw mx::Exception("DIV: first argument must be a variable");
1188 }
1189 } else {
1190 if (isVariable(i.op1.op)) {
1191 Variable &v = getVariable(i.op1.op);
1192 if (v.type == VarType::VAR_INTEGER) {
1193 generateLoadVar(out, VarType::VAR_INTEGER, "%rax", i.op2);
1194 generateLoadVar(out, VarType::VAR_INTEGER, "%rcx", i.op3);
1195 out << "\tcmpq $0, %rcx\n";
1196 out << "\tje 1f\n";
1197 out << "\tcqto\n";
1198 out << "\tidivq %rcx\n";
1199 out << "\tjmp 2f\n";
1200 out << "1:\n";
1201 out << "\txor %eax, %eax\n";
1202 out << "2:\n";
1203 sysv_emitStoreVar(out, "%rax", i.op1);
1204 } else if (v.type == VarType::VAR_FLOAT) {
1205 generateLoadVar(out, VarType::VAR_FLOAT, "%xmm0", i.op2);
1206 generateLoadVar(out, VarType::VAR_FLOAT, "%xmm1", i.op3);
1207 out << "\txorpd %xmm2, %xmm2\n";
1208 out << "\tucomisd %xmm2, %xmm1\n";
1209 out << "\tje 1f\n";
1210 out << "\tdivsd %xmm1, %xmm0\n";
1211 out << "\tjmp 2f\n";
1212 out << "1:\n";
1213 out << "\txorpd %xmm0, %xmm0\n";
1214 out << "2:\n";
1215 out << "\tmovsd %xmm0, " << getMangledName(i.op1) << "(%rip)\n";
1216 } else {
1217 throw mx::Exception("DIV: unsupported variable type");
1218 }
1219 } else {
1220 throw mx::Exception("DIV: first argument must be a variable");
1221 }
1222 }
1223 }
1224
1225 void Program::gen_mod(std::ostream &out, const Instruction &i) {
1226 if (i.op3.op.empty()) {
1227 if (isVariable(i.op1.op)) {
1228 Variable &v = getVariable(i.op1.op);
1229 if (v.type == VarType::VAR_INTEGER) {
1230 generateLoadVar(out, VarType::VAR_INTEGER, "%rax", i.op1);
1231 generateLoadVar(out, VarType::VAR_INTEGER, "%rcx", i.op2);
1232 out << "\tcmpq $0, %rcx\n";
1233 out << "\tje 1f\n";
1234 out << "\tcqto\n";
1235 out << "\tidivq %rcx\n";
1236 out << "\tjmp 2f\n";
1237 out << "1:\n";
1238 out << "\txor %edx, %edx\n";
1239 out << "2:\n";
1240 sysv_emitStoreVar(out, "%rdx", i.op1);
1241 } else {
1242 throw mx::Exception("MOD only supports integer variables");
1243 }
1244 } else {
1245 throw mx::Exception("MOD: first argument must be a variable");
1246 }
1247 } else {
1248 if (isVariable(i.op1.op)) {
1249 Variable &v = getVariable(i.op1.op);
1250 if (v.type == VarType::VAR_INTEGER) {
1251 generateLoadVar(out, VarType::VAR_INTEGER, "%rax", i.op2);
1252 generateLoadVar(out, VarType::VAR_INTEGER, "%rcx", i.op3);
1253 out << "\tcmpq $0, %rcx\n";
1254 out << "\tje 1f\n";
1255 out << "\tcqto\n";
1256 out << "\tidivq %rcx\n";
1257 out << "\tjmp 2f\n";
1258 out << "1:\n";
1259 out << "\txor %edx, %edx\n";
1260 out << "2:\n";
1261 sysv_emitStoreVar(out, "%rdx", i.op1);
1262 } else {
1263 throw mx::Exception("MOD only supports integer variables");
1264 }
1265 } else {
1266 throw mx::Exception("MOD: first argument must be a variable");
1267 }
1268 }
1269 }
1270
1271 void Program::gen_getline(std::ostream &out, const Instruction &i) {
1272 if (!isVariable(i.op1.op)) {
1273 throw mx::Exception("GETLINE destination must be a variable");
1274 }
1275 Variable &dest = getVariable(i.op1.op);
1276 if (dest.type != VarType::VAR_STRING || dest.var_value.buffer_size == 0) {
1277 throw mx::Exception("GETLINE destination must be a string buffer variable");
1278 }
1279 static int over_count = 0;
1280 out << "\tleaq " << getMangledName(i.op1) << "(%rip), %rdi\n";
1281 out << "\tmovq $" << dest.var_value.buffer_size << ", %rsi\n";
1282 out << "\tmovq " << getPlatformSymbolName("stdin") << "(%rip), %rdx\n";
1283 out << "\tmovq %rsp, %rbx\n";
1284 out << "\tandq $-16, %rsp\n";
1285 out << "\tcall " << getPlatformSymbolName("fgets") << "\n";
1286 out << "\tmovq %rbx, %rsp\n";
1287 out << "\tleaq " << getMangledName(i.op1) << "(%rip), %rdi\n";
1288 out << "\tmovq %rsp, %rbx\n";
1289 out << "\tandq $-16, %rsp\n";
1290 out << "\tcall " << getPlatformSymbolName("strlen") << "\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";
1296 out << "\tleaq " << getMangledName(i.op1) << "(%rip), %rdi\n";
1297 out << "\tmovb $0, (%rdi, %rcx, 1)\n";
1298 out << ".over" << over_count << ": \n";
1299 over_count++;
1300 }
1301
1302 void Program::gen_bitop(std::ostream &out, const std::string &opc, const Instruction &i) {
1303 if (i.op3.op.empty()) {
1304 if (isVariable(i.op1.op)) {
1305 Variable &v = getVariable(i.op1.op);
1306 if (v.type == VarType::VAR_INTEGER) {
1307 generateLoadVar(out, VarType::VAR_INTEGER, "%rax", i.op1);
1308 generateLoadVar(out, VarType::VAR_INTEGER, "%rcx", i.op2);
1309 out << "\t" << opc << "q %rcx, %rax\n";
1310 sysv_emitStoreVar(out, "%rax", i.op1);
1311 } else {
1312 throw mx::Exception("Bitwise operations only supported for integer variables");
1313 }
1314 } else {
1315 throw mx::Exception("First argument of bitop instruction must be a variable.");
1316 }
1317 } else {
1318 if (isVariable(i.op1.op)) {
1319 Variable &v = getVariable(i.op1.op);
1320 if (v.type == VarType::VAR_INTEGER) {
1321 generateLoadVar(out, VarType::VAR_INTEGER, "%rax", i.op2);
1322 generateLoadVar(out, VarType::VAR_INTEGER, "%rcx", i.op3);
1323 out << "\t" << opc << "q %rcx, %rax\n";
1324 sysv_emitStoreVar(out, "%rax", i.op1);
1325 } else {
1326 throw mx::Exception("Bitwise operations only supported for integer variables");
1327 }
1328 } else {
1329 throw mx::Exception("First argument of bitop instruction must be a variable.");
1330 }
1331 }
1332 }
1333
1334 void Program::gen_not(std::ostream &out, const Instruction &i) {
1335 if (!i.op1.op.empty()) {
1336 if (isVariable(i.op1.op)) {
1337 Variable &v = getVariable(i.op1.op);
1338 if (v.type != VarType::VAR_INTEGER) {
1339 throw mx::Exception("NOT instruction expect integer variable");
1340 }
1341 generateLoadVar(out, VarType::VAR_INTEGER, "%rax", i.op1);
1342 out << "\ttestq %rax, %rax\n";
1343 out << "\tsete %al\n";
1344 out << "\tmovzbq %al, %rax\n";
1345 sysv_emitStoreVar(out, "%rax", i.op1);
1346 } else {
1347 throw mx::Exception("NOT instruction requires variable");
1348 }
1349 } else {
1350 throw mx::Exception("NOT instruction requires operand");
1351 }
1352 }
1353
1365 void Program::gen_push(std::ostream &out, const Instruction &i) {
1366 if (isVariable(i.op1.op)) {
1367 Variable &v = getVariable(i.op1.op);
1369 sysv_emitLoadVar(out, "%rax", i.op1);
1370 out << "\tpushq %rax\n";
1371 } else if (v.type == VarType::VAR_STRING) {
1372 out << "\tleaq " << getMangledName(i.op1) << "(%rip), %rax\n";
1373 out << "\tpushq %rax\n";
1374 } else if (v.type == VarType::VAR_FLOAT) {
1375 out << "\tmovsd " << getMangledName(i.op1) << "(%rip), %xmm0\n";
1376 out << "\tsubq $8, %rsp\n";
1377 out << "\tmovsd %xmm0, (%rsp)\n";
1378 } else {
1379 throw mx::Exception("PUSH only supports integer, pointer, or float variables");
1380 }
1381 } else if (i.op1.type == OperandType::OP_CONSTANT) {
1382 out << "\tmovq $" << i.op1.op << ", %rax\n";
1383 out << "\tpushq %rax\n";
1384 } else {
1385 throw mx::Exception("PUSH argument must be a variable or integer constant");
1386 }
1387 }
1388
1400 void Program::gen_pop(std::ostream &out, const Instruction &i) {
1401 if (!isVariable(i.op1.op)) {
1402 throw mx::Exception("POP destination must be a variable");
1403 }
1404 Variable &v = getVariable(i.op1.op);
1405 if (v.type == VarType::VAR_INTEGER) {
1406 out << "\tpopq %rax\n";
1407 sysv_emitStoreVar(out, "%rax", i.op1);
1408 } else if (v.type == VarType::VAR_POINTER || v.type == VarType::VAR_EXTERN) {
1409 out << "\tpopq %rax\n";
1410 out << "\tmovq %rax, " << getMangledName(i.op1) << "(%rip)\n";
1411 } else if (v.type == VarType::VAR_FLOAT) {
1412 out << "\tmovsd (%rsp), %xmm0\n";
1413 out << "\taddq $8, %rsp\n";
1414 out << "\tmovsd %xmm0, " << getMangledName(i.op1) << "(%rip)\n";
1415 } else {
1416 throw mx::Exception("POP only supports integer, pointer, or float variables");
1417 }
1418 }
1419
1420 void Program::gen_stack_load(std::ostream &out, const Instruction &i) {
1421 if (!isVariable(i.op1.op)) {
1422 throw mx::Exception("stack_load, requires first argument to be a variable.");
1423 }
1424 Variable &dest = getVariable(i.op1.op);
1425 if (i.op2.op.empty()) {
1426 throw mx::Exception("stack_load, requires second argument");
1427 }
1428 generateLoadVar(out, dest.type, "%rax", i.op2);
1429 out << "\tmovq (%rsp, %rax, 8), " << "%rcx\n";
1430 sysv_emitStoreVar(out, "%rcx", i.op1);
1431 }
1432
1433 void Program::gen_stack_store(std::ostream &out, const Instruction &i) {
1434 if (!isVariable(i.op1.op)) {
1435 throw mx::Exception("stack_store, requires first argument to be a variable.");
1436 }
1437 Variable &dest = getVariable(i.op1.op);
1438 if (i.op2.op.empty()) {
1439 throw mx::Exception("stack_store, requires second argument");
1440 }
1441 generateLoadVar(out, dest.type, "%rax", i.op2);
1442 sysv_emitLoadVar(out, "%rcx", i.op1);
1443 out << "\tmovq %rcx, (%rsp, %rax, 8)\n";
1444 }
1445
1446 void Program::gen_stack_sub(std::ostream &out, const Instruction &i) {
1447 if (!i.op1.op.empty()) {
1448 if (isVariable(i.op1.op)) {
1449 sysv_emitLoadVar(out, "%rcx", i.op1);
1450 } else {
1451 out << "\tmovq $" << i.op1.op << ", %rcx\n";
1452 }
1453 } else {
1454 out << "\tmovq $1, %rcx\n";
1455 }
1456 out << "\tshl $3, %rcx\n";
1457 out << "\taddq %rcx, %rsp\n";
1458 }
1459
1460 int Program::generateLoadVar(std::ostream &out, int r, const Operand &op) {
1461 int count = 0;
1462
1463 if (isVariable(op.op)) {
1464 if (op.type != OperandType::OP_VARIABLE) {
1465 throw mx::Exception("Operand expected variable instead I found: " + op.op);
1466 }
1467 Variable &v = getVariable(op.op);
1468
1469 std::string reg = getRegisterByIndex(r, v.type);
1470 switch (v.type) {
1473 case VarType::VAR_EXTERN: {
1474 auto ra = sysv_reg_vars.find(op.op);
1475 if (ra != sysv_reg_vars.end()) {
1476 if (ra->second != reg)
1477 out << "\tmovq " << ra->second << ", " << reg << "\n";
1478 } else {
1479 out << "\tmovq " << getPlatformSymbolName(getMangledName(op)) << "(%rip), " << reg << "\n";
1480 }
1481 count = 0;
1482 break;
1483 }
1484 case VarType::VAR_FLOAT:
1485 out << "\tmovsd " << getPlatformSymbolName(getMangledName(op)) << "(%rip), " << reg << "\n";
1486 count = 1;
1487 break;
1489 count = 0;
1490 out << "\tleaq " << getPlatformSymbolName(getMangledName(op)) << "(%rip), " << reg << "\n";
1491 break;
1492 case VarType::VAR_BYTE:
1493 count = 0;
1494 out << "\tmovzbq " << getPlatformSymbolName(getMangledName(op)) << "(%rip), " << reg << "\n";
1495 break;
1496 default:
1497 break;
1498 }
1499 } else {
1500 if (op.type == OperandType::OP_CONSTANT) {
1501 out << "\tmovq $" << op.op << ", " << getRegisterByIndex(r, VarType::VAR_INTEGER) << "\n";
1502 }
1503 }
1504 return count;
1505 }
1506
1507 int Program::generateLoadVar(std::ostream &out, VarType reqType, std::string reg, const Operand &op) {
1508 int count = 0;
1509 if (isVariable(op.op)) {
1510 if (op.type != OperandType::OP_VARIABLE) {
1511 throw mx::Exception("Operand expected variable instead I found: " + op.op);
1512 }
1513 Variable &v = getVariable(op.op);
1514 if (reqType == VarType::VAR_FLOAT) {
1515 if (v.type == VarType::VAR_FLOAT) {
1516 out << "\tmovsd " << getPlatformSymbolName(getMangledName(op)) << "(%rip), " << reg << "\n";
1517 count = 1;
1519 auto ra = sysv_reg_vars.find(op.op);
1520 if (ra != sysv_reg_vars.end()) {
1521 out << "\tmovq " << ra->second << ", %rax\n";
1522 } else {
1523 out << "\tmovq " << getPlatformSymbolName(getMangledName(op)) << "(%rip), %rax\n";
1524 }
1525 out << "\tcvtsi2sd %rax, " << reg << "\n";
1526 count = 1;
1527 } else if (v.type == VarType::VAR_BYTE) {
1528 out << "\tmovzbq " << getPlatformSymbolName(getMangledName(op)) << "(%rip), %rax\n";
1529 out << "\tcvtsi2sd %rax, " << reg << "\n";
1530 count = 1;
1531 } else if (v.type == VarType::VAR_STRING) {
1532 out << "\tleaq " << getPlatformSymbolName(getMangledName(op)) << "(%rip), %rdi\n";
1533 out << "\tmovq %rsp, %rbx\n";
1534 out << "\tandq $-16, %rsp\n";
1535 out << "\tcall " << getPlatformSymbolName("atof") << "\n";
1536 out << "\tmovq %rbx, %rsp\n";
1537 out << "\tmovsd %xmm0, " << reg << "\n";
1538 count = 1;
1539 } else {
1540 throw mx::Exception("LoadVar: unsupported source type for float");
1541 }
1542 return count;
1543 }
1544
1545 if (reqType == VarType::VAR_INTEGER || reqType == VarType::VAR_POINTER || reqType == VarType::VAR_EXTERN) {
1547 auto ra = sysv_reg_vars.find(op.op);
1548 if (ra != sysv_reg_vars.end()) {
1549 if (ra->second != reg)
1550 out << "\tmovq " << ra->second << ", " << reg << "\n";
1551 } else {
1552 out << "\tmovq " << getPlatformSymbolName(getMangledName(op)) << "(%rip), " << reg << "\n";
1553 }
1554 } else if (v.type == VarType::VAR_BYTE) {
1555 out << "\tmovzbq " << getPlatformSymbolName(getMangledName(op)) << "(%rip), " << reg << "\n";
1556 } else if (v.type == VarType::VAR_FLOAT) {
1557 out << "\tmovsd " << getPlatformSymbolName(getMangledName(op)) << "(%rip), %xmm0\n";
1558 out << "\tcvttsd2si %xmm0, " << reg << "\n";
1559 } else if (v.type == VarType::VAR_STRING) {
1560 out << "\tleaq " << getPlatformSymbolName(getMangledName(op)) << "(%rip), %rdi\n";
1561 out << "\tmovq %rsp, %rbx\n";
1562 out << "\tandq $-16, %rsp\n";
1563 out << "\tcall " << getPlatformSymbolName("atol") << "\n";
1564 out << "\tmovq %rbx, %rsp\n";
1565 out << "\tmovq %rax, " << reg << "\n";
1566 } else {
1567 throw mx::Exception("LoadVar: unsupported source type for integer");
1568 }
1569 return count;
1570 }
1571
1572 if (reqType == VarType::VAR_STRING) {
1573 if (v.type == VarType::VAR_STRING) {
1574 out << "\tleaq " << getPlatformSymbolName(getMangledName(op)) << "(%rip), " << reg << "\n";
1576 auto ra = sysv_reg_vars.find(op.op);
1577 if (ra != sysv_reg_vars.end()) {
1578 if (ra->second != reg)
1579 out << "\tmovq " << ra->second << ", " << reg << "\n";
1580 } else {
1581 out << "\tmovq " << getPlatformSymbolName(getMangledName(op)) << "(%rip), " << reg << "\n";
1582 }
1583 } else {
1584 throw mx::Exception("LoadVar: unsupported source type for string");
1585 }
1586 return count;
1587 }
1588
1589 switch (v.type) {
1592 case VarType::VAR_EXTERN: {
1593 auto ra = sysv_reg_vars.find(op.op);
1594 if (ra != sysv_reg_vars.end()) {
1595 if (ra->second != reg)
1596 out << "\tmovq " << ra->second << ", " << reg << "\n";
1597 } else {
1598 out << "\tmovq " << getPlatformSymbolName(getMangledName(op)) << "(%rip), " << reg << "\n";
1599 }
1600 count = 0;
1601 break;
1602 }
1603 case VarType::VAR_BYTE:
1604 out << "\tmovzbq " << getPlatformSymbolName(getMangledName(op)) << "(%rip), " << reg << "\n";
1605 break;
1606 case VarType::VAR_FLOAT:
1607 out << "\tmovsd " << getPlatformSymbolName(getMangledName(op)) << "(%rip), " << reg << "\n";
1608 count = 1;
1609 break;
1611 out << "\tleaq " << getPlatformSymbolName(getMangledName(op)) << "(%rip), " << reg << "\n";
1612 break;
1613 default:
1614 break;
1615 }
1616 } else {
1617 if (op.type == OperandType::OP_CONSTANT) {
1618 if (reqType == VarType::VAR_INTEGER || reqType == VarType::VAR_POINTER || reqType == VarType::VAR_EXTERN) {
1619 out << "\tmovq $" << op.op << ", " << reg << "\n";
1620 } else if (reqType == VarType::VAR_FLOAT) {
1621 out << "\tmovq $" << op.op << ", %rax\n";
1622 out << "\tcvtsi2sd %rax, " << reg << "\n";
1623 count = 1;
1624 } else if (reqType == VarType::VAR_STRING) {
1625 out << "\tmovq $" << op.op << ", " << reg << "\n";
1626 } else {
1627 out << "\tmovq $" << op.op << ", " << reg << "\n";
1628 }
1629 }
1630 }
1631 return count;
1632 }
1633
1634 std::string Program::getRegisterByIndex(int index, VarType type) {
1635 if (index < 6 && (type == VarType::VAR_INTEGER || type == VarType::VAR_STRING || type == VarType::VAR_POINTER || type == VarType::VAR_EXTERN || type == VarType::VAR_BYTE)) {
1636 static const char *reg[] = {"%rdi", "%rsi", "%rdx", "%rcx", "%r8", "%r9"};
1637 return reg[index];
1638 } else if (index < 9 && type == VarType::VAR_FLOAT) {
1639 return "%xmm" + std::to_string(xmm_offset++);
1640 }
1641 return "[stack]";
1642 }
1643
1644 void Program::gen_print(std::ostream &out, const Instruction &i) {
1645 xmm_offset = 0;
1646 out << "\tleaq " << getMangledName(i.op1) << "(%rip), %rdi\n";
1647 std::vector<Operand> args;
1648 if (!i.op2.op.empty())
1649 args.push_back(i.op2);
1650 if (!i.op3.op.empty())
1651 args.push_back(i.op3);
1652 for (const auto &v : i.vop) {
1653 if (!v.op.empty())
1654 args.push_back(v);
1655 }
1656
1657 int total = 0;
1658 const size_t stack_args = (args.size() > 5) ? (args.size() - 5) : 0;
1659
1660 out << "\tmovq %rsp, %rbx\n";
1661 out << "\tandq $-16, %rsp\n";
1662
1663 if (stack_args) {
1664 size_t stack_bytes = stack_args * 8;
1665 size_t aligned = (stack_bytes + 15) & ~(size_t)15;
1666 out << "\tsubq $" << aligned << ", %rsp\n";
1667 size_t slot = 0;
1668 for (size_t idx = 6; idx <= args.size(); ++idx, ++slot) {
1669 size_t aidx = idx - 1;
1670 if (isVariable(args[aidx].op)) {
1671 Variable &v = getVariable(args[aidx].op);
1672 if (v.type == VarType::VAR_FLOAT) {
1673 generateLoadVar(out, VarType::VAR_FLOAT, "%xmm0", args[aidx]);
1674 out << "\tmovsd %xmm0, " << (slot * 8) << "(%rsp)\n";
1675 } else {
1676 generateLoadVar(out, v.type, "%rax", args[aidx]);
1677 out << "\tmovq %rax, " << (slot * 8) << "(%rsp)\n";
1678 }
1679 } else {
1680 out << "\tmovq $" << args[aidx].op << ", " << (slot * 8) << "(%rsp)\n";
1681 }
1682 }
1683 }
1684
1685 int reg_count = 1;
1686 for (size_t z = 0; z < args.size() && reg_count < 6; ++z, ++reg_count) {
1687 total += generateLoadVar(out, reg_count, args[z]);
1688 }
1689 if (total == 0) {
1690 out << "\txor %eax, %eax\n";
1691 } else {
1692 out << "\tmovb $" << total << ", %al\n";
1693 }
1694 out << "\tcall " << getPlatformSymbolName("printf") << "\n";
1695 out << "\tmovq %rbx, %rsp\n";
1696 }
1697
1698 void Program::gen_jmp(std::ostream &out, const Instruction &i) {
1699 if (!i.op1.op.empty()) {
1700 auto pos = labels.find(i.op1.op);
1701 if (pos == labels.end()) {
1702 throw mx::Exception("Jump instruction must have valid label: " + i.op1.op);
1703 }
1704 const char *mnem = nullptr;
1705 switch (i.instruction) {
1706 case JMP:
1707 mnem = "jmp";
1708 break;
1709 case JE:
1710 mnem = "je";
1711 break;
1712 case JNE:
1713 mnem = "jne";
1714 break;
1715 case JZ:
1716 mnem = "jz";
1717 break;
1718 case JNZ:
1719 mnem = "jnz";
1720 break;
1721
1722 case JL:
1723 mnem = (last_cmp_type == CMP_FLOAT) ? "jb" : "jl";
1724 break;
1725 case JLE:
1726 mnem = (last_cmp_type == CMP_FLOAT) ? "jbe" : "jle";
1727 break;
1728 case JG:
1729 mnem = (last_cmp_type == CMP_FLOAT) ? "ja" : "jg";
1730 break;
1731 case JGE:
1732 mnem = (last_cmp_type == CMP_FLOAT) ? "jae" : "jge";
1733 break;
1734 case JA:
1735 mnem = "ja";
1736 break;
1737 case JB:
1738 mnem = "jb";
1739 break;
1740 default:
1741 throw mx::Exception("Invalid jump opcode for gen_jmp");
1742 }
1743 out << "\t" << mnem << " ." << i.op1.op << "\n";
1744 } else {
1745 throw mx::Exception("Jump instruction must have label: " + i.op1.op);
1746 }
1747 }
1748
1749 void Program::gen_cmp(std::ostream &out, const Instruction &i) {
1750 if (i.op2.op.empty()) {
1751 throw mx::Exception("CMP requires two operands");
1752 }
1753
1756 if (isVariable(i.op1.op)) {
1757 type1 = getVariable(i.op1.op).type;
1758 }
1759 if (isVariable(i.op2.op)) {
1760 type2 = getVariable(i.op2.op).type;
1761 }
1762
1763 if (type1 == VarType::VAR_FLOAT || type2 == VarType::VAR_FLOAT) {
1764 generateLoadVar(out, VarType::VAR_FLOAT, "%xmm0", i.op1);
1765 generateLoadVar(out, VarType::VAR_FLOAT, "%xmm1", i.op2);
1766 out << "\tcomisd %xmm1, %xmm0\n";
1768
1769 } else if (type1 == VarType::VAR_POINTER && (type2 == VarType::VAR_INTEGER || type2 == VarType::VAR_BYTE)) {
1770 generateLoadVar(out, type1, "%rax", i.op1);
1771 generateLoadVar(out, type2, "%rcx", i.op2);
1772 out << "\tcmpq %rcx, %rax\n";
1774 } else if (!isVariable(i.op2.op) && i.op2.type == OperandType::OP_CONSTANT && isVariable(i.op1.op)) {
1775 auto ra = sysv_reg_vars.find(i.op1.op);
1776 if (ra != sysv_reg_vars.end()) {
1777 out << "\tcmpq $" << i.op2.op << ", " << ra->second << "\n";
1778 } else if (type1 == VarType::VAR_BYTE) {
1779 out << "\tmovzbq " << getMangledName(i.op1) << "(%rip), %rax\n";
1780 out << "\tcmpq $" << i.op2.op << ", %rax\n";
1781 } else {
1782 out << "\tcmpq $" << i.op2.op << ", " << getMangledName(i.op1) << "(%rip)\n";
1783 }
1785 } else {
1786 generateLoadVar(out, type1, "%rax", i.op1);
1787 generateLoadVar(out, type2, "%rcx", i.op2);
1788 out << "\tcmpq %rcx, %rax\n";
1790 }
1791 }
1792
1793 void Program::gen_fcmp(std::ostream &out, const Instruction &i) {
1794 if (i.op2.op.empty()) {
1795 throw mx::Exception("FCMP requires two operands");
1796 }
1797
1798 generateLoadVar(out, VarType::VAR_FLOAT, "%xmm0", i.op1);
1799 generateLoadVar(out, VarType::VAR_FLOAT, "%xmm1", i.op2);
1800 out << "\tcomisd %xmm1, %xmm0\n";
1802 }
1803
1804 void Program::gen_jae(std::ostream &out, const Instruction &i) {
1805 out << "\tjae ." << i.op1.op << "\n";
1806 }
1807
1808 void Program::gen_jbe(std::ostream &out, const Instruction &i) {
1809 out << "\tjbe ." << i.op1.op << "\n";
1810 }
1811
1812 void Program::gen_jc(std::ostream &out, const Instruction &i) {
1813 out << "\tjc ." << i.op1.op << "\n";
1814 }
1815
1816 void Program::gen_jnc(std::ostream &out, const Instruction &i) {
1817 out << "\tjnc ." << i.op1.op << "\n";
1818 }
1819
1820 void Program::gen_jp(std::ostream &out, const Instruction &i) {
1821 out << "\tjp ." << i.op1.op << "\n";
1822 }
1823
1824 void Program::gen_jnp(std::ostream &out, const Instruction &i) {
1825 out << "\tjnp ." << i.op1.op << "\n";
1826 }
1827
1828 void Program::gen_jo(std::ostream &out, const Instruction &i) {
1829 out << "\tjo ." << i.op1.op << "\n";
1830 }
1831
1832 void Program::gen_jno(std::ostream &out, const Instruction &i) {
1833 out << "\tjno ." << i.op1.op << "\n";
1834 }
1835
1836 void Program::gen_js(std::ostream &out, const Instruction &i) {
1837 out << "\tjs ." << i.op1.op << "\n";
1838 }
1839
1840 void Program::gen_jns(std::ostream &out, const Instruction &i) {
1841 out << "\tjns ." << i.op1.op << "\n";
1842 }
1843
1844 void Program::gen_mov(std::ostream &out, const Instruction &i) {
1845 if (!isVariable(i.op1.op)) {
1846 throw mx::Exception("MOV destination must be a variable: " + i.op1.op);
1847 }
1848 Variable &dest = getVariable(i.op1.op);
1849
1850 if (isVariable(i.op2.op)) {
1851 Variable &src = getVariable(i.op2.op);
1852 if (dest.type == VarType::VAR_FLOAT && src.type == VarType::VAR_FLOAT) {
1853 out << "\tmovsd " << getMangledName(i.op2) << "(%rip), %xmm0\n";
1854 out << "\tmovsd %xmm0, " << getMangledName(i.op1) << "(%rip)\n";
1855 } else if (dest.type == VarType::VAR_FLOAT && src.type != VarType::VAR_FLOAT) {
1856 if (src.type == VarType::VAR_BYTE) {
1857 out << "\tmovzbq " << getMangledName(i.op2) << "(%rip), %rax\n";
1858 out << "\tcvtsi2sd %rax, %xmm0\n";
1859 } else if (src.type == VarType::VAR_STRING) {
1860 out << "\tleaq " << getMangledName(i.op2) << "(%rip), %rdi\n";
1861 out << "\tmovq %rsp, %rbx\n";
1862 out << "\tandq $-16, %rsp\n";
1863 out << "\tcall " << getPlatformSymbolName("atof") << "\n";
1864 out << "\tmovq %rbx, %rsp\n";
1865 } else {
1866 sysv_emitLoadVar(out, "%rax", i.op2);
1867 out << "\tcvtsi2sd %rax, %xmm0\n";
1868 }
1869 out << "\tmovsd %xmm0, " << getMangledName(i.op1) << "(%rip)\n";
1870 } else if (dest.type != VarType::VAR_FLOAT && src.type == VarType::VAR_FLOAT) {
1871 out << "\tmovsd " << getMangledName(i.op2) << "(%rip), %xmm0\n";
1872 out << "\tcvttsd2si %xmm0, %rax\n";
1873 if (dest.type == VarType::VAR_BYTE) {
1874 out << "\tmovb %al, " << getMangledName(i.op1) << "(%rip)\n";
1875 } else {
1876 sysv_emitStoreVar(out, "%rax", i.op1);
1877 }
1878 } else if (dest.type == VarType::VAR_POINTER && src.type == VarType::VAR_STRING) {
1879 out << "\tleaq " << getMangledName(i.op2) << "(%rip), %rax\n";
1880 out << "\tmovq %rax, " << getMangledName(i.op1) << "(%rip)\n";
1881 } else if (dest.type == VarType::VAR_INTEGER && src.type == VarType::VAR_STRING) {
1882 out << "\tleaq " << getMangledName(i.op2) << "(%rip), %rdi\n";
1883 out << "\tmovq %rsp, %rbx\n";
1884 out << "\tandq $-16, %rsp\n";
1885 out << "\tcall " << getPlatformSymbolName("atol") << "\n";
1886 out << "\tmovq %rbx, %rsp\n";
1887 sysv_emitStoreVar(out, "%rax", i.op1);
1888 } else if (dest.type == VarType::VAR_BYTE && src.type != VarType::VAR_BYTE) {
1889 if (src.type == VarType::VAR_FLOAT) {
1890 out << "\tmovsd " << getMangledName(i.op2) << "(%rip), %xmm0\n";
1891 out << "\tcvttsd2si %xmm0, %rax\n";
1892 out << "\tmovb %al, " << getMangledName(i.op1) << "(%rip)\n";
1893 } else {
1894 sysv_emitLoadVar(out, "%rax", i.op2);
1895 out << "\tmovb %al, " << getMangledName(i.op1) << "(%rip)\n";
1896 }
1897 } else if (dest.type != VarType::VAR_BYTE && src.type == VarType::VAR_BYTE) {
1898 out << "\tmovzbq " << getMangledName(i.op2) << "(%rip), %rax\n";
1899 if (dest.type == VarType::VAR_FLOAT) {
1900 out << "\tcvtsi2sd %rax, %xmm0\n";
1901 out << "\tmovsd %xmm0, " << getMangledName(i.op1) << "(%rip)\n";
1902 } else {
1903 sysv_emitStoreVar(out, "%rax", i.op1);
1904 }
1905 } else {
1906 switch (dest.type) {
1910 sysv_emitLoadVar(out, "%rax", i.op2);
1911 sysv_emitStoreVar(out, "%rax", i.op1);
1912 break;
1913 case VarType::VAR_BYTE:
1914 out << "\tmovb " << getMangledName(i.op2) << "(%rip), %al\n";
1915 out << "\tmovb %al, " << getMangledName(i.op1) << "(%rip)\n";
1916 break;
1918 out << "\tleaq " << getMangledName(i.op2) << "(%rip), %rax\n";
1919 out << "\tmovq %rax, " << getMangledName(i.op1) << "(%rip)\n";
1920 break;
1921 default:
1922 throw mx::Exception("MOV: unsupported destination type");
1923 }
1924 }
1925 } else {
1927 if (dest.type == VarType::VAR_FLOAT) {
1928 double val = std::stod(i.op2.op);
1929 uint64_t bits;
1930 std::memcpy(&bits, &val, sizeof(bits));
1931 out << "\tmovq $" << bits << ", %rax\n";
1932 out << "\tmovq %rax, " << getMangledName(i.op1) << "(%rip)\n";
1933 } else if (dest.type == VarType::VAR_BYTE) {
1934 out << "\tmovb $" << i.op2.op << ", " << getMangledName(i.op1) << "(%rip)\n";
1935 } else {
1936 sysv_emitStoreVarImm(out, i.op2.op, i.op1);
1937 }
1938 } else {
1939 throw mx::Exception("MOV: unsupported source operand type");
1940 }
1941 }
1942 }
1943
1944 void Program::gen_arth(std::ostream &out, std::string arth, const Instruction &i) {
1945 auto emitIntegerOp = [&](const Operand &lhs, const Operand &rhs, const Operand &dest) {
1946 if (arth == "mul")
1947 arth = "imul";
1948 if (!isVariable(rhs.op) && rhs.type == OperandType::OP_CONSTANT && arth != "imul" && lhs.op == dest.op && isVariable(lhs.op)) {
1949 auto ra = sysv_reg_vars.find(lhs.op);
1950 if (ra != sysv_reg_vars.end()) {
1951 out << "\t" << arth << "q $" << rhs.op << ", " << ra->second << "\n";
1952 } else {
1953 out << "\t" << arth << "q $" << rhs.op << ", " << getMangledName(lhs) << "(%rip)\n";
1954 }
1955 return;
1956 }
1957 generateLoadVar(out, VarType::VAR_INTEGER, "%r10", lhs);
1958 if (!isVariable(rhs.op) && rhs.type == OperandType::OP_CONSTANT) {
1959 out << "\t" << arth << "q $" << rhs.op << ", %r10\n";
1960 } else {
1961 generateLoadVar(out, VarType::VAR_INTEGER, "%r11", rhs);
1962 out << "\t" << arth << "q %r11, %r10\n";
1963 }
1964 sysv_emitStoreVar(out, "%r10", dest);
1965 };
1966
1967 if (i.op3.op.empty()) {
1968 if (!isVariable(i.op1.op)) {
1969 throw mx::Exception("First argument of arithmetic must be a variable: " + i.op1.op);
1970 }
1971 Variable &v = getVariable(i.op1.op);
1972
1974 emitIntegerOp(i.op1, i.op2, i.op1);
1975 } else if (v.type == VarType::VAR_FLOAT) {
1976 generateLoadVar(out, VarType::VAR_FLOAT, "%xmm0", i.op1);
1977 generateLoadVar(out, VarType::VAR_FLOAT, "%xmm1", i.op2);
1978 out << "\t" << arth << "sd %xmm1, %xmm0\n";
1979 out << "\tmovsd %xmm0, " << getMangledName(i.op1) << "(%rip)\n";
1980 } else {
1981 throw mx::Exception("Unsupported variable type for arithmetic");
1982 }
1983 } else {
1984 if (!isVariable(i.op1.op)) {
1985 throw mx::Exception("First argument of arithmetic must be a variable: " + i.op1.op);
1986 }
1987 Variable &v = getVariable(i.op1.op);
1988
1990 emitIntegerOp(i.op2, i.op3, i.op1);
1991 } else if (v.type == VarType::VAR_FLOAT) {
1992 generateLoadVar(out, VarType::VAR_FLOAT, "%xmm0", i.op2);
1993 generateLoadVar(out, VarType::VAR_FLOAT, "%xmm1", i.op3);
1994 out << "\t" << arth << "sd %xmm1, %xmm0\n";
1995 out << "\tmovsd %xmm0, " << getMangledName(i.op1) << "(%rip)\n";
1996 } else {
1997 throw mx::Exception("Unsupported variable type for arithmetic");
1998 }
1999 }
2000 }
2001
2002 void Program::gen_exit(std::ostream &out, const Instruction &i) {
2003 bool uses_std_module = false;
2004 for (const auto &e : external) {
2005 if (e.mod == "std") {
2006 uses_std_module = true;
2007 break;
2008 }
2009 }
2010 if (uses_std_module) {
2011 out << "\tmovq %rsp, %rbx\n";
2012 out << "\tandq $-16, %rsp\n";
2013 out << "\tcall " << getPlatformSymbolName("free_program_args") << "\n";
2014 out << "\tmovq %rbx, %rsp\n";
2015 }
2016
2017 if (!i.op1.op.empty()) {
2018 std::vector<Operand> opz;
2019 opz.push_back(i.op1);
2020 generateFunctionCall(out, "exit", opz);
2021 } else {
2022 throw mx::Exception("exit requires argument.\n");
2023 }
2024 }
2025} // 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
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.
Definition icode_gen.cpp:79
void sysv_emitFlushRegs(std::ostream &out)
Write all register-allocated variables back to memory.
Definition icode_gen.cpp:67
void gen_cmp(std::ostream &out, const Instruction &i)
std::vector< std::string > sysv_reg_save_order
Definition icode.hpp:678
bool isFunctionValid(const std::string &label)
Check whether a label exists and is marked as a function entry.
Definition icode.cpp:175
void gen_jc(std::ostream &out, const Instruction &i)
void sysv_emitRestoreRegs(std::ostream &out)
Restore callee-saved registers at function epilogue.
Definition icode_gen.cpp:85
void add_standard()
Register standard library runtime functions.
Definition icode.cpp:107
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).
Definition icode_gen.cpp:91
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
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
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).
Definition icode_gen.cpp:14
Platform platform
Definition icode.hpp:861
bool last_call_returns_owned_ptr
tracks ownership of last call's return value
Definition icode.hpp:418
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.
Definition icode_gen.cpp:24
std::unordered_map< std::string, std::string > sysv_reg_vars
SysV variable-to-register mapping.
Definition icode.hpp:677
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.
Definition icode.cpp:22
void generateCode(const Platform &platform, bool obj, std::ostream &out)
Generate native x64 assembly for the entire program.
LastCmpType last_cmp_type
Definition icode.hpp:417
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)
std::string name
Definition icode.hpp:306
void gen_jmp(std::ostream &out, const Instruction &i)
void sysv_emitReloadRegs(std::ostream &out)
Reload register-allocated variables from memory.
Definition icode_gen.cpp:73
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
Definition icode.hpp:406
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)
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
bool isFunctionReturningOwnedPtr(const std::string &funcName)
Check whether a named function returns an owned (heap-allocated) pointer.
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
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