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