| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // Copyright (c) 2021-2025 ChilliBits. All rights reserved. | ||
| 2 | |||
| 3 | #include "IRGenerator.h" | ||
| 4 | |||
| 5 | #include <driver/Driver.h> | ||
| 6 | |||
| 7 | namespace spice::compiler { | ||
| 8 | |||
| 9 | 1 | std::string IRGenerator::getSysCallAsmString(uint8_t numRegs) const { | |
| 10 | 1 | const llvm::Triple &targetTriple = cliOptions.targetTriple; | |
| 11 |
1/2✓ Branch 2 → 3 taken 1 time.
✗ Branch 2 → 117 not taken.
|
1 | std::stringstream asmString; |
| 12 | |||
| 13 | // For each OS/architecture we have a mapping of operand index -> register. | ||
| 14 |
2/4✓ Branch 3 → 4 taken 1 time.
✗ Branch 3 → 115 not taken.
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 46 taken 1 time.
|
1 | if (targetTriple.isOSDarwin()) { |
| 15 | ✗ | if (targetTriple.getArch() == llvm::Triple::ArchType::x86_64) { | |
| 16 | static constexpr const char *regs[] = {"%rax", "%rdi", "%rsi", "%rdx", "%r10", "%r8", "%r9"}; | ||
| 17 | ✗ | for (uint8_t i = 0; i < numRegs; i++) { | |
| 18 | // macOS syscall numbers are offset by 0x2000000 | ||
| 19 | ✗ | if (i == 0) | |
| 20 | ✗ | asmString << "movq $(" << std::to_string(0x2000000) << " + " << std::to_string(i) << "), " << regs[i] << "\n"; | |
| 21 | else | ||
| 22 | ✗ | asmString << "movq $" << std::to_string(i) << ", " << regs[i] << "\n"; | |
| 23 | } | ||
| 24 | ✗ | asmString << "syscall\n"; | |
| 25 | ✗ | } else if (targetTriple.isAArch64()) { | |
| 26 | static constexpr const char *regs[] = {"x16", "x0", "x1", "x2", "x3", "x4", "x5"}; | ||
| 27 | ✗ | for (uint8_t i = 0; i < numRegs; ++i) | |
| 28 | ✗ | asmString << "mov " << regs[i] << ", $" << std::to_string(i) << "\n"; | |
| 29 | ✗ | asmString << "svc #0x80\n"; | |
| 30 | } else { // LCOV_EXCL_LINE | ||
| 31 | − | assert_fail("Unsupported macOS target for inline assembly"); // LCOV_EXCL_LINE | |
| 32 | } // LCOV_EXCL_LINE | ||
| 33 |
1/2✓ Branch 47 → 48 taken 1 time.
✗ Branch 47 → 88 not taken.
|
1 | } else if (targetTriple.isOSLinux()) { |
| 34 |
1/2✓ Branch 49 → 50 taken 1 time.
✗ Branch 49 → 61 not taken.
|
1 | if (targetTriple.getArch() == llvm::Triple::ArchType::x86_64) { |
| 35 | static constexpr const char *regs[] = {"%rax", "%rdi", "%rsi", "%rdx", "%r10", "%r8", "%r9"}; | ||
| 36 |
2/2✓ Branch 59 → 51 taken 4 times.
✓ Branch 59 → 60 taken 1 time.
|
5 | for (uint8_t i = 0; i < numRegs; ++i) |
| 37 |
5/10✓ Branch 51 → 52 taken 4 times.
✗ Branch 51 → 115 not taken.
✓ Branch 53 → 54 taken 4 times.
✗ Branch 53 → 106 not taken.
✓ Branch 54 → 55 taken 4 times.
✗ Branch 54 → 106 not taken.
✓ Branch 55 → 56 taken 4 times.
✗ Branch 55 → 106 not taken.
✓ Branch 56 → 57 taken 4 times.
✗ Branch 56 → 106 not taken.
|
4 | asmString << "movq $" << std::to_string(i) << ", " << regs[i] << "\n"; |
| 38 |
1/2✓ Branch 60 → 89 taken 1 time.
✗ Branch 60 → 115 not taken.
|
1 | asmString << "syscall\n"; |
| 39 | ✗ | } else if (targetTriple.getArch() == llvm::Triple::ArchType::x86) { | |
| 40 | // Note: Using movl for 32-bit registers. | ||
| 41 | static constexpr const char *regs[] = {"%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi", "%ebp"}; | ||
| 42 | ✗ | for (uint8_t i = 0; i < numRegs; ++i) | |
| 43 | ✗ | asmString << "movl $" << std::to_string(i) << ", " << regs[i] << "\n"; | |
| 44 | ✗ | asmString << "int $0x80\n"; | |
| 45 | ✗ | } else if (targetTriple.isAArch64()) { | |
| 46 | static constexpr const char *regs[] = {"x8", "x0", "x1", "x2", "x3", "x4", "x5"}; | ||
| 47 | ✗ | for (uint8_t i = 0; i < numRegs; ++i) | |
| 48 | ✗ | asmString << "mov " << regs[i] << ", $" << std::to_string(i) << "\n"; | |
| 49 | ✗ | asmString << "svc 0\n"; | |
| 50 | } else { // LCOV_EXCL_LINE | ||
| 51 | − | assert_fail("Unsupported Linux target for inline assembly"); // LCOV_EXCL_LINE | |
| 52 | } // LCOV_EXCL_LINE | ||
| 53 | } else { // LCOV_EXCL_LINE | ||
| 54 | − | assert_fail("Unsupported target for inline assembly"); // LCOV_EXCL_LINE | |
| 55 | } // LCOV_EXCL_LINE | ||
| 56 | |||
| 57 |
1/2✓ Branch 89 → 90 taken 1 time.
✗ Branch 89 → 115 not taken.
|
2 | return asmString.str(); |
| 58 | 1 | } | |
| 59 | |||
| 60 | 1 | std::string IRGenerator::getSysCallConstraintString(uint8_t numRegs) const { | |
| 61 | 1 | const llvm::Triple &targetTriple = cliOptions.targetTriple; | |
| 62 |
1/2✓ Branch 2 → 3 taken 1 time.
✗ Branch 2 → 87 not taken.
|
1 | std::stringstream constraints; |
| 63 | |||
| 64 | // Generate a comma-separated constraint string: first the operand constraints, | ||
| 65 | // then the corresponding clobbers, then extra clobbers. | ||
| 66 |
2/4✓ Branch 3 → 4 taken 1 time.
✗ Branch 3 → 85 not taken.
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 34 taken 1 time.
|
1 | if (targetTriple.isOSDarwin()) { |
| 67 | ✗ | if (targetTriple.getArch() == llvm::Triple::ArchType::x86_64) { | |
| 68 | static constexpr const char *clobbers[] = {"~{rax}", "~{rdi}", "~{rsi}", "~{rdx}", "~{r10}", "~{r8}", "~{r9}"}; | ||
| 69 | ✗ | for (uint8_t i = 0; i < numRegs; i++) { | |
| 70 | ✗ | constraints << "r"; | |
| 71 | ✗ | if (i != numRegs - 1) | |
| 72 | ✗ | constraints << ","; | |
| 73 | } | ||
| 74 | ✗ | for (uint8_t i = 0; i < numRegs; i++) | |
| 75 | ✗ | constraints << "," << clobbers[i]; | |
| 76 | ✗ | constraints << ",~{dirflag},~{fpsr},~{flags}"; | |
| 77 | ✗ | } else if (targetTriple.isAArch64()) { | |
| 78 | static constexpr const char *clobbers[] = {"~{x16}", "~{x0}", "~{x1}", "~{x2}", "~{x3}", "~{x4}", "~{x5}"}; | ||
| 79 | ✗ | for (uint8_t i = 0; i < numRegs; i++) { | |
| 80 | ✗ | constraints << "r"; | |
| 81 | ✗ | if (i != numRegs - 1) | |
| 82 | ✗ | constraints << ","; | |
| 83 | } | ||
| 84 | ✗ | for (uint8_t i = 0; i < numRegs; i++) | |
| 85 | ✗ | constraints << "," << clobbers[i]; | |
| 86 | ✗ | constraints << ",~{dirflag},~{fpsr},~{flags}"; | |
| 87 | } else { // LCOV_EXCL_LINE | ||
| 88 | − | assert_fail("Unsupported macOS target for inline assembly"); // LCOV_EXCL_LINE | |
| 89 | } // LCOV_EXCL_LINE | ||
| 90 |
1/2✓ Branch 35 → 36 taken 1 time.
✗ Branch 35 → 79 not taken.
|
1 | } else if (targetTriple.isOSLinux()) { |
| 91 |
1/2✓ Branch 37 → 38 taken 1 time.
✗ Branch 37 → 50 not taken.
|
1 | if (targetTriple.getArch() == llvm::Triple::ArchType::x86_64) { |
| 92 | static constexpr const char *clobbers[] = {"~{rax}", "~{rdi}", "~{rsi}", "~{rdx}", "~{r10}", "~{r8}", "~{r9}"}; | ||
| 93 |
2/2✓ Branch 43 → 39 taken 4 times.
✓ Branch 43 → 44 taken 1 time.
|
5 | for (uint8_t i = 0; i < numRegs; i++) { |
| 94 |
1/2✓ Branch 39 → 40 taken 4 times.
✗ Branch 39 → 85 not taken.
|
4 | constraints << "r"; |
| 95 |
2/2✓ Branch 40 → 41 taken 3 times.
✓ Branch 40 → 42 taken 1 time.
|
4 | if (i != numRegs - 1) |
| 96 |
1/2✓ Branch 41 → 42 taken 3 times.
✗ Branch 41 → 85 not taken.
|
3 | constraints << ","; |
| 97 | } | ||
| 98 |
2/2✓ Branch 48 → 45 taken 4 times.
✓ Branch 48 → 49 taken 1 time.
|
5 | for (uint8_t i = 0; i < numRegs; i++) |
| 99 |
2/4✓ Branch 45 → 46 taken 4 times.
✗ Branch 45 → 85 not taken.
✓ Branch 46 → 47 taken 4 times.
✗ Branch 46 → 85 not taken.
|
4 | constraints << "," << clobbers[i]; |
| 100 |
1/2✓ Branch 49 → 80 taken 1 time.
✗ Branch 49 → 85 not taken.
|
1 | constraints << ",~{dirflag},~{fpsr},~{flags}"; |
| 101 | ✗ | } else if (targetTriple.getArch() == llvm::Triple::ArchType::x86) { | |
| 102 | static constexpr const char *clobbers[] = {"~{eax}", "~{ebx}", "~{ecx}", "~{edx}", "~{esi}", "~{edi}", "~{ebp}"}; | ||
| 103 | ✗ | for (uint8_t i = 0; i < numRegs; i++) { | |
| 104 | ✗ | constraints << "r"; | |
| 105 | ✗ | if (i != numRegs - 1) | |
| 106 | ✗ | constraints << ","; | |
| 107 | } | ||
| 108 | ✗ | for (uint8_t i = 0; i < numRegs; i++) | |
| 109 | ✗ | constraints << "," << clobbers[i]; | |
| 110 | ✗ | constraints << ",~{dirflag},~{fpsr},~{flags}"; | |
| 111 | ✗ | } else if (targetTriple.isAArch64()) { | |
| 112 | static constexpr const char *clobbers[] = {"~{x8}", "~{x0}", "~{x1}", "~{x2}", "~{x3}", "~{x4}", "~{x5}"}; | ||
| 113 | ✗ | for (uint8_t i = 0; i < numRegs; i++) { | |
| 114 | ✗ | constraints << "r"; | |
| 115 | ✗ | if (i != numRegs - 1) | |
| 116 | ✗ | constraints << ","; | |
| 117 | } | ||
| 118 | ✗ | for (uint8_t i = 0; i < numRegs; i++) | |
| 119 | ✗ | constraints << "," << clobbers[i]; | |
| 120 | ✗ | constraints << ",~{dirflag},~{fpsr},~{flags}"; | |
| 121 | } else { // LCOV_EXCL_LINE | ||
| 122 | − | assert_fail("Unsupported Linux target for inline assembly"); // LCOV_EXCL_LINE | |
| 123 | } // LCOV_EXCL_LINE | ||
| 124 | } else { // LCOV_EXCL_LINE | ||
| 125 | − | assert_fail("Unsupported target for inline assembly"); // LCOV_EXCL_LINE | |
| 126 | } // LCOV_EXCL_LINE | ||
| 127 | |||
| 128 |
1/2✓ Branch 80 → 81 taken 1 time.
✗ Branch 80 → 85 not taken.
|
2 | return constraints.str(); |
| 129 | 1 | } | |
| 130 | |||
| 131 | } // namespace spice::compiler | ||
| 132 |