objectemitter/tpde-object-emitter.spice
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // Copyright (c) 2021-2026 ChilliBits. All rights reserved. | ||
| 2 | |||
| 3 | // Std imports | ||
| 4 | import "std/data/vector"; | ||
| 5 | import "std/io/file"; | ||
| 6 | import "std/io/filepath"; | ||
| 7 | import "std/bindings/llvm/llvm" as llvm; | ||
| 8 | import "std/bindings/tpde/tpde" as tpde; | ||
| 9 | |||
| 10 | // Own imports | ||
| 11 | import "@/exception/fatal-error"; | ||
| 12 | import "@/exception/compiler-error"; | ||
| 13 | import "@/objectemitter/abstract-object-emitter"; | ||
| 14 | |||
| 15 | /** | ||
| 16 | * Object emitter backed by the experimental TPDE compiler back-end framework. | ||
| 17 | * | ||
| 18 | * Consumes an LLVM module directly and emits an ELF object at roughly -O0 code quality but significantly faster than LLVM | ||
| 19 | * CodeGen at -O0. Supported targets: ELF on x86_64 or aarch64; small code model; PIC relocation only; no LTO. | ||
| 20 | * | ||
| 21 | * The emitter uses the std TPDE bindings, which are only backed by TPDE, if the TPDE libraries were linked into the compiler | ||
| 22 | * (see std/bindings/tpde). The driver rejects the TPDE backend otherwise, so this emitter is only used if TPDE is available. | ||
| 23 | */ | ||
| 24 | public type TPDEObjectEmitter struct : IAbstractObjectEmitter { | ||
| 25 | llvm::Module* module | ||
| 26 | } | ||
| 27 | |||
| 28 | 3497 | public p TPDEObjectEmitter.ctor(llvm::Module* module) { | |
| 29 | 3497 | this.module = module; | |
| 30 | 3497 | } | |
| 31 | |||
| 32 | 2 | public p TPDEObjectEmitter.emit(const FilePath& objectPath) { | |
| 33 | 2 | const string objectPathString = objectPath.toString(); | |
| 34 | |||
| 35 | // Create a TPDE compiler for the module's target triple | ||
| 36 | 2 | const String tripleString = this.module.getTargetTripleString(); | |
| 37 | 2 | const llvm::Triple triple = llvm::Triple(tripleString.getRaw()); | |
| 38 | 2 | tpde::Compiler compiler = tpde::Compiler(triple); | |
| 39 | 2 | if !compiler.isValid() { | |
| 40 | − | const String message = "The TPDE backend does not support target triple '" + tripleString + "'"; // LCOV_EXCL_LINE | |
| 41 | − | raiseObjectEmitterError(CompilerErrorType::WRONG_OUTPUT_TYPE, message); // LCOV_EXCL_LINE | |
| 42 | − | } // LCOV_EXCL_LINE | |
| 43 | |||
| 44 | // Compile to an in-memory ELF object | ||
| 45 | 2 | Vector<byte> objBytes; | |
| 46 | 2 | String errorMessage; | |
| 47 | 2 | if compiler.compileToBuffer(*this.module, objBytes, errorMessage) { | |
| 48 | ✗ | raiseObjectEmitterError(CompilerErrorType::WRONG_OUTPUT_TYPE, errorMessage); | |
| 49 | ✗ | } | |
| 50 | |||
| 51 | // Write to the requested path | ||
| 52 | 2 | Result<File> fileResult = openFile(objectPathString, "wb"); | |
| 53 | 2 | if !fileResult.isOk() { | |
| 54 | − | const String message = "File '" + String(objectPathString) + "' could not be opened"; // LCOV_EXCL_LINE | |
| 55 | − | raiseObjectEmitterError(CompilerErrorType::CANT_OPEN_OUTPUT_FILE, message); // LCOV_EXCL_LINE | |
| 56 | − | } // LCOV_EXCL_LINE | |
| 57 | 2 | File file = fileResult.unwrap(); | |
| 58 | 2 | bool writeFailed = false; | |
| 59 | 5818 | foreach byte objByte : objBytes { | |
| 60 | 5816 | if !file.write(cast<char>(objByte)) { | |
| 61 | − | writeFailed = true; // LCOV_EXCL_LINE | |
| 62 | − | break; // LCOV_EXCL_LINE | |
| 63 | } // LCOV_EXCL_LINE | ||
| 64 | 5816 | } | |
| 65 | 2 | const bool closeFailed = !file.close(); | |
| 66 | 2 | if writeFailed || closeFailed { | |
| 67 | − | const String message = "File '" + String(objectPathString) + "' could not be written"; // LCOV_EXCL_LINE | |
| 68 | − | raiseObjectEmitterError(CompilerErrorType::CANT_OPEN_OUTPUT_FILE, message); // LCOV_EXCL_LINE | |
| 69 | − | } // LCOV_EXCL_LINE | |
| 70 | 2 | } | |
| 71 | |||
| 72 | 2 | public p TPDEObjectEmitter.getASMString(String& output) { | |
| 73 | // TPDE emits ELF bytes directly and does not expose an assembly listing | ||
| 74 | 2 | output = String("; Assembly listing is not available under the TPDE backend.\n; Use --backend=llvm to obtain assembly output.\n"); | |
| 75 | 2 | } | |
| 76 | |||
| 77 | /** | ||
| 78 | * Abort with a compiler error | ||
| 79 | * | ||
| 80 | * @param errorType Type of the compiler error | ||
| 81 | * @param message Error message suffix | ||
| 82 | */ | ||
| 83 | ✗ | p raiseObjectEmitterError(CompilerErrorType errorType, const String& message) { | |
| 84 | ✗ | const CompilerError error = CompilerError(errorType, message.getRaw()); | |
| 85 | ✗ | abortWithError(error.getMessage()); | |
| 86 | ✗ | } | |
| 87 |