src/driver/Driver.h
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // Copyright (c) 2021-2026 ChilliBits. All rights reserved. | ||
| 2 | |||
| 3 | #pragma once | ||
| 4 | |||
| 5 | #include <util/GlobalDefinitions.h> | ||
| 6 | |||
| 7 | #include <llvm/TargetParser/Triple.h> | ||
| 8 | |||
| 9 | #include <CLI/CLI.hpp> | ||
| 10 | |||
| 11 | // Undef conflicting macros (only problematic on Windows) | ||
| 12 | #undef TRUE | ||
| 13 | #undef FALSE | ||
| 14 | #undef CONST | ||
| 15 | |||
| 16 | namespace spice::compiler { | ||
| 17 | |||
| 18 | const char *const TARGET_UNKNOWN = "unknown"; | ||
| 19 | const char *const ENV_VAR_DOCKERIZED = "SPICE_DOCKERIZED"; | ||
| 20 | |||
| 21 | enum class OptLevel : uint8_t { | ||
| 22 | O0 = 0, // No optimization | ||
| 23 | O1 = 1, // Only basic optimizations | ||
| 24 | O2 = 2, // Default optimization level | ||
| 25 | O3 = 3, // Aggressively optimize for performance | ||
| 26 | Os = 4, // Optimize for code size | ||
| 27 | Oz = 5, // Aggressively optimize for code size | ||
| 28 | }; | ||
| 29 | |||
| 30 | enum class DebugInfoLevel : uint8_t { | ||
| 31 | NONE = 0, // Do not emit any debug info | ||
| 32 | LINE_ONLY = 1, // Only emit DWARF line tables, no type, variable or scope info | ||
| 33 | FULL = 2, // Emit full debug info, incl. types, variables and lexical scopes | ||
| 34 | }; | ||
| 35 | const char *const DEBUG_INFO_NONE = "none"; | ||
| 36 | const char *const DEBUG_INFO_LINE_ONLY = "line-only"; | ||
| 37 | const char *const DEBUG_INFO_FULL = "full"; | ||
| 38 | |||
| 39 | enum class Sanitizer : uint8_t { | ||
| 40 | NONE = 0, // No sanitizer | ||
| 41 | ADDRESS = 1, // Sanitize memory accesses | ||
| 42 | THREAD = 2, // Sanitize threads for data race prevention | ||
| 43 | MEMORY = 3, // Sanitize against uninitialized memory reads | ||
| 44 | TYPE = 4, // Sanitize type casts and accesses | ||
| 45 | }; | ||
| 46 | const char *const SANITIZER_NONE = "none"; | ||
| 47 | const char *const SANITIZER_ADDRESS = "address"; | ||
| 48 | const char *const SANITIZER_THREAD = "thread"; | ||
| 49 | const char *const SANITIZER_MEMORY = "memory"; | ||
| 50 | const char *const SANITIZER_TYPE = "type"; | ||
| 51 | |||
| 52 | enum class BuildMode : uint8_t { | ||
| 53 | DEBUG = 0, // Default build mode, uses -O0 per default | ||
| 54 | RELEASE = 1, // Build without debug information and with -O2 per default | ||
| 55 | TEST = 2, // Build with test main function and always emit assertions | ||
| 56 | }; | ||
| 57 | const char *const BUILD_MODE_DEBUG = "debug"; | ||
| 58 | const char *const BUILD_MODE_RELEASE = "release"; | ||
| 59 | const char *const BUILD_MODE_TEST = "test"; | ||
| 60 | |||
| 61 | enum class OutputContainer : uint8_t { | ||
| 62 | EXECUTABLE = 0, // On Linux and macOS no extension, on Windows .exe files | ||
| 63 | OBJECT_FILE = 1, // On Linux and macOS .o files and on Windows .obj files | ||
| 64 | STATIC_LIBRARY = 2, // On Linux and macOS .a files and on Windows .lib files | ||
| 65 | SHARED_LIBRARY = 3, // On Linux .so, on macOS .dylib and on Windows .dll files | ||
| 66 | MAX, | ||
| 67 | }; | ||
| 68 | |||
| 69 | const char *const OUTPUT_CONTAINER_EXECUTABLE = "exec"; | ||
| 70 | const char *const OUTPUT_CONTAINER_OBJECT_FILE = "obj"; | ||
| 71 | const char *const OUTPUT_CONTAINER_STATIC_LIBRARY = "lib"; | ||
| 72 | const char *const OUTPUT_CONTAINER_SHARED_LIBRARY = "dylib"; | ||
| 73 | |||
| 74 | enum class Backend : uint8_t { | ||
| 75 | LLVM = 0, // Standard LLVM CodeGen backend (default) | ||
| 76 | TPDE = 1, // Experimental TPDE backend — fast, -O0-quality codegen (opt-in via SPICE_ENABLE_TPDE) | ||
| 77 | }; | ||
| 78 | const char *const BACKEND_LLVM = "llvm"; | ||
| 79 | const char *const BACKEND_TPDE = "tpde"; | ||
| 80 | |||
| 81 | /** | ||
| 82 | * Representation of the various cli options | ||
| 83 | */ | ||
| 84 | struct CliOptions { | ||
| 85 | std::filesystem::path mainSourceFile; // e.g. ./main.spice | ||
| 86 | llvm::Triple targetTriple; // In format: <arch><sub>-<vendor>-<sys>-<abi> | ||
| 87 | std::string targetArch = TARGET_UNKNOWN; | ||
| 88 | std::string targetVendor = TARGET_UNKNOWN; | ||
| 89 | std::string targetOs = TARGET_UNKNOWN; | ||
| 90 | bool isNativeTarget = true; | ||
| 91 | bool useCPUFeatures = true; | ||
| 92 | bool execute = false; | ||
| 93 | std::filesystem::path cacheDir; // Where the cache files go. Should always be a temp directory | ||
| 94 | std::filesystem::path outputDir = "./"; // Where the object files go. Should always be a temp directory | ||
| 95 | std::filesystem::path outputPath; // Where the output binary goes. | ||
| 96 | BuildMode buildMode = BuildMode::DEBUG; // Default build mode is debug | ||
| 97 | OutputContainer outputContainer = OutputContainer::EXECUTABLE; // Default output container is executable | ||
| 98 | unsigned short compileJobCount = 0; // 0 for auto | ||
| 99 | bool ignoreCache = false; | ||
| 100 | std::string llvmArgs; | ||
| 101 | bool printDebugOutput = false; | ||
| 102 | struct DumpSettings { | ||
| 103 | bool dumpCST = false; | ||
| 104 | bool dumpAST = false; | ||
| 105 | bool dumpSymbolTable = false; | ||
| 106 | bool dumpTypes = false; | ||
| 107 | bool dumpCacheStats = false; | ||
| 108 | bool dumpDependencyGraph = false; | ||
| 109 | bool dumpIR = false; | ||
| 110 | bool dumpAssembly = false; | ||
| 111 | bool dumpObjectFiles = false; | ||
| 112 | bool dumpToFiles = false; | ||
| 113 | bool abortAfterDump = false; | ||
| 114 | } dump; | ||
| 115 | bool namesForIRValues = false; | ||
| 116 | bool useLifetimeMarkers = false; | ||
| 117 | bool useTBAAMetadata = false; | ||
| 118 | OptLevel optLevel = OptLevel::O0; // The default optimization level for debug build mode is O0 | ||
| 119 | bool useLTO = false; | ||
| 120 | Backend backend = Backend::LLVM; // Codegen backend selection (TPDE is experimental, opt-in at build time) | ||
| 121 | bool noEntryFct = false; | ||
| 122 | bool generateTestMain = false; | ||
| 123 | bool staticLinking = false; | ||
| 124 | struct InstrumentationSettings { | ||
| 125 | DebugInfoLevel debugInfoLevel = DebugInfoLevel::NONE; | ||
| 126 | bool codeCoverage = false; | ||
| 127 | Sanitizer sanitizer = Sanitizer::NONE; | ||
| 128 | |||
| 129 | 2164970 | [[nodiscard]] ALWAYS_INLINE bool emitsDebugInfo() const { return debugInfoLevel != DebugInfoLevel::NONE; } | |
| 130 | 458368 | [[nodiscard]] ALWAYS_INLINE bool emitsFullDebugInfo() const { return debugInfoLevel == DebugInfoLevel::FULL; } | |
| 131 | } instrumentation; | ||
| 132 | bool disableVerifier = !SPICE_DEBUG; | ||
| 133 | bool testMode = false; | ||
| 134 | bool comparableOutput = false; | ||
| 135 | std::map<std::string, std::string> buildVars; | ||
| 136 | // New fields must be appended here, at the very end: test/TestRunner.cpp positionally aggregate-initializes | ||
| 137 | // CliOptions, so inserting a field anywhere else silently shifts every field after it into the wrong slot. | ||
| 138 | bool lintOnly = false; | ||
| 139 | bool keepFramePointers = false; | ||
| 140 | bool stripSymbols = false; | ||
| 141 | }; | ||
| 142 | |||
| 143 | /** | ||
| 144 | * Helper class to set up the driver and command line parser | ||
| 145 | */ | ||
| 146 | class Driver { | ||
| 147 | public: | ||
| 148 | // Constructors | ||
| 149 | explicit Driver(CliOptions &foreignCliOptions, bool dryRun = false); | ||
| 150 | |||
| 151 | // Delete copy ctor and assignment operator | ||
| 152 | Driver(const Driver &) = delete; | ||
| 153 | Driver &operator=(const Driver &) = delete; | ||
| 154 | |||
| 155 | // Public methods | ||
| 156 | int parse(int argc, const char *argv[]); | ||
| 157 | void enrich() const; | ||
| 158 | void runBinary() const; | ||
| 159 | |||
| 160 | // Public members | ||
| 161 | CliOptions &cliOptions; | ||
| 162 | bool shouldCompile = false; | ||
| 163 | bool shouldInstall = false; | ||
| 164 | bool shouldUninstall = false; | ||
| 165 | bool shouldExecute = false; | ||
| 166 | bool performDryRun = false; // For unit testing purposes | ||
| 167 | |||
| 168 | private: | ||
| 169 | // Private methods | ||
| 170 | void addBuildSubcommand(); | ||
| 171 | void addRunSubcommand(); | ||
| 172 | void addTestSubcommand(); | ||
| 173 | void addLintSubcommand(); | ||
| 174 | void addInstallSubcommand(); | ||
| 175 | void addUninstallSubcommand(); | ||
| 176 | void addCompileSubcommandOptions(CLI::App *subCmd) const; | ||
| 177 | void addInstrumentationOptions(CLI::App *subCmd); | ||
| 178 | static void ensureNotDockerized(); | ||
| 179 | |||
| 180 | // Members | ||
| 181 | CLI::App app = CLI::App("Spice Programming Language", "spice"); | ||
| 182 | // Whether the user named a debug info level themselves. DebugInfoLevel::NONE is also the default, so without this | ||
| 183 | // an explicit '--debug-info=none' would be indistinguishable from not passing the option at all. | ||
| 184 | bool debugInfoLevelSetExplicitly = false; | ||
| 185 | }; | ||
| 186 | |||
| 187 | } // namespace spice::compiler | ||
| 188 |